vagrant-2.0.2/000077500000000000000000000000001323370221500131535ustar00rootroot00000000000000vagrant-2.0.2/.github/000077500000000000000000000000001323370221500145135ustar00rootroot00000000000000vagrant-2.0.2/.github/CONTRIBUTING.md000066400000000000000000000041541323370221500167500ustar00rootroot00000000000000# How to contribute We like to encourage you to contribute to the repository. This should be as easy as possible for you but there are a few things to consider when contributing. The following guidelines for contribution should be followed if you want to submit a pull request. ## How to prepare * You need a [GitHub account](https://github.com/signup/free) * Submit an [issue ticket](https://github.com/mitchellh/vagrant/issues) for your issue if there is not one yet. * Describe the issue and include steps to reproduce when it's a bug. * Ensure to mention the earliest version that you know is affected. * If you plan on submitting a bug report, please submit debug-level logs along with the report using [gist](https://gist.github.com/) or some other paste service by prepending `VAGRANT_LOG=debug` to your `vagrant` commands. * Fork the repository on GitHub ## Make Changes * In your forked repository, create a topic branch for your upcoming patch. * Usually this is based on the master branch. * Create a branch based on master; `git branch fix/master/my_contribution master` then checkout the new branch with `git checkout fix/master/my_contribution`. Please avoid working directly on the `master` branch. * Make commits of logical units and describe them properly. * Check for unnecessary whitespace with `git diff --check` before committing. * If possible, submit tests to your patch / new feature so it can be tested easily. * Assure nothing is broken by running all the tests. ## Submit Changes * Push your changes to a topic branch in your fork of the repository. * Open a pull request to the original repository and choose the right original branch you want to patch. * If not done in commit messages (which you really should do) please reference and update your issue with the code changes. * Even if you have write access to the repository, do not directly push or merge pull-requests. Let another team member review your pull request and approve. # Additional Resources * [General GitHub documentation](https://help.github.com/) * [GitHub pull request documentation](https://help.github.com/send-pull-requests/) vagrant-2.0.2/.github/ISSUE_TEMPLATE.md000066400000000000000000000025661323370221500172310ustar00rootroot00000000000000Please note that the Vagrant issue tracker is reserved for bug reports and enhancements. For general usage questions, please use the Vagrant mailing list: https://groups.google.com/forum/#!forum/vagrant-up. Thank you! ### Vagrant version Run `vagrant -v` to show the version. If you are not running the latest version of Vagrant, please upgrade before submitting an issue. ### Host operating system This is the operating system that you run locally. ### Guest operating system This is the operating system you run in the virtual machine. ### Vagrantfile ```ruby # Copy-paste your Vagrantfile here (but don't include sensitive information such as passwords, authentication tokens, or email addresses) ``` Please note, if you are using Homestead or a different Vagrantfile format, we may be unable to assist with your issue. Try to reproduce the issue using a vanilla Vagrantfile first. ### Debug output Provide a link to a GitHub Gist containing the complete debug output: https://www.vagrantup.com/docs/other/debugging.html. The debug output should be very long. Do NOT paste the debug output in the issue, just paste the link to the Gist. ### Expected behavior What should have happened? ### Actual behavior What actually happened? ### Steps to reproduce 1. 2. 3. ### References Are there any other GitHub issues (open or closed) that should be linked here? For example: - GH-1234 - ... vagrant-2.0.2/.gitignore000066400000000000000000000010021323370221500151340ustar00rootroot00000000000000# OS-specific .DS_Store # Editor swapfiles .*.sw? *~ # Vagrant stuff acceptance_config.yml boxes/* /.vagrant /website/.vagrant /website/build /vagrant-spec.config.rb test/vagrant-spec/.vagrant/ # Bundler/Rubygems *.gem .bundle pkg/* tags /Gemfile.lock test/tmp/ vendor/ /exec # Documentation _site/* .yardoc/ doc/ # Python *.pyc # Rubinius *.rbc # IDE junk .idea/* *.iml .project # Ruby Managers .rbenv .rbenv-gemsets .ruby-gemset .ruby-version .rvmrc # Box storage for spec test/vagrant-spec/boxes/*.box vagrant-2.0.2/.travis.yml000066400000000000000000000003561323370221500152700ustar00rootroot00000000000000language: ruby sudo: false cache: bundler addons: apt: packages: - bsdtar rvm: - 2.3.5 - 2.4.2 branches: only: - master env: global: - NOKOGIRI_USE_SYSTEM_LIBRARIES=true script: bundle exec rake test:unit vagrant-2.0.2/.vimrc000066400000000000000000000001221323370221500142670ustar00rootroot00000000000000" tabstop settings set tabstop=2 set softtabstop=2 set shiftwidth=2 set expandtab vagrant-2.0.2/.yardopts000066400000000000000000000000141323370221500150140ustar00rootroot00000000000000-m markdown vagrant-2.0.2/CHANGELOG.md000066400000000000000000005370701323370221500150000ustar00rootroot00000000000000## 2.0.2 (January 29, 2018) FEATURES: - core: Provide mechanism for removing sensitive data from output [GH-9276] - core: Relax Ruby constraints to include 2.5 [GH-9363] - core: Hide sensitive values in output [GH-9369] - command/init: Support custom Vagrantfile templates [GH-9202] - guests: Add support for the Haiku operating system [GH-7805, GH-9245] - synced_folders/smb: Add support for macOS hosts [GH-9294] - vagrant-spec: Update vagrant-spec to include Windows platforms and updated linux boxes [GH-9183] IMPROVEMENTS: - config/ssh: Deprecate :paranoid in favor of :verify_host_key [GH-9341] - core: Add optional timestamp prefix on log output [GH-9269] - core: Print more helpful error message for NameEror exceptions in Vagrantfiles [GH-9252] - core: Update checkpoint implementation to announce updates and support notifications [GH-9380] - core: Use Ruby's Resolv by default [GH-9394] - docs: Include virtualbox 5.2.x as supported in docs [GH-9237] - docs: Improve how to pipe debug log on powershell [GH-9330] - guests/amazon: Improve guest detection [GH-9307] - guests/debian: Update guest configure networks [GH-9338] - guests/dragonflybsd: Base guest on FreeBSD to inherit more functionality [GH-9205] - guests/linux: Improve NFS service name detection and interactions [GH-9274] - guests/linux: Support mount option overrides for SMB mounts [GH-9366] - guests/linux: Use `ip` for reading guest address if available [GH-9315] - guests/solaris: Improve guest detection for alternatives [GH-9295] - hosts/windows: Check credentials during SMB prepare [GH-9365] - providers/hyper-v: Ensure Hyper-V cmdlets are fully qualified [GH-8863] - middleware/authentication: Add app.vagrantup.com to allowed hosts [GH-9145] - provisioners/shell: Support hiding environment variable values in output [GH-9367] - providers/virtualbox: Add a clean error message for invalid IP addresses [GH-9275] - providers/virtualbox: Introduce flag for SharedFoldersEnableSymlinksCreate setting [GH-9354] - providers/virtualbox: Provide warning for SharedFoldersEnableSymlinksCreate setting [GH-9389] - provisioners/salt: Fixes timeout issue in salt bootstrapping for windows [GH-8992] - synced_folders/smb: Update Windows implementation [GH-9294] - util/ssh: Attempt to locate local ssh client before attempting installer provided [GH-9400] BUG FIXES: - commands/box: Show all box providers with `update outdated --global` [GH-9347] - commands/destroy: Exit 0 if vagrant destroy finds no running vms [GH-9251] - commands/package: Fix --output path with specified folder [GH-9131] - guests/suse: Do not use full name when setting hostname [GH-9212] - providers/hyper-v: Fix enable virtualization extensions on import [GH-9255] - provisioners/ansible(both): Fix broken 'ask_sudo_pass' option [GH-9173] ## 2.0.1 (November 2, 2017) FEATURES: - core: Introduce Ruby 2.4 to Vagrant [GH-9102] - providers/virtualbox: Virtualbox 5.2 support [GH-8955] IMPROVEMENTS: - command/destroy: Introduce parallel destroy for certain providers [GH-9127] - communicators/winrm: Include APIPA check within ready check [GH-8997] - core: Clear POSIXLY_CORRECT when using optparse [GH-8685] - docs: Add auto_start_action and auto_stop_action to docs. [GH-9029] - docs: Fix typo in box format doc [GH-9100] - provisioners/chef: Handle chef provisioner reboot request [GH-8874] - providers/salt: Support Windows Salt Minions greater than 2016.x.x [GH-8926] - provisioners/salt: Add wget to bootstrap_salt options when fetching installer file [GH-9112] - provisioners/shell: Use ui.detail for displaying output [GH-8983] - util/downloader: Use CURL_CA_BUNDLE environment variable [GH-9135] BUG FIXES: - communicators/ssh: Retry on Errno::EPIPE exceptions [GH-9065] - core: Rescue more exceptions when checking if port is open [GH-8517] - guests/solaris11: Inherit from Solaris guest and keep solaris11 specific methods [GH-9034] - guests/windows: Split out cygwin path helper for msys2/cygwin paths and ensure cygpath exists [GH-8972] - guests/windows: Specify expected shell when executing on guest (fixes winssh communicator usage) [GH-9012] - guests/windows: Include WinSSH Communicator when using insert_public_key [GH-9105] - hosts/windows: Check for vagrant.exe when validating versions within WSL [GH-9107, GH-8962] - providers/docker: Isolate windows check within executor to handle running through VM [GH-8921] - providers/hyper-v: Properly invoke Auto stop action [GH-9000] - provisioners/puppet: Fix winssh communicator support in puppet provisioner [GH-9014] - virtualbox/synced_folders: Allow synced folders to contain spaces in the guest path [GH-8995] ## 2.0.0 (September 7, 2017) IMPROVEMENTS: - commands/login: Add support for two-factor authentication [GH-8935] - commands/ssh-config: Properly display windows path if invoked from msys2 or cygwin [GH-8915] - guests/alt: Add support for ALT Linux [GH-8746] - guests/kali: Fix file permissions on guest plugin ruby files [GH-8950] - hosts/linux: Provide common systemd detection for services interaction, fix NFS host interactions [GH-8938] - providers/salt: Remove duplicate stdout, stderr output from salt [GH-8767] - providers/salt: Introduce salt_call_args and salt_args option for salt provisioner [GH-8927] - providers/virtualbox: Improving resilience of some VirtualBox commands [GH-8951] - provisioners/ansible(both): Add the compatibility_mode option, with auto-detection enabled by default [GH-8913, GH-6570] - provisioners/ansible: Add the version option to the host-based provisioner [GH-8913, GH-8914] - provisioners/ansible(both): Add the become and become_user options with deprecation of sudo and sudo_user options [GH-8913, GH-6570] - provisioners/ansible: Add the ask_become_pass option with deprecation of the ask_sudo_pass option [GH-8913, GH-6570] BUG FIXES: - guests/shell_expand_guest_path : Properly expand guest paths that include relative path alias [GH-8918] - hosts/linux: Remove duplicate export folders before writing /etc/exports [GH-8945] - provisioners/ansible(both): Add single quotes to the inventory host variables, only when necessary [GH-8597] - provisioners/ansible(both): Add the "all:vars" section to the inventory when defined in `groups` option [GH-7730] - provisioners/ansible_local: Extra variables are no longer truncated when a dollar ($) character is present [GH-7735] - provisioners/file: Align file provisioner functionality on all platforms [GH-8939] - util/ssh: Properly quote key path for IdentityFile option to allow for spaces [GH-8924] BREAKING CHANGES: - Both Ansible provisioners are now capable of automatically setting the compatibility_mode that best fits with the Ansible version in use. You may encounter some compatibility issues when upgrading. If you were using Ansible 2.x and referring to the _ssh-prefixed variables present in the generated inventory (e.g. `ansible_ssh_host`). In this case, you can fix your Vagrant setup by setting compatibility_mode = "1.8", or by migrating to the new variable names (e.g. ansible_host). ## 1.9.8 (August 23, 2017) IMPROVEMENTS: - bash: Add box prune to contrib bash completion [GH-8806] - commands/login: Ask for description of Vagrant Cloud token [GH-8876] - commands/validate: Improve functionality of the validate command [GH-8889]n - core: Updated Vagrants rspec gem to 3.5.0 [GH-8850] - core: Validate powershell availability and version before use [GH-8839] - core: Introduce extra_args setting for ssh configs [GH-8895] - docs: Align contrib/sudoers file for ubuntu linux with docs [GH-8842] - provider/hyperv: Prefer IPv4 guest address [GH-8831, GH-8759] - provisioners/chef: Add config option omnibus_url for chef provisioners [GH-8682] - provisioners/chef: Improve exception handling around missing folder paths [GH-8775] BUG FIXES: - box/update: Add force flag for box upgrade command [GH-8871] - commands/rsync-auto: Ensure relative dirs are still rsync'd if defined [GH-8781] - commands/up: Disable install providers when using global id on vagrant up [GH-8910] - communicators/winssh: Fix public key insertion to retain ACL [GH-8790] - core: Update util/ssh to use `-o` for identity files [GH-8786] - guests/freebsd: Fix regex for listing network devices on some FreeBSD boxes. [GH-8760] - hosts/windows: Prevent control characters in version check for WSL [GH-8902, GH-8901] - providers/docker: Split String type links into Array when using compose [GH-8837, GH-8821] - providers/docker: Expand relative volume paths correctly [GH-8838, GH-8822] - providers/docker: Error when compose option enabled with force_host_vm [GH-8911] - provisioners/ansible: Update to use `-o` for identity files [GH-8786] - provisioners/file: Ensure remote folder exists prior to scp file or folder [GH-8880] - provisioners/salt: Fix error case when github is unreachable for installer [GH-8864] - provisioners/shell: Allow frozen string scripts [GH-8875] - provisioners/puppet: Remove `--manifestdir` flag from puppet apply in provisioner [GH-8797] - synced_folders/rsync: Correctly format IPv6 host [GH-8840, GH-8809] ## 1.9.7 (July 7, 2017) FEATURES: - core: Add support for preferred providers [GH-8558] IMPROVEMENTS: - guests/bsd: Invoke `tee` with explicit path [GH-8740] - guests/smartos: Guest updates for host name and nfs capabilities [GH-8695] - guests/windows: Add public key capabilities for WinSSH communicator [GH-8761] - hosts/windows: Log command exec encoding failures and use original string on failure [GH-8820] - providers/virtualbox: Filter machine IPs when preparing NFS settings [GH-8819] BUG FIXES: - communicators/winssh: Make script upload directory configurable [GH-8761] - core: Update cygwin detection to prevent PATH related errors [GH-8749, GH-6788] - core: Fix URI parsing of box names to prevent errors [GH-8762, GH-8758] - provider/docker: Only rsync-auto current working dir with docker provider [GH-8756] ## 1.9.6 (June 28, 2017) IMPROVEMENTS: - commands/snapshot: Enforce unique snapshot names and introduce `--force` flag [GH-7810] - commands/ssh: Introduce tty flag for `vagrant ssh -c` [GH-6827] - core: Warn about vagrant CWD changes for a machine [GH-3921] - core: Allow Compression and DSAAuthentication ssh flags to be configurable [GH-8693] - core/box: Warn if user sets box as url [GH-7118] - core/bundler: Enforce stict constraints on vendored libraries [GH-8692] - guests/kali: Add support for guest [GH-8553] - guests/smartos: Update halt capability and add public key insert and remove capabilities [GH-8618] - provisioners/ansible: Fix SSH keys only behavior to be consistent with Vagrant [GH-8467] - providers/docker: Add post install provisioner for docker setup [GH-8722] - snapshot/delete: Improve error message when given snapshot doesn't exist [GH-8653] - snapshot/list: Raise exception if provider does not support snapshots [GH-8619] - snapshot/restore: Improve error message when given snapshot doesn't exist [GH-8653] - snapshot/save: Raise exception if provider does not support snapshots [GH-8619] BUG FIXES: - communicators/ssh: Move `none` cipher to end of default cipher list in Net::SSH [GH-8661] - core: Add unique identifier to provisioner objects [GH-8680] - core: Stop config loader from loading dupe config if home and project dir are equal [GH-8707] - core/bundler: Impose constraints on update and allow system plugins to properly update [GH-8729] - guests/linux: Strip whitespace from GID [GH-8666, GH-8664] - guests/solaris: Do not use UNC style path for shared folders from windows hosts [GH-7723] - guests/windows: Fix directory creation when using rsync for synced folders [GH-8588] - hosts/windows: Force common encoding when running system commands [GH-8725] - providers/docker: Fix check for docker-compose [GH-8659, GH-8660] - providers/docker: Fix SSH under docker provider [GH-8706] - providers/hyperv: Fix box import [GH-8678, GH-8677] - provisioners/ansible_local: Catch pip_args in FreeBSD's and SUSE's ansible_install [GH-8676] - provisioners/salt: Fix minion ID configuration [GH-7865, GH-7454] - snapshot/restore: Exit 1 if vm has not been created when command is invoked [GH-8653] ## 1.9.5 (May 15, 2017) FEATURES: - hosts/windows: Support running within WSL [GH-8570, GH-8582] IMPROVEMENTS: - communicators/ssh: Retry on aborted connections [GH-8526, GH-8520] - communicators/winssh: Enabling shared folders and networking setup [GH-8567] - core: Remove nokogiri dependency and constraint [GH-8571] - guests: Do not modify existing /etc/hosts content [GH-8506, GH-7794] - guests/redhat: Update network configuration capability to properly handle NM [GH-8531] - hosts/windows: Check for elevated shell for Hyper-V [GH-8548, GH-8510] - hosts/windows: Fix invalid share names on Windows guests from Windows hosts [GH-8433] - providers: Return errors from docker/hyperv on ssh when not available [GH-8565, GH-8508] - providers/docker: Add support for driving provider with docker-compose [GH-8576] BUG FIXES: - guests/debian: Fix use_dhcp_assigned_default_route [GH-8577, GH-8575] - provisioners/shell: Fix Windows batch file provisioning [GH-8539, GH-8535] - providers/docker: Fall back to old style for SSH info lookup [GH-8566, GH-8552] - providers/hyperv: Fix import script [GH-8529] - providers/hyperv: Use string comparison for conditional checks in import scripts [GH-8568, GH-8444] ## 1.9.4 (April 24, 2017) FEATURES: - command/validate: Add Vagrantfile validation command [GH-8264, GH-8151] - communicators/winssh: Add WinSSH communicator for Win32-OpenSSH [GH-8485] - provider/hyperv: Support integration services configuration [GH-8379, GH-8378] IMPROVEMENTS: - core: Update internal dependencies [GH-8329, GH-8456] - core/bundler: Warn when plugin require fails instead of generating hard failure [GH-8400, GH-8392] - core/bundler: Error when configured plugin sources are unavailable [GH-8442] - guests/elementary: Add support for new guest "Elementary OS" [GH-8472] - guests/esxi: Add public_key capability [GH-8310] - guests/freebsd: Add chef_install and chef_installed? capabilities [GH-8443] - guests/gentoo: Add support for systemd in network configuration [GH-8407, GH-8406] - guests/windows: Support mounting synced folders via SSH on windows [GH-7425, GH-6220] - hosts/windows: Improve user permission detection [GH-7797] - provider/docker: Improve IP and port detection [GH-7840, GH-7651] - provider/docker: Do not force docker host VM on Darwin or Windows [GH-8437, GH-7895] - provisioners/ansible_local: Add `pip_args` option to define additional parameters when installing Ansible via pip [GH-8170, GH-8405] - provisioners/ansible_local: Add `:pip_args_only` install mode to allow full custom pip installations [GH-8405] - provisioners/salt: Update minion version installed to 2016.11.3 [GH-8448] BUG FIXES: - command/box: Remove extraneous sort from box list prior to display [GH-8422] - command/box: Properly handle local paths with spaces for box add [GH-8503, GH-6825] - command/up: Prevent other provider installation when explicitly defined [GH-8393, GH-8389] - communicators/ssh: Do not yield empty output data [GH-8495, GH-8259] - core: Provide fallback and retry when 0.0.0.0 is unavailable during port check [GH-8399, GH-8395] - core: Support port checker methods that do not expect inclusion of host_ip [GH-8497, GH-8423] - core/bundler: Check if source is local path and prevent addition to remote sources [GH-8401] - core/ui: Prevent deadlock detection errors [GH-8414, GH-8125] - guests/debian: Remove hardcoded device name in interface template [GH-8336, GH-7960] - guests/linux: Fix SMB mount capbability [GH-8410, GH-8404] - hosts/windows: Fix issues with Windows encoding [GH-8385, GH-8380, GH-8212, GH-8207, GH-7516] - hosts/windows: Fix UNC path generation when UNC path is provided [GH-8504] - provisioners/salt: Allow Salt version to match 2 digit month [GH-8428] - provisioners/shell: Properly handle remote paths on Windows that include spaces [GH-8498, GH-7234] ## 1.9.3 (March 21, 2017) IMPROVEMENTS: - command/plugin: Remove requirement for paths with no spaces [GH-7967] - core: Support host_ip for forwarded ports [GH-7035, GH-8350] - core: Include disk space hint in box install failure message [GH-8089] - core/bundler: Allow vagrant constraint matching in prerelease mode [GH-8341] - provisioner/docker: Include /bin/docker as valid path [GH-8390] - provider/hyperv: Support enabling Hyper-V nested virtualization [GH-8325, GH-7738] BUG FIXES: - communicator/winrm: Prevent inaccurate WinRM address [GH-7983, GH-8073] - contrib/bash: Handle path spaces in bash completion [GH-8337] - core: Fix box sorting on find and list [GH-7956, GH-8334] - core/bundler: Force path as preferred source on install [GH-8327] - core/provision: Update "never" behavior to match documentation [GH-8366, GH-8016] - plugins/push: Isolate deprecation to Atlas strategy only - plugins/synced_folders: Give UID/GID precedence if found within mount options [GH-8122, GH-8064, GH-7859] ## 1.9.2 (February 27, 2017) FEATURES: - providers/hyperv: Support packaging of Hyper-V boxes [GH-7867] - util/command_deprecation: Add utility module for command deprecation [GH-8300] - util/subprocess: Add #stop and #running? methods [GH-8270] IMPROVEMENTS: - commands/expunge: Display default value on prompt and validate input [GH-8192, GH-8171] - communicator/winrm: Refactor WinRM communicator to use latest WinRM gems and V2 API [GH-8102] - core: Scrub URL credentials from output when adding boxes [GH-8194, GH-8117] - providers/hyperv: Prefer VMCX over XML configuration when VMCX is supported [GH-8119] BUG FIXES: - command/init: Include box version when using minimal option [GH-8283, GH-8282] - command/package: Fix SecureRandom constant error [GH-8159] - communicator/ssh: Remove any STDERR output prior to command execution [GH-8291, GH-8288] - core/bundler: Prevent pristine warning messages [GH-8191, GH-8190, GH-8147] - core/bundler: Fix local installations of pre-release plugins [GH-8252, GH-8253] - core/bundler: Prefer user defined source when installing plugins [GH-8273, GH-8210] - core/environment: Prevent persisting original environment variable if name is empty [GH-8198, GH-8126] - core/environment: Fix gems_path location [GH-8248] - core/environment: Properly expand dotfile path [GH-8196, GH-8108] - guests/arch: Fix configuring multiple network interfaces [GH-8165] - guests/linux: Fix guest detection for names with spaces [GH-8092] - guests/redhat: Fix network interface configuration [GH-8148] DEPRECATIONS: - command/push: Disable push command [GH-8300] ## 1.9.1 (December 7, 2016) IMPROVEMENTS: - core: Disable Vagrantfile loading when running plugin commands [GH-8066] - guests/redhat: Detect and restart NetworkManager service if in use [GH-8052, GH-7994] BUG FIXES: - core: Detect load failures within install solution sets and retry [GH-8068] - core: Prevent interactive shell on plugin uninstall [GH-8086, GH-8087] - core: Remove bundler usage from Util::Env [GH-8090, GH-8094] - guests/linux: Prevent stderr output on init version check for synced folders [GH-8051] ## 1.9.0 (November 28, 2016) FEATURES: - commands/box: Add `prune` subcommand for removing outdated boxes [GH-7978] - core: Remove Bundler integration for handling internal plugins [GH-7793, GH-8000, GH-8011, GH-8031] - providers/hyperv: Add support for Hyper-V binary configuration format [GH-7854, GH-7706, GH-6102] - provisioners/shell: Support MD5/SHA1 checksum validation of remote scripts [GH-7985, GH-6323] IMPROVEMENTS: - commands/plugin: Retain name sorted output when listing plugins [GH-8028] - communicator/ssh: Support custom environment variable export template [GH-7976, GH-6747] - provisioners/ansible(both): Add `config_file` option to point the location of an `ansible.cfg` file via ANSIBLE_CONFIG environment variable [GH-7195, GH-7918] - synced_folders: Support custom naming and disable auto-mount [GH-7980, GH-6836] BUG FIXES: - guests/linux: Do not match interfaces with special characters when sorting [GH-7989, GH-7988] - provisioner/salt: Fix Hash construction for constant [GH-7986, GH-7981] ## 1.8.7 (November 4, 2016) IMPROVEMENTS: - guests/linux: Place ethernet devices at start of network devices list [GH-7848] - guests/linux: Provide more consistent guest detection [GH-7887, GH-7827] - guests/openbsd: Validate guest rsync installation success [GH-7929, GH-7898] - guests/redhat: Include Virtuozzo Linux 7 within flavor identification [GH-7818] - guests/windows: Allow vagrant to start Windows Nano without provisioning [GH-7831] - provisioners/ansible_local: Change the Ansible binary detection mechanism [GH-7536] - provisioners/ansible(both): Add the `playbook_command` option [GH-7881] - provisioners/puppet: Support custom environment variables [GH-7931, GH-7252, GH-2270] - util/safe_exec: Use subprocess for safe_exec on Windows [GH-7802] - util/subprocess: Allow closing STDIN [GH-7778] BUG FIXES: - communicators/winrm: Prevent connection leakage [GH-7712] - core: Prevent duplicate provider priorities [GH-7756] - core: Allow Numeric type for box version [GH-7874, GH-6960] - core: Provide friendly error when user environment is too large [GH-7889, GH-7857] - guests: Remove `set -e` usage for better shell compatibility [GH-7921, GH-7739] - guests/linux: Fix incorrectly configured private network [GH-7844, GH-7848] - guests/linux: Properly order network interfaces [GH-7866, GH-7876, GH-7858, GH-7876] - guests/linux: Only emit upstart event if initctl is available [GH-7813] - guests/netbsd: Fix rsync installation [GH-7922, GH-7901] - guests/photon: Fix networking setup [GH-7808, GH-7873] - guests/redhat: Properly configure network and restart service [GH-7751] - guests/redhat: Prevent NetworkManager from managing devices on initial start [GH-7926] - hosts/linux: Fix race condition in writing /etc/exports file for NFS configuration [GH-7947, GH-7938] - Thanks to Aron Griffis (@agriffis) for identifying this issue - plugins/rsync: Escape exclude paths [GH-7928, GH-7910] - providers/docker: Remove --interactive flag when pty is true [GH-7688] - provisioners/ansible_local: Use enquoted path for file/directory existence checks - provisioners/salt: Synchronize configuration defaults with documentation [GH-7907, GH-6624] - pushes/atlas: Fix atlas push on Windows platform [GH-6938, GH-7802] ## 1.8.6 (September 27, 2016) IMPROVEMENTS: - Add detection for DragonFly BSD [GH-7701] - Implement auto_start and auto_stop actions for Hyper-V [GH-7647] - communicators/ssh: Remove any content prepended to STDOUT [GH-7676, GH-7613] BUG FIXES: - commands/package: Provide machine data directory for base box package [GH-5070, GH-7725] - core: Fix windows path formatting [GH-6598] - core: Fixes for ssh-agent interactions [GH-7703, GH-7621, GH-7398] - core: Support VAGRANT_DOTFILE_PATH relative to the Vagrantfile [GH-7623] - guests: Prevent ssh disconnect errors on halt command [GH-7675] - guests/bsd: Remove Darwin matching [GH-7701] - guests/linux: Fix SSH key permissions [GH-7610, GH-7611] - guests/linux: Always sort discovered network interfaces [GH-7705, GH-7668] - guests/linux: Fixes for user and group ID lookups for virtualbox shared folders [GH-7616, GH-7662, GH-7720] - guests/openbsd: Add custom halt capability [GH-7701] - guests/ubuntu: Fix detection on older guests [GH-7632, GH-7524, GH-7625] - hosts/arch: Detect NFS server by service name on arch [GH-7630, GH-7629] - hosts/darwin: Fix generated RDP configuration file [GH-7698] - provisioners/ansible: Add support for `ssh.proxy_command` setting [GH-7752] - synced_folders/nfs: Display warning when configured for NFSv4 and UDP [GH-7740] - synced_folders/rsync: Properly ignore excluded files within synced directory from `chown` command. [GH-5256, GH-7726] ## 1.8.5 (July 18, 2016) FEATURES: - core: Provide a way to globally disable box update checks with the environment variable `VAGRANT_BOX_UPDATE_CHECK_DISABLE`. Setting this to any non-empty value will instruct Vagrant to not look for box updates when running `vagrant up`. Setting this environment variable has no effect on the `vagrant box` commands. IMPROVEMENTS: - guests/arch: Support installing synced folder clients [GH-7519] - guests/darwin: Allow ipv6 static networks [GH-7491] - providers/virtualbox: Add support for 5.1 [GH-7574] BUG FIXES: - core: Bump listen gem and Ruby version to improve rsync performance [GH-7453, GH-7441] - core: Check process stdout when detecting if a hyperv admin [GH-7465, GH-7467] - core: Ensure removal of temporary directory when box download fails [GH-7496, GH-7499] - core: Fix regression for installing plugins from path [GH-7505, GH-7493] - core: Skip checking conflicts on disabled ports [GH-7587] - core: Idempotent write-out for state file [GH-7550] - core/guests: Create common BSD guest for shared logic - core/guests: Ignore empty output from `/sbin/ip` [GH-7539, GH-7537, GH-7533, GH-7605] - synced_folders/nfs: Shellescape rsync paths [GH-7540, GH-7605] - synced_folders/nfs: Ensure retries take place [GH-6360, GH-7605] - synced_folders/rsync: Shellescape rsync paths [GH-7580, GH-6690, GH-7579, GH-7605] - synced_folders/rsync: Translate Windows paths [GH-7012, GH-6702, GH-6568, GH-7046] - guests/bsd: Consolidate core logic for mounting NFS folders [GH-7480, GH-7474, GH-7466] - guests/bsd: Consolidate core logic for public key management [GH-7481] - guests/bsd: Consolidate core logic for halting [GH-7484] - guests/centos: Use `ip` instead of `ifconfig` to detect network interfaces [GH-7460] - guests/debian: Ensure newline when inserting public key [GH-7456] - guests/linux: Ensure NFS retries during mounting [GH-7492] - guests/redhat: Use `/sbin/ip` to list and configure networks for compatability with older versions of CentOS [GH-7482] - guests/redhat: Ensure newline when inserting public key [GH-7598, GH-7605] - guests/ubuntu: Use /etc/os-release to detech [GH-7524] - guests/ubuntu: Use short hostname [GH-7488, GH-7605] - providers/hyperv: Fix version check and catch statement [GH-7447, GH-7487] ## 1.8.4 (June 13, 2016) BUG FIXES: - core: Fix bundler plugin issue and version constraint [GH-7418, GH-7415] - providers/virtualbox: Use 8 network interfaces (due to Windows limitation) [GH-7417, GH-7419] - provisioners/ansible(both): Honor "galaxy_roles_path" option when running ansible-playbook [GH-7269, GH-7420] - provisioners/ansible_local: Add quotes around "ansible-galaxy" arguments [GH-7420] IMPROVEMENTS: - guests/redhat: Add CloudLinux detection [GH-7428, GH-7427] ## 1.8.3 (June 10, 2016) BREAKING CHANGES: - The `winrm` communicator now shares the same upload behavior as the `ssh` communicator. This change should have no impact to most vagrant operations but may break behavior when uploading directories to an existing destination target. The `file` provisioner should be the only builtin provisioner affected by this change. When uploading a directory and the destination directory exists on the endpoint, the source base directory will be created below the destination directory on the endpoint and the source directory contents will be unzipped to that location. Prior to this release, the contents of the source directory would be unzipped to an existing destination directory without creating the source base directory. This new behavior is more consistent with SCP and other well known shell copy commands. - The Chef provisioner's `channel` default value has changed from "current" to "stable". The "current" channel includes nightly releases and should be opt-in only. Note that users wishing to download the Chef Development Kit will need to opt into the "current" channel until Chef Software promotes into the "stable" channel. - The Arch Linux host capability for NFS removed support for rc.d in favor or systemd which has been present since 2012. Please see GH-7181 for more information. FEATURES: - provider/docker: Allow non-linux users to opt-out of the host VM to run Docker containers by setting `config.force_host_vm = false` in the Vagrantfile. This is especially useful for customers who wish to use the beta builds for Mac and Windows, dlite, or a custom provider. [GH-7277, GH-7298, 8c11b53] - provider/docker: New command: `docker-exec` allows attaching to an already-running container. [GH-7377, GH-6566, GH-5193, GH-4904, GH-4057, GH-4179, GH-4903] IMPROVEMENTS: - core/downloader: increase box resume download limit to 24h [GH-7352, GH-7272] - core/package: run validations prior to packaging [GH-7353, GH-7351] - core/action: make `start` ("vagrant up") run provisioners [GH-4467, GH-4421] - commands/all: Make it clear that machine IDs can be specified [GH-7356, GH-7228] - commands/init: Add support for specifying the box version [GH-7363, GH-5004] - commands/login: Print a warning with both the environment variable and local login token are present [GH-7206, GH-7219] - communicators/winrm: Upgrade to latest WinRM gems [GH-6922] - provisioners/ansible_local: Allow to install Ansible from pip, with version selection capability [GH-6654, GH-7167] - provisioners/ansible_local: Use `provisioning_path` as working directory for `ansible-galaxy` execution - provisioners/ansible(both provisioners): Add basic config validators/converters on `raw_arguments` and `raw_ssh_args` options [GH-7103] - provisioners/chef: Add the ability to install on SUSE [GH-6806] - provisioners/chef: Support legacy solo mode [GH-7327] - provisioners/docker: Restart container if newer image is available [GH-7358, GH-6620] - hosts/arch: Remove sysvinit and assume systemd [GH-7181] - hosts/linux: Do not use a pager with systemctl commands [GH-7270] - hosts/darwin: Add `extra_args` support for RDP [GH-5523, GH-6602] - hosts/windows: Use SafeExec to capture history in Powershell [GH-6749] - guests/amazon: Add detection [GH-7395, GH-7254] - guests/freebsd: Add quotes around hostname [GH-6867] - guests/fedora: Add support for ipv6 static networks [GH-7275, GH-7276] - guests/tinycore: Add support for shared folders [GH-6977, GH-6968] - guests/trisquel: Add initial support [GH-6842, GH-6843] - guests/windows: Add support for automatic login (no password prompting) [GH-5670] - core: Add `--no-delete` and provisioning flags to snapshot restore/pop [GH-6879] - providers/docker: Allow TCP and UDP ports on the same number [GH-7365, GH-5527] - providers/hyperv: Add support for differencing disk [GH-7090] - providers/hyperv: Add support for snapshots [GH-7110] - providers/hyperv: Reinstate compatibility with PS 4 [GH-7108] - providers/virtualbox: Add linked clone support for Virtualbox 1.4 [GH-7050] - synced_folders/nfs: Read static and dynamic IPs [GH-7290, GH-7289] BUG FIXES: - core: Bump nokogiri version to fix windows bug [GH-6766, GH-6848] - core: Revert a change made to the output of the identify file [GH-6962, GH-6929, GH-6589] - core: Fix login command behind a proxy [GH-6898, GH-6899] - core: Fix support for regular expressions on multi-machine `up` [GH-6908, GH-6909] - core: Allow boxes to use pre-release versions [GH-6892, GH-6893] - core: Rescue `Errno:ENOTCONN` waiting for port to be open [GH-7182, GH-7184] - core: Properly authenticate metadata box URLs [GH-6776, GH-7158] - core: Do not run provisioners if already run on resume [GH-7059, GH-6787] - core: Implement better tracking of tempfiles and tmpdirs to identify file leaks [GH-7355] - core: Allow SSH forwarding on Windows [GH-7287, GH-7202] - core: Allow customizing `keys_only` SSH option [GH-7360, GH-4275] - core: Allow customizing `paranoid` SSH option [GH-7360, GH-4275] - command/box_update: Do not update the same box twice [GH-6042, GH-7379] - command/init: Remove unnecessary `sudo` from generated Vagrantfile [GH-7369, GH-7295] - docs & core: Be consistent about the "2" in the Vagrantfile version [GH-6961, GH-6963] - guests/all: Refactor guest capabilities to run in a single command - **please see GH-7393 for the complete list of changes!** - guests/arch: Restart network after configuration [GH-7120, GH-7119] - guests/debian: Do not return an error if ifdown fails [GH-7159, GH-7155, GH-6871] - guests/freebsd: Use `pkg` to install rsync [GH-6760] - guests/freebsd: Use `netif` to configure networks [GH-5852, GH-7093] - guests/coreos: Detect all interface names [GH-6608, GH-6610] - providers/hyperv: Only specify Hyper-V if the parameter is support [GH-7101, GH-7098] - providers/virtualbox: Set maximum network adapters to 36 [GH-7293, GH-7286] - providers/virtualbox: Do not fail when master VM from linked clone is missing [GH-7126, GH-6742] - providers/virtualbox: Use scoped overrides in preparring NFS [GH-7387, GH-7386] - provisioners/ansible: Fix a race condition in the concurrent generations of the ansible inventory file, while running `vagrant up --parallel` [GH-6526, GH-7190] - provisioners/ansible_local: Don't quote the Ansible arguments defined in the `raw_arguments` option [GH-7103] - provisioners/ansible_local: Format json `extra_vars` with double quotes [GH-6726, GH-7103] - provisioners/ansible_local: Fix errors in absolute paths to playbook or galaxy resources when running on a Windows host [GH-6740, GH-6757] - provisioners/ansible_local: Change the way to verify `ansible-galaxy` presence, to avoid a non-zero status code with Ansible 2.0 [GH-6793] - provisioners/ansible(both provisioners): The Ansible configuration files detection is only executed by the `provision` action [GH-6763, GH-6984] - provisioners/chef: Do not use double sudo when installing [GGH-6805, GH-6804] - provisioners/chef: Change the default channel to "stable" (previously it was "current") [GH-7001, GH-6979] - provisioners/chef: Default node_name to hostname if present [GH-7063, GH-7153] - provisioners/docker: Fix -no-trunc command option [GH-7085] - provisioners/docker: Allow provisioning when container name is specified [GH-7074, GH-7086] - provisioners/puppet: Use `where.exe` to locate puppet binary [GH-6912, GH-6876] - provisioners/salt: Move masterless config to apply to all platforms [GH-7207, Gh-6924, GH-6915] - pushes/ftp: Create parent directories when uploading [GH-7154, GH-6316] - synced_folders/smb: Do not interpolate configuration file [GH-6906] ## 1.8.1 (December 21, 2015) BUG FIXES: - core: Don't create ".bundle" directory in pwd [GH-6717] - core: Fix exception on installing VirtualBox [GH-6713] - core: Do not convert standalone drive letters such as "D:" to UNC paths [GH-6598] - core: Fix a crash in parsing the config in some cases with network configurations [GH-6730] - core: Clean up temporarily files created by bundler [GH-7354, GH-6301, GH-3469, GH-6231] - commands/up: Smarter logic about what provider to install, avoiding situations where VirtualBox was installed over the correct provider [GH-6731] - guests/debian: Fix Docker install [GH-6722] - provisioners/chef: convert chef version to a string before comparing for the command builder [GH-6709, GH-6711] - provisioners/shell: convert env var values to strings [GH-6714] ## 1.8.0 (December 21, 2015) FEATURES: - **New Command: `vagrant powershell`**: For machines that support it, this will open a PowerShell prompt. - **New Command: `vagrant port`**: For machines that support it, this will display the list of forwarded ports from the guest to the host. - **Linked Clones**: VirtualBox and VMware providers now support linked clones for very fast (millisecond) imports on up. [GH-4484] - **Snapshots**: The `vagrant snapshot` command can be used to checkpoint and restore point-in-time snapshots. - **IPv6 Private Networks**: Private networking now supports IPv6. This only works with VirtualBox and VMware at this point. [GH-6342] - New provisioner: `ansible_local` to execute Ansible from the guest machine. [GH-2103] BREAKING CHANGES: - The `ansible` provisioner now can override the effective ansible remote user (i.e. `ansible_ssh_user` setting) to always correspond to the vagrant ssh username. This change is enabled by default, but we expect this to affect only a tiny number of people as it corresponds to the common usage. If you however use multiple remote usernames in your Ansible plays, tasks, or custom inventories, you can simply set the option `force_remote_user` to false to make Vagrant behave the same as before. - provisioners/salt: the "config_dir" option has been removed. It has no effect in Vagrant 1.8. [GH-6073] IMPROVEMENTS: - core: allow removal of all box versions with `--all` flag [GH-3462] - core: prune entries from global status on non-existent cwd [GH-6535] - core: networking: allow specifying a DHCP IP [GH-6325] - core: run provisioner cleanup tasks before powering off the VM [GH-6553] - core: only run provisioner cleanup tasks if they're implemented [GH-6603] This improves UX, but wasn't a bug before. - command/plugin: Add `--plugin-clean-sources` flag to reset plugin install sources, primarily for corp firewalls. [GH-4738] - command/rsync-auto: SSH connection is cached for faster sync times [GH-6399] - command/up: provisioners are run on suspend resume [GH-5815] - communicators/ssh: allow specifying host environment variables to forward to guests [GH-4132, GH-6562] - communicators/winrm: Configurable execution time limit [GH-6213] - providers/virtualbox: cache version lookup, which caused significant slowdown on some Windows hosts [GH-6552] - providers/virtualbox: add `public_address` capability for virtualbox [GH-6583, GH-5978] - provisioners/chef: perform cleanup tasks on the guest instead of the host - provisioners/chef: automatically generate a node_name if one was not given [GH-6555] - provisioners/chef: install Chef automatically on Windows [GH-6557] - provisioners/chef: allow the user to specify the Chef product (such as the Chef Development Kit) [GH-6557] - provisioners/chef: allow data_bags_path to be an array [GH-5988, GH-6561] - provisioners/shell: Support interactive mode for elevated PowerShell scripts [GH-6185] - provisioners/shell: add `env` option [GH-6588, GH-6516] - provisioners/ansible+ansible_local: add support for ansible-galaxy [GH-2718] - provisioners/ansible+ansible_local: add support for group and host variables in the generated inventory [GH-6619] - provisioners/ansible+ansible_local: add support for alphanumeric patterns for groups in the generated inventory [GH-3539] - provisioners/ansible: add support for WinRM settings [GH-5086] - provisioners/ansible: add new `force_remote_user` option to control whether `ansible_ssh_user` parameter should be applied or not [GH-6348] - provisioners/ansible: show a warning when running from a Windows Host [GH-5292] - pushes/local-exec: add support for specifying script args [GH-6661, GH-6660] - guests/slackware: add support for networking [GH-6514] BUG FIXES: - core: Ctrl-C weirdness fixed where it would exit parent process before Vagrant finished cleaning up [GH-6085] - core: DHCP network configurations don't warn on IP addresses ending in ".1" [GH-6150] - core: only append `access_token` when it does not exist in the URL [GH-6395, GH-6534] - core: use the correct private key when packaging a box [GH-6406] - core: fix crash when using invalid box checksum type [GH-6327] - core: don't check for metadata if the download URL is not HTTP [GH-6540] - core: don't make custom dotfile path if there is no Vagrantfile [GH-6542] - core: more robust check for admin privs on Windows [GH-5616] - core: properly detect when HTTP server doesn't support byte ranges and retry from scratch [GH-4479] - core: line numbers show properly in Vagrantfile syntax errors on Windows [GH-6445] - core: catch errors setting env vars on Windows [GH-6017] - core: remove cached synced folders when they're removed from the Vagrantfile [GH-6567] - core: use case-insensitive comparison for box checksum validations [GH-6648, GH-6650] - commands/box: add command with `~` paths on Windows works [GH-5747] - commands/box: the update command supports CA settings [GH-4473] - commands/box: removing all versions and providers of a box will properly clean all directories in `~/.vagrant.d/boxes` [GH-3570] - commands/box: outdated global won't halt on metadata download failure [GH-6453] - commands/login: respect environment variables in `vagrant login` command [GH-6590, GH-6422] - commands/package: when re-packaging a packaged box, preserve the generated SSH key [GH-5780] - commands/plugin: retry plugin install automatically a few times to avoid network issues [GH-6097] - commands/rdp: prefer `xfreerdp` if it is available on Linux [GH-6475] - commands/up: the `--provision-with` flag works with provisioner names [GH-5981] - communicator/ssh: fix potential crash case with PTY [GH-6225] - communicator/ssh: escape IdentityFile path [GH-6428, GH-6589] - communicator/winrm: respect `boot_timeout` setting [GH-6229] - communicator/winrm: execute scheduled tasks immediately on Windows XP since elevation isn't required [GH-6195] - communicator/winrm: Decouple default port forwarding rules for "winrm" and "winrm-ssl" [GH-6581] - communicator/winrm: Hide progress bars from PowerShell v5 [GH-6309] - guests/arch: enable network device after setting it up [GH-5737] - guests/darwin: advanced networking works with more NICs [GH-6386] - guests/debian: graceful shutdown works properly with newer releases [GH-5986] - guests/fedora: Preserve `localhost` entry when changing hostname [GH-6203] - guests/fedora: Use dnf if it is available [GH-6288] - guests/linux: when replacing a public SSH key, use POSIX-compliant sed flags [GH-6565] - guests/suse: DHCP network interfaces properly configured [GH-6502] - hosts/slackware: Better detection of NFS [GH-6367] - providers/hyper-v: support generation 2 VMs [GH-6372] - providers/hyper-v: support VMs with more than one NIC [GH-4346] - providers/hyper-v: check if user is in the Hyper-V admin group if they're not a Windows admin [GH-6662] - providers/virtualbox: ignore "Unknown" status bridge interfaces [GH-6061] - providers/virtualbox: only fix ipv6 interfaces that are in use [GH-6586, GH-6552] - provisioners/ansible: use quotes for the `ansible_ssh_private_key_file` value in the generated inventory [GH-6209] - provisioners/ansible: use quotes when passing the private key files via OpenSSH `-i` command line arguments [GH-6671] - provisioners/ansible: don't show the `ansible-playbook` command when verbose option is an empty string - provisioners/chef: fix `nodes_path` for Chef Zero [GH-6025, GH-6049] - provisioners/chef: do not error when the `node_name` is unset [GH-6005, GH-6064, GH-6541] - provisioners/chef: only force the formatter on Chef 11 or higher [GH-6278, GH-6556] - provisioners/chef: require `nodes_path` to be set for Chef Zero [GH-6110, GH-6559] - provisioners/puppet: apply provisioner uses correct default manifests with environments. [GH-5987] - provisioners/puppet: remove broken backticks [GH-6404] - provisioners/puppet: find Puppet binary properly on Windows [GH-6259] - provisioners/puppet-server: works with Puppet Collection 1 [GH-6389] - provisioners/salt: call correct executables on Windows [GH-5999] - provisioners/salt: log level and colorize works for masterless [GH-6474] - push/local-exec: use subprocess on windows when fork does not exist [GH-5307, GH-6563] - push/heroku: use current branch [GH-6554] - synced\_folders/rsync: on Windows, replace all paths with Cygwin paths since all rsync implementations require this [GH-6160] - synced\_folders/smb: use credentials files to allow for more characters in password [GH-4230] PLUGIN AUTHOR CHANGES: - installer: Upgrade to Ruby 2.2.3 ## 1.7.4 (July 17, 2015) BUG FIXES: - communicators/winrm: catch timeout errors [GH-5971] - communicators/ssh: use the same SSH args for `vagrant ssh` with and without a command [GH-4986, GH-5928] - guests/fedora: networks can be configured without nmcli [GH-5931] - guests/fedora: biosdevname can return 4 or 127 [GH-6139] - guests/redhat: systemd detection should happen on guest [GH-5948] - guests/ubuntu: setting hostname fixed in 12.04 [GH-5937] - hosts/linux: NFS can be configured without `$TMP` set on the host [GH-5954] - hosts/linux: NFS will sudo copying back to `/etc/exports` [GH-5957] - providers/docker: Add `pull` setting, default to false [GH-5932] - providers/virtualbox: remove UNC path conversion on Windows since it caused mounting regressions [GH-5933] - provisioners/puppet: Windows Puppet 4 paths work correctly [GH-5967] - provisioners/puppet: Fix config merging errors [GH-5958] - provisioners/salt: fix "dummy config" error on bootstrap [GH-5936] ## 1.7.3 (July 10, 2015) FEATURES: - **New guest: `atomic`* - Project Atomic is supported as a guest - providers/virtualbox: add support for 5.0 [GH-5647] IMPROVEMENTS: - core: add password authentication to rdp_info hash [GH-4726] - core: improve error message when packaging fails [GH-5399] - core: improve message when adding a box from a file path [GH-5395] - core: add support for network gateways [GH-5721] - core: allow redirecting stdout and stderr in the UI [GH-5433] - core: update version of winrm-fs to 0.2.0 [GH-5738] - core: add option to enabled trusted http(s) redirects [GH-4422] - core: capture additional information such as line numbers during Vagrantfile loading [GH-4711, GH-5769] - core: add .color? to UI objects to see if they support color [GH-5771] - core: ignore hidden directories when searching for boxes [GH-5748, GH-5785] - core: use `config.ssh.sudo_command` to customize the sudo command format [GH-5573] - core: add `Vagrant.original_env` for Vagrant and plugins to restore or inspect the original environment when Vagrant is being run from the installer [GH-5910] - guests/darwin: support inserting generated key [GH-5204] - guests/darwin: support mounting SMB shares [GH-5750] - guests/fedora: support Fedora 21 [GH-5277] - guests/fedora: add capabilities for nfs and flavor [GH-5770, GH-4847] - guests/linux: specify user's domain as separate parameter [GH-3620, GH-5512] - guests/redhat: support Scientific Linux 7 [GH-5303] - guests/photon: initial support [GH-5612] - guests/solaris,solaris11: support inserting generated key [GH-5182] [GH-5290] - providers/docker: images are pulled prior to starting [GH-5249] - provisioners/ansible: store the first ssh private key in the auto-generated inventory [GH-5765] - provisioners/chef: add capability for checking if Chef is installed on Windows [GH-5669] - provisioners/docker: restart containers if arguments have changed [GH-3055, GH-5924] - provisioners/puppet: add support for Puppet 4 and configuration options [GH-5601] - provisioners/puppet: add support for `synced_folder_args` in apply [GH-5359] - provisioners/salt: add configurable `config_dir` [GH-3138] - provisioners/salt: add support for masterless configuration [GH-3235] - provisioners/salt: provider path to missing file in errors [GH-5637] - provisioners/salt: add ability to run salt orchestrations [GH-4371] - provisioners/salt: update to 2015.5.2 [GH-4152, GH-5437] - provisioners/salt: support specifying version to install [GH-5892] - provisioners/shell: add :name attribute to shell provisioner [GH-5607] - providers/docker: supports file downloads with the file provisioner [GH-5651] - providers/docker: support named Dockerfile [GH-5480] - providers/docker: don't remove image on reload so that build cache can be used fully [GH-5905] - providers/hyperv: select a Hyper-V switch based on a `network_name` [GH-5207] - providers/hyperv: allow configuring VladID [GH-5539] - providers/virtualbox: regexp supported for bridge configuration [GH-5320] - providers/virtualbox: handle a list of bridged NICs [GH-5691] - synced_folders/rsync: allow showing rsync output in debug mode [GH-4867] - synced_folders/rsync: set `rsync__rsync_path` to specify the remote command used to execute rsync [GH-3966] BUG FIXES: - core: push configurations are validated with global configs [GH-5130] - core: remove executable permissions on internal file [GH-5220] - core: check name and version in `has_plugin?` [GH-5218] - core: do not create duplicates when defining two private network addresses [GH-5325] - core: update ssh to check for Plink [GH-5604] - core: do not report plugins as installed when plugins are disabled [GH-5698, GH-5430] - core: Only take files when packaging a box to avoid duplicates [GH-5658, GH-5657] - core: escape curl urls and authentication [GH-5677] - core: fix crash if a value is missing for CLI arguments [GH-5550] - core: retry SSH key generation for transient RSA errors [GH-5056] - core: `ssh.private_key_path` will override the insecure key [GH-5632] - core: restore the original environment when shelling out to subprocesses outside of the installer [GH-5912] - core/cli: fix box checksum validation [GH-4665, GH-5221] - core/windows: allow Windows UNC paths to allow more than 256 characters [GH-4815] - command/rsync-auto: don't crash if rsync command fails [GH-4991] - communicators/winrm: improve error handling significantly and improve the error messages shown to be more human-friendly. [GH-4943] - communicators/winrm: remove plaintext passwords from files after provisioner is complete [GH-5818] - hosts/nfs: allow colons (`:`) in NFS IDs [GH-5222] - guests/darwin: remove dots from LocalHostName [GH-5558] - guests/debian: Halt works properly on Debian 8. [GH-5369] - guests/fedora: recognize future fedora releases [GH-5730] - guests/fedora: reload iface connection by NetworkManager [GH-5709] - guests/fedora: do not use biosdevname if it is not installed [GH-5707] - guests/freebsd: provide an argument to the backup file [GH-5516, GH-5517] - guests/funtoo: fix incorrect path in configure networks [GH-4812] - guests/linux: fix edge case exception where no home directory is available on guest [GH-5846] - guests/linux: copy NFS exports to tmpdir to do edits to guarantee permissions are available [GH-5773] - guests/openbsd: output newline after inserted public key [GH-5881] - guests/tinycore: fix change hostname functionality [GH-5623] - guests/ubuntu: use `hostnamectl` to set hostname on Ubuntu Vivid [GH-5753] - guests/windows: Create rsync folder prior to rsync-ing. [GH-5282] - guests/windows: Changing hostname requires reboot again since the non-reboot code path was crashing Windows server. [GH-5261] - guests/windows: ignore virtual NICs [GH-5478] - hosts/windows: More accurately get host IP address in VPNs. [GH-5349] - plugins/login: allow users to login with a token [GH-5145] - providers/docker: Build image from `/var/lib/docker` for more disk space on some systems. [GH-5302] - providers/docker: Fix crash that could occur in some scenarios when the host VM path changed. - providers/docker: Fix crash that could occur on container destroy with VirtualBox shared folders [GH-5143] - providers/hyperv: allow users to configure memory, cpu count, and vmname [GH-5183] - providers/hyperv: import respects secure boot. [GH-5209] - providers/hyperv: only set EFI secure boot for gen 2 machines [GH-5538] - providers/virtualbox: read netmask from dhcpservers [GH-5233] - providers/virtualbox: Fix exception when VirtualBox version is empty. [GH-5308] - providers/virtualbox: Fix exception when VBoxManage.exe can't be run on Windows [GH-1483] - providers/virtualbox: Error if another user is running after a VM is created to avoid issue with VirtualBox "losing" the VM [GH-5895] - providers/virtualbox: The "name" setting on private networks will choose an existing hostonly network [GH-5389] - provisioners/ansible: fix SSH settings to support more than 5 ssh keys [GH-5017] - provisioners/ansible: increase ansible connection timeout to 30 seconds [GH-5018] - provisioners/ansible: disable color if Vagrant is not colored [GH-5531, GH-5532] - provisioners/ansible: only show ansible-playbook command when `verbose` option is enabled [GH-5803] - provisioners/ansible: fix a race condition in the inventory file generation [GH-5551] - provisioners/docker: use `service` to restart Docker instad of upstart [GH-5245, GH-5577] - provisioners/docker: Only add docker user to group if exists. [GH-5315] - provisioners/docker: Use https for repo [GH-5749] - provisioners/docker: `apt-get update` before installing linux kernel images to get the correct version [GH-5860] - provisioners/chef: Fix shared folders missing error [GH-5199] - provisioners/chef: Use `command -v` to check for binary instead of `which` since that doesn't exist on some systems. [GH-5170] - provisioners/chef-zero: support more chef-zero/local mode attributes [GH-5339] - provisioners/chef: use windows-specific paths in Chef provisioners [GH-5913] - provisioners/docker: use docker.com instead of docker.io [GH-5216] - provisioners/docker: use `--restart` instead of `-r` on daemon [GH-4477] - provisioners/file: validation of source is relative to Vagrantfile [GH-5252] - pushes/atlas: send additional box metadata [GH-5283] - pushes/local-exec: fix "text file busy" error for inline [GH-5695] - pushes/ftp: improve check for remote directory existence [GH-5549] - synced\_folders/rsync: add `IdentitiesOnly=yes` to the rsync command. [GH-5175] - synced\_folders/smb: use correct `password` option [GH-5805] - synced\_folders/smb: prever IPv4 over IPv6 address to mount [GH-5798] - virtualbox/config: fix misleading error message for private_network [GH-5536, GH-5418] ## 1.7.2 (January 6, 2015) BREAKING CHANGES: - If you depended on the paths that Chef/Puppet provisioners use to store cookbooks (ex. "/tmp/vagrant-chef-1"), these will no longer be correct. Without this change, Chef/Puppet didn't work at all with `vagrant provision`. We expect this to affect only a minor number of people, since it's not something that was ever documented or recommended by Vagrant, or even meant to be supported. FEATURES: - provisioners/salt: add support for grains [GH-4895] IMPROVEMENTS: - commands/reload,up: `--provision-with` implies `--provision` [GH-5085] BUG FIXES: - core: private boxes still referencing vagrantcloud.com will have their vagrant login access token properly appended - core: push plugin configuration is properly validated - core: restore box packaging functionality - commands/package: fix crash - commands/push: push lookups are by user-defined name, not push strategy name [GH-4975] - commands/push: validate the configuration - communicators/winrm: detect parse errors in PowerShell and error - guests/arch: fix network configuration due to poor line breaks. [GH-4964] - guests/solaris: Merge configurations properly so configs can be set in default Vagrantfiles. [GH-5092] - installer: SSL cert bundle contains 1024-bit keys, fixing SSL verification for a lot of sites. - installer: vagrant executable properly `cygpaths` the SSL bundle path for Cygwin - installer: Nokogiri (XML lib used by Vagrant and dependencies) linker dependencies fixed, fixing load issues on some platforms - providers/docker: Symlinks in shared folders work. [GH-5093] - providers/hyperv: VM start errors turn into proper Vagrant errors. [GH-5101] - provisioners/chef: fix missing shared folder error [GH-4988] - provisioners/chef: remove Chef version check from solo.rb generation and make `roles_path` populate correctly - provisioners/chef: fix bad invocation of `with_clean_env` [GH-5021] - pushes/atlas: support more verbose logging - pushes/ftp: expand file paths relative to the Vagrantfile - pushes/ftp: improved debugging output - pushes/ftp: create parent directories if they do not exist on the remote server ## 1.7.1 (December 11, 2014) IMPROVEMENTS: - provisioners/ansible: Use Docker proxy if needed. [GH-4906] BUG FIXES: - providers/docker: Add support of SSH agent forwarding. [GH-4905] ## 1.7.0 (December 9, 2014) BREAKING CHANGES: - provisioners/ansible: `raw_arguments` has now highest priority - provisioners/ansible: only the `ssh` connection transport is supported (`paramiko` can be enabled with `raw_arguments` at your own risks) FEATURES: - **Vagrant Push**: Vagrant can now deploy! `vagrant push` is a single command to deploy your application. Deploy to Heroku, FTP, or HashiCorp's commercial product Atlas. New push strategies can be added with plugins. - **Named provisioners**: Provisioners can now be named. This name is used for output as well as `--provision-with` for better control. - Default provider logic improved: Providers in `config.vm.provider` blocks in your Vagrantfile now have higher priority than plugins. Earlier providers are chosen before later ones. [GH-3812] - If the default insecure keypair is used, Vagrant will automatically replace it with a randomly generated keypair on first `vagrant up`. [GH-2608] - Vagrant Login is now part of Vagrant core - Chef Zero provisioner: Use Chef 11's "local" mode to run recipes against an in-memory Chef Server - Chef Apply provisioner: Specify inline Chef recipes and recipe snippets using the Chef Apply provisioner IMPROVEMENTS: - core: `has_plugin?` function now takes a second argument which is a version constraint requirement. [GH-4650] - core: ".vagrantplugins" file in the same folder as your Vagrantfile will be loaded for defining inline plugins. [GH-3775] - commands/plugin: Plugin list machine-readable output contains the plugin name as the target for versions and other info. [GH-4506] - env/with_cleanenv: New helper for plugin developers to use when shelling out to another Ruby environment - guests/arch: Support predictable network interface naming. [GH-4468] - guests/suse: Support NFS client install, rsync setup. [GH-4492] - guests/tinycore: Support changing host names. [GH-4469] - guests/tinycore: Support DHCP-based networks. [GH-4710] - guests/windows: Hostname can be set without reboot. [GH-4687] - providers/docker: Build output is now shown. [GH-3739] - providers/docker: Can now start containers from private repositories more easily. Vagrant will login for you if you specify auth. [GH-4042] - providers/docker: `stop_timeout` can be used to modify the `docker stop` timeout. [GH-4504] - provisioners/chef: Automatically install Chef when using a Chef provisioner. - provisioners/ansible: Always show Ansible command executed when Vagrant log level is debug (even if ansible.verbose is false) - synced\_folders/nfs: Won't use `sudo` to write to /etc/exports if there are write privileges. [GH-2643] - synced\_folders/smb: Credentials from one SMB will be copied to the rest. [GH-4675] BUG FIXES: - core: Fix cases where sometimes SSH connection would hang. - core: On a graceful halt, force halt if capability "insert public key" is missing. [GH-4684] - core: Don't share `/vagrant` if any "." folder is shared. [GH-4675] - core: Fix SSH private key permissions more aggressively. [GH-4670] - core: Custom Vagrant Cloud server URL now respected in more cases. - core: On downloads, don't continue downloads if the remote server doesn't support byte ranges. [GH-4479] - core: Box downloads recognize more complex content types that include "application/json" [GH-4525] - core: If all sub-machines are `autostart: false`, don't start any. [GH-4552] - core: Update global-status state in more cases. [GH-4513] - core: Only delete machine state if the machine is not created in initialize - commands/box: `--cert` flag works properly. [GH-4691] - command/docker-logs: Won't crash if container is removed. [GH-3990] - command/docker-run: Synced folders will be attached properly. [GH-3873] - command/rsync: Sync to Docker containers properly. [GH-4066] - guests/darwin: Hostname sets bonjour name and local host name. [GH-4535] - guests/freebsd: NFS mounting can specify the version. [GH-4518] - guests/linux: More descriptive error message if SMB mount fails. [GH-4641] - guests/rhel: Hostname setting on 7.x series works properly. [GH-4527] - guests/rhel: Installing NFS client works properly on 7.x [GH-4499] - guests/solaris11: Static IP address preserved after restart. [GH-4621] - guests/ubuntu: Detect with `lsb_release` instead of `/etc/issue`. [GH-4565] - hosts/windows: RDP client shouldn't map all drives by default. [GH-4534] - providers/docker: Create args works. [GH-4526] - providers/docker: Nicer error if package is called. [GH-4595] - providers/docker: Host IP restriction is forwarded through. [GH-4505] - providers/docker: Protocol is now honored in direct `ports settings. - providers/docker: Images built using `build_dir` will more robustly capture the final image. [GH-4598] - providers/docker: NFS synced folders now work. [GH-4344] - providers/docker: Read the created container ID more robustly. - providers/docker: `vagrant share` uses correct IP of proxy VM if it exists. [GH-4342] - providers/docker: `vagrant_vagrantfile` expands home directory. [GH-4000] - providers/docker: Fix issue where multiple identical proxy VMs would be created. [GH-3963] - providers/docker: Multiple links with the same name work. [GH-4571] - providers/virtualbox: Show a human-friendly error if VirtualBox didn't clean up an existing VM. [GH-4681] - providers/virtualbox: Detect case when VirtualBox reports 0.0.0.0 as IP address and don't allow it. [GH-4671] - providers/virtualbox: Show more descriptive error if VirtualBox is reporting an empty version. [GH-4657] - provisioners/ansible: Force `ssh` (OpenSSH) connection by default [GH-3396] - provisioners/ansible: Don't use or modify `~/.ssh/known_hosts` file by default, similarly to native vagrant commands [GH-3900] - provisioners/ansible: Use intermediate Docker host when needed. [GH-4071] - provisioners/docker: Get GPG key over SSL. [GH-4597] - provisioners/docker: Search for docker binary in multiple places. [GH-4580] - provisioners/salt: Highstate works properly with a master. [GH-4471] - provisioners/shell: Retry getting SSH info a few times. [GH-3924] - provisioners/shell: PowerShell scripts can have args. [GH-4548] - synced\_folders/nfs: Don't modify NFS exports file if no exports. [GH-4619] - synced\_folders/nfs: Prune exports for file path IDs. [GH-3815] PLUGIN AUTHOR CHANGES: - `Machine#action` can be called with the option `lock: false` to not acquire a machine lock. - `Machine#reload` will now properly trigger the `machine_id_changed` callback on providers. ## 1.6.5 (September 4, 2014) BUG FIXES: - core: forward SSH even if WinRM is used. [GH-4437] - communicator/ssh: Fix crash when pty is enabled with SSH. [GH-4452] - guests/redhat: Detect various RedHat flavors. [GH-4462] - guests/redhat: Fix typo causing crash in configuring networks. [GH-4438] - guests/redhat: Fix typo causing hostnames to not set. [GH-4443] - providers/virtualbox: NFS works when using DHCP private network. [GH-4433] - provisioners/salt: Fix error when removing non-existent bootstrap script on Windows. [GH-4614] ## 1.6.4 (September 2, 2014) BACKWARDS INCOMPATIBILITIES: - commands/docker-run: Started containers are now deleted after run. Specify the new `--no-rm` flag to retain the original behavior. [GH-4327] - providers/virtualbox: Host IO cache is no longer enabled by default since it causes stale file issues. Please enable manually if you require this. [GH-3934] IMPROVEMENTS: - core: Added `config.vm.box_server_url` setting to point at a Vagrant Cloud instance. [GH-4282] - core: File checksumming performance has been improved by at least 100%. Memory requirements have gone down by half. [GH-4090] - commands/docker-run: Add the `--no-rm` flag. Containers are deleted by default. [GH-4327] - commands/plugin: Better error output is shown when plugin installation fails. - commands/reload: show post up messsage [GH-4168] - commands/rsync-auto: Add `--poll` flag. [GH-4392] - communicators/winrm: Show stdout/stderr if command fails. [GH-4094] - guests/nixos: Added better NFS support. [GH-3983] - providers/hyperv: Accept VHD disk format. [GH-4208] - providers/hyperv: Support generation 2 VMs. [GH-4324] - provisioners/docker: More verbose output. [GH-4377] - provisioners/salt: Get proper exit codes to detect failed runs. [GH-4304] BUG FIXES: - core: Downloading box files should resume in more cases since the temporary file is preserved in more cases. [GH-4301] - core: Windows is not detected as NixOS in some cases. [GH-4302] - core: Fix encoding issues with Windows. There are still some outlying but this fixes a few. [GH-4159] - core: Fix crash case when destroying with an invalid provisioner. [GH-4281] - core: Box names with colons work on Windows. [GH-4100] - core: Cleanup all temp files. [GH-4103] - core: User curlrc is not loaded, preventing strange download issues. [GH-4328] - core: VM names may no longer contain brackets, since they cause issues with some providers. [GH-4319] - core: Use "-f" to `rm` files in case pty is true. [GH-4410] - core: SSH key doesn't have to be owned by our user if we're running as root. [GH-4387] - core: "vagrant provision" will cause "vagrant up" to properly not reprovision. [GH-4393] - commands/box/add: "Content-Type" header is now case-insensitive when looking for metadata type. [GH-4369] - commands/docker-run: Named docker containers no longer conflict. [GH-4294] - commands/package: base package won't crash with exception [GH-4017] - commands/rsync-auto: Destroyed machines won't raise exceptions. [GH-4031] - commands/ssh: Extra args are passed through to Docker container. [GH-4378] - communicators/ssh: Nicer error if remote unexpectedly disconnects. [GH-4038] - communicators/ssh: Clean error when max sessions is hit. [GH-4044] - communicators/ssh: Fix many issues around PTY-enabled output parsing. [GH-4408] - communicators/winrm: Support `mkdir` [GH-4271] - communicators/winrm: Properly escape double quotes. [GH-4309] - communicators/winrm: Detect failed commands that aren't CLIs. [GH-4383] - guests/centos: Fix issues when NFS client is installed by restarting NFS [GH-4088] - guests/debian: Deleting default route on DHCP networks can fail. [GH-4262] - guests/fedora: Fix networks on Fedora 20 with libvirt. [GH-4104] - guests/freebsd: Rsync install for rsync synced folders work on FreeBSD 10. [GH-4008] - guests/freebsd: Configure vtnet devices properly [GH-4307] - guests/linux: Show more verbose error when shared folder mount fails. [GH-4403] - guests/redhat: NFS setup should use systemd for RH7+ [GH-4228] - guests/redhat: Detect RHEL 7 (and CentOS) and install Docker properly. [GH-4402] - guests/redhat: Configuring networks on EL7 works. [GH-4195] - guests/redhat: Setting hostname on EL7 works. [GH-4352] - guests/smartos: Use `pfexec` for rsync. [GH-4274] - guests/windows: Reboot after hostname change. [GH-3987] - hosts/arch: NFS works with latest versions. [GH-4224] - hosts/freebsd: NFS exports are proper syntax. [GH-4143] - hosts/gentoo: NFS works with latest versions. [GH-4418] - hosts/windows: RDP command works without crash. [GH-3962] - providers/docker: Port on its own will choose random host port. [GH-3991] - providers/docker: The proxy VM Vagrantfile can be in the same directory as the main Vagrantfile. [GH-4065] - providers/virtualbox: Increase network device limit to 36. [GH-4206] - providers/virtualbox: Error if can't detect VM name. [GH-4047] - provisioners/cfengine: Fix default Yum repo URL. [GH-4335] - provisioners/chef: Chef client cleanup should work. [GH-4099] - provisioners/puppet: Manifest file can be a directory. [GH-4169] - provisioners/puppet: Properly escape facter variables for PowerShell on Windows guests. [GH-3959] - provisioners/puppet: When provisioning fails, don't repeat all of stdout/stderr. [GH-4303] - provisioners/salt: Update salt minion version on Windows. [GH-3932] - provisioners/shell: If args is an array and contains numbers, it no longer crashes. [GH-4234] - provisioners/shell: If fails, the output/stderr isn't repeated again. [GH-4087] ## 1.6.3 (May 29, 2014) FEATURES: - **New Guest:** NixOS - Supports changing host names and setting networks. [GH-3830] IMPROVEMENTS: - core: A CA path can be specified in the Vagrantfile, not just a file, when using a custom CA. [GH-3848] - commands/box/add: `--capath` flag added for custom CA path. [GH-3848] - commands/halt: Halt in reverse order of up, like destroy. [GH-3790] - hosts/linux: Uses rdesktop to RDP into machines if available. [GH-3845] - providers/docker: Support for UDP forwarded ports. [GH-3886] - provisioners/salt: Works on Windows guests. [GH-3825] BUG FIXES: - core: Provider plugins more easily are compatible with global-status and should show less stale data. [GH-3808] - core: When setting a synced folder, it will assume it is not disabled unless explicitly specified. [GH-3783] - core: Ignore UDP forwarded ports for collision detection. [GH-3859] - commands/package: Package with `--base` for VirtualBox doesn't crash. [GH-3827] - guests/solaris11: Fix issue with public network and DHCP on newer Solaris releases. [GH-3874] - guests/windows: Private networks with static IPs work when there is more than one. [GH-3818] - guests/windows: Don't look up a forwarded port for WinRM if we're not accessing the local host. [GH-3861] - guests/windows: Fix errors with arg lists that are too long over WinRM in some cases. [GH-3816] - guests/windows: Powershell exits with proper exit code, fixing - issues where non-zero exit codes weren't properly detected. [GH-3922] - hosts/windows: Don't execute mstsc using PowerShell since it doesn't exit properly. [GH-3837] - hosts/windows: For RDP, don't remove the Tempfile right away. [GH-3875] - providers/docker: Never do graceful shutdown, always use `docker stop`. [GH-3798] - providers/docker: Better error messaging when SSH is not ready direct to container. [GH-3763] - providers/docker: Don't port map SSH port if container doesn't support SSH. [GH-3857] - providers/docker: Proper SSH info if using native driver. [GH-3799] - providers/docker: Verify host VM has SSH ready. [GH-3838] - providers/virtualbox: On Windows, check `VBOX_MSI_INSTALL_PATH` for VBoxManage path as well. [GH-3852] - provisioners/puppet: Fix setting facter vars with Windows guests. [GH-3776] - provisioners/puppet: On Windows, run in elevated prompt. [GH-3903] - guests/darwin: Respect mount options for NFS. [GH-3791] - guests/freebsd: Properly register the rsync_pre capability - guests/windows: Certain executed provisioners won't leave output and exit status behind. [GH-3729] - synced\_folders/rsync: `rsync__chown` can be set to `false` to disable recursive chown after sync. [GH-3810] - synced\_folders/rsync: Use a proper msys path if not in Cygwin. [GH-3804] - synced\_folders/rsync: Don't append args infinitely, clear out arg list on each run. [GH-3864] PLUGIN AUTHOR CHANGES: - Providers can now implement the `rdp_info` provider capability to get proper info for `vagrant rdp` to function. ## 1.6.2 (May 12, 2014) IMPROVEMENTS: - core: Automatically forward WinRM port if communicator is WinRM. [GH-3685] - command/rdp: Args after "--" are passed directly through to the RDP client. [GH-3686] - providers/docker: `build_args` config to specify extra args for `docker build`. [GH-3684] - providers/docker: Can specify options for the build dir synced folder when a host VM is in use. [GH-3727] - synced\_folders/nfs: Can tell Vagrant not to handle exporting by setting `nfs_export: false` [GH-3636] BUG FIXES: - core: Hostnames can be one character. [GH-3713] - core: Don't lock machines on SSH actions. [GH-3664] - core: Fixed crash when adding a box from Vagrant Cloud that was the same name as a real directory. [GH-3732] - core: Parallelization is more stable, doesn't crash due to to bad locks. [GH-3735] - commands/package: Don't double included files in package. [GH-3637] - guests/linux: Rsync chown ignores symlinks. [GH-3744] - provisioners/shell: Fix shell provisioner config validation when the `binary` option is set to false [GH-3712] - providers/docker: default proxy VM won't use HGFS [GH-3687] - providers/docker: fix container linking [GH-3719] - providers/docker: Port settings expose to host properly. [GH-3723] - provisioners/puppet: Separate module paths with ';' on Windows. [GH-3731] - synced\_folders\rsync: Copy symlinks as real files. [GH-3734] - synced\_folders/rsync: Remove non-portable '-v' flag from chown. [GH-3743] ## 1.6.1 (May 7, 2014) IMPROVEMENTS: - **New guest: Linux Mint** is now properly detected. [GH-3648] BUG FIXES: - core: Global control works from directories that don't have a Vagrantfile. - core: Plugins that define config methods that collide with Ruby Kernel/Object - methods are merged properly. [GH-3670] - commands/docker-run: `--help` works. [GH-3698] - commands/package: `--base` works without crashing for VirtualBox. - commands/reload: If `--provision` is specified, force provisioning. [GH-3657] - guests/redhat: Fix networking issues with CentOS. [GH-3649] - guests/windows: Human error if WinRM not in use to configure networks. [GH-3651] - guests/windows: Puppet exit code 2 doesn't cause Windows to raise an error. [GH-3677] - providers/docker: Show proper error message when on Linux. [GH-3654] - providers/docker: Proxy VM works properly even if default provider environmental variable set to "docker" [GH-3662] - providers/docker: Put sync folders in `/var/lib/docker` because it usually has disk space. [GH-3680] - synced\_folders/rsync: Create the directory before syncing. ## 1.6.0 (May 6, 2014) BACKWARDS INCOMPATIBILITIES: - Deprecated: `halt_timeout` and `halt_check_interval` settings for SmartOS, Solaris, and Solaris11 guests. These will be fully removed in 1.7. A warning will be shown if they're in use in 1.6. FEATURES: - **New guest: Windows**. Vagrant now fully supports Windows as a guest VM. WinRM can be used for communication (or SSH), and the shell provisioner, Chef, and Puppet all work with Windows VMs. - **New command: global-status**. This command shows the state of every created Vagrant environment on the system for that logged in user. - **New command: rdp**. This command connects to the running machine via the Remote Desktop Protocol. - **New command: version**. This outputs the currently installed version as well as the latest version of Vagrant available. - **New provider: Docker**. This provider will back your development environments with Docker containers. If you're not on Linux, it will automatically spin up a VM for you on any provider. You can even specify a specific Vagrantfile to use as the Docker container host. - Control Vagrant environments from any directory. Using the UUIDs given in `vagrant global-status`, you can issue commands from anywhere on your machine, not just that environment's directory. Example: `vagrant destroy UUID` from anywhere. - Can now specify a `post_up_message` in your Vagrantfile that is shown after a `vagrant up`. This is useful for putting some instructions of how to use the development environment. - Can configure provisioners to run "once" or "always" (defaults to "once"), so that subsequent `vagrant up` or `reload` calls will always run a provisioner. [GH-2421] - Multi-machine environments can specify an "autostart" option (default to true). `vagrant up` starts all machines that have enabled autostart. - Vagrant is smarter about choosing a default provider. If `VAGRANT_DEFAULT_PROVIDER` is set, it still takes priority, but otherwise Vagrant chooses a "best" provider. IMPROVEMENTS: - core: Vagrant locks machine access to one Vagrant process at a time. This will protect against two simultaneous `up` actions happening on the same environment. - core: Boxes can be compressed with LZMA now as well. - commands/box/remove: Warns if the box appears to be in use by an environment. Can be forced with `--force`. - commands/destroy: Exit codes changes. 0 means everything succeeded. 1 means everything was declined. 2 means some were declined. [GH-811] - commands/destroy: Doesn't require box to exist anymore. [GH-1629] - commands/init: force flag. [GH-3564] - commands/init: flag for minimal Vagrantfile creation (no comments). [GH-3611] - commands/rsync-auto: Picks up and syncs provisioner folders if provisioners are backed by rsync. - commands/rsync-auto: Detects when new synced folders were added and warns user they won't be synced until `vagrant reload`. - commands/ssh-config: Works without a target in multi-machine envs [GH-2844] - guests/freebsd: Support for virtio interfaces. [GH-3082] - guests/openbsd: Support for virtio interfaces. [GH-3082] - guests/redhat: Networking works for upcoming RHEL7 release. [GH-3643] - providers/hyperv: Implement `vagrant ssh -c` support. [GH-3615] - provisioners/ansible: Support for Ansible Vault. [GH-3338] - provisioners/ansible: Show Ansible command executed. [GH-3628] - provisioners/salt: Colorize option. [GH-3603] - provisioners/salt: Ability to specify log level. [GH-3603] - synced\_folders: nfs: Improve sudo commands used to make them sudoers friendly. Examples in docs. [GH-3638] BUG FIXES: - core: Adding a box from a network share on Windows works again. [GH-3279] - commands/plugin/install: Specific versions are now locked in. - commands/plugin/install: If insecure RubyGems.org is specified as a source, use that. [GH-3610] - commands/rsync-auto: Interrupt exits properly. [GH-3552] - commands/rsync-auto: Run properly on Windows. [GH-3547] - communicators/ssh: Detect if `config.ssh.shell` is invalid. [GH-3040] - guests/debian: Can set hostname if hosts doesn't contain an entry already for 127.0.1.1 [GH-3271] - guests/linux: For `read_ip_address` capability, set `LANG=en` so it works on international systems. [GH-3029] - providers/virtualbox: VirtalBox detection works properly again on Windows when the `VBOX_INSTALL_PATH` has multiple elements. [GH-3549] - providers/virtualbox: Forcing MAC address on private network works properly again. [GH-3588] - provisioners/chef-solo: Fix Chef version checking to work with prerelease versions. [GH-3604] - provisioners/salt: Always copy keys and configs on provision. [GH-3536] - provisioners/salt: Install args should always be present with bootstrap. - provisioners/salt: Overwrite keys properly on subsequent provisions [GH-3575] - provisioners/salt: Bootstrap uses raw GitHub URL rather than subdomain. [GH-3583] - synced\_folders/nfs: Acquires a process-level lock so exports don't collide with Vagrant running in parallel. - synced\_folders/nfs: Implement usability check so that hosts that don't support NFS get an error earlier. [GH-3625] - synced\_folders/rsync: Add UserKnownHostsFile option to not complain. [GH-3511] - synced\_folders/rsync: Proxy command is used properly if set. [GH-3553] - synced\_folders/rsync: Owner/group settings are respected. [GH-3544] - synced\_folders/smb: Passwords with symbols work. [GH-3642] PLUGIN AUTHOR CHANGES: - **New host capability:** "rdp\_client". This capability gets the RDP connection info and must launch the RDP client on the system. - core: The "Call" middleware now merges the resulting middleware stack into the current stack, rather than running it as a separate stack. The result is that ordering is preserved. - core: The "Message" middleware now takes a "post" option that will output the message on the return-side of the middleware stack. - core: Forwarded port collision repair works when Vagrant is run in parallel with other Vagrant processes. [GH-2966] - provider: Providers can now specify that boxes are optional. This lets you use the provider without a `config.vm.box`. Useful for providers like AWS or Docker. - provider: A new class-level `usable?` method can be implemented on the provider implementation. This returns or raises an error when the provider is not usable (i.e. VirtualBox isn't installed for VirtualBox) - synced\_folders: New "disable" method for removing synced folders from a running machine. ## 1.5.4 (April 21, 2014) IMPROVEMENTS: - commands/box/list: Doesn't parse Vagrantfile. [GH-3502] - providers/hyperv: Implement the provision command. [GH-3494] BUG FIXES: - core: Allow overriding of the default SSH port. [GH-3474] - commands/box/remove: Make output nicer. [GH-3470] - commands/box/update: Show currently installed version. [GH-3467] - command/rsync-auto: Works properly on Windows. - guests/coreos: Fix test for Docker daemon running. - guests/linux: Fix test for Docker provisioner whether Docker is running. - guests/linux: Fix regression where rsync owner/group stopped working. [GH-3485] - provisioners/docker: Fix issue where we weren't waiting for Docker to properly start before issuing commands. [GH-3482] - provisioners/shell: Better validation of master config path, results in no more stack traces at runtime. [GH-3505] ## 1.5.3 (April 14, 2014) IMPROVEMENTS: - core: 1.5 upgrade code gives users a chance to quit. [GH-3212] - commands/rsync-auto: An initial sync is done before watching folders. [GH-3327] - commands/rsync-auto: Exit immediately if there are no paths to watch. [GH-3446] - provisioners/ansible: custom vars/hosts files can be added in .vagrant/provisioners/ansible/inventory/ directory [GH-3436] BUG FIXES: - core: Randomize some filenames internally to improve the parallelism of Vagrant. [GH-3386] - core: Don't error if network problems cause box update check to fail [GH-3391] - core: `vagrant` on Windows cmd.exe doesn't always exit with exit code zero. [GH-3420] - core: Adding a box from a network share has nice error on Windows. [GH-3279] - core: Setting an ID on a provisioner now works. [GH-3424] - core: All synced folder paths containing symlinks are fully expanded before sharing. [GH-3444] - core: Windows no longer sees "process not started" errors rarely. - commands/box/repackage: Works again. [GH-3372] - commands/box/update: Update should check for updates from latest version. [GH-3452] - commands/package: Nice error if includes contain symlinks. [GH-3200] - commands/rsync-auto: Don't crash if the machine can't be communicated to. [GH-3419] - communicators/ssh: Throttle connection attempt warnings if the warnings are the same. [GH-3442] - guests/coreos: Docker provisioner works. [GH-3425] - guests/fedora: Fix hostname setting. [GH-3382] - guests/fedora: Support predictable network interface names for public/private networks. [GH-3207] - guests/linux: Rsync folders have proper group if owner not set. [GH-3223] - guests/linux: If SMB folder mounting fails, the password will no longer be shown in plaintext in the output. [GH-3203] - guests/linux: SMB mount works with passwords with symbols. [GH-3202] - providers/hyperv: Check for PowerShell features. [GH-3398] - provisioners/docker: Don't automatically generate container name with a forward slash. [GH-3216] - provisioners/shell: Empty shell scripts don't cause errors. [GH-3423] - synced\_folders/smb: Only set the chmod properly by default on Windows if it isn't already set. [GH-3394] - synced\_folders/smb: Sharing folders with odd characters like parens works properly now. [GH-3405] ## 1.5.2 (April 2, 2014) IMPROVEMENTS: - **New guest:** SmartOS - core: Change wording from "error" to "warning" on SSH retry output to convey actual meaning. - commands/plugin: Listing plugins now has machine-readable output. [GH-3293] - guests/omnios: Mount NFS capability [GH-3282] - synced\_folders/smb: Verify PowerShell v3 or later is running. [GH-3257] BUG FIXES: - core: Vagrant won't collide with newer versions of Bundler [GH-3193] - core: Allow provisioner plugins to not have a config class. [GH-3272] - core: Removing a specific box version that doesn't exist doesn't crash Vagrant. [GH-3364] - core: SSH commands are forced to be ASCII. - core: private networks with DHCP type work if type parameter is a string and not a symbol. [GH-3349] - core: Converting to cygwin path works for folders with spaces. [GH-3304] - core: Can add boxes with spaces in their path. [GH-3306] - core: Prerelease plugins installed are locked to that prerelease version. [GH-3301] - core: Better error message when adding a box with a malformed version. [GH-3332] - core: Fix a rare issue where vagrant up would complain it couldn't check version of a box that doesn't exist. [GH-3326] - core: Box version constraint can't be specified with old-style box. [GH-3260] - commands/box: Show versions when listing. [GH-3316] - commands/box: Outdated check can list local boxes that are newer. [GH-3321] - commands/status: Machine readable output contains the target. [GH-3218] - guests/arch: Reload udev rules after network change. [GH-3322] - guests/debian: Changing host name works properly. [GH-3283] - guests/suse: Shutdown works correctly on SLES [GH-2775] - hosts/linux: Don't hardcode `exportfs` path. Now searches the PATH. [GH-3292] - providers/hyperv: Resume command works properly. [GH-3336] - providers/virtualbox: Add missing translation for stopping status. [GH-3368] - providers/virtualbox: Host-only networks set cableconnected property to "yes" [GH-3365] - provisioners/docker: Use proper flags for 0.9. [GH-3356] - synced\_folders/rsync: Set chmod flag by default on Windows. [GH-3256] - synced\_folders/smb: IDs of synced folders are hashed to work better with VMware. [GH-3219] - synced\_folders/smb: Properly remove existing folders with the same name. [GH-3354] - synced\_folders/smb: Passwords with symbols now work. [GH-3242] - synced\_folders/smb: Exporting works for non-english locale Windows machines. [GH-3251] ## 1.5.1 (March 13, 2014) IMPROVEMENTS: - guests/tinycore: Will now auto-install rsync. - synced\_folders/rsync: rsync-auto will not watch filesystem for excluded paths. [GH-3159] BUG FIXES: - core: V1 Vagrantfiles can upgrade provisioners properly. [GH-3092] - core: Rare EINVAL errors on box adding are gone. [GH-3094] - core: Upgrading the home directory for Vagrant 1.5 uses the Vagrant temp dir. [GH-3095] - core: Assume a box isn't metadata if it exceeds 20 MB. [GH-3107] - core: Asking for input works even in consoles that don't support hiding input. [GH-3119] - core: Adding a box by path in Cygwin on Windos works. [GH-3132] - core: PowerShell scripts work when they're in a directory with spaces. [GH-3100] - core: If you add a box path that doesn't exist, error earlier. [GH-3091] - core: Validation on forwarded ports to make sure they're between 0 and 65535. [GH-3187] - core: Downloads with user/password use the curl `-u` flag. [GH-3183] - core: `vagrant help` no longer loads the Vagrantfile. [GH-3180] - guests/darwin: Fix an exception when configuring networks. [GH-3143] - guests/linux: Only chown folders/files in rsync if they don't have the proper owner. [GH-3186] - hosts/linux: Unusual sed delimiter to avoid conflicts. [GH-3167] - providers/virtualbox: Make more internal interactions with VBoxManage retryable to avoid spurious VirtualBox errors. [GH-2831] - providers/virtualbox: Import progress works again on Windows. - provisioners/ansible: Request SSH info within the provision method, when we know its available. [GH-3111] - synced\_folders/rsync: owner/group settings work. [GH-3163] ## 1.5.0 (March 10, 2014) BREAKING CHANGES: - provisioners/ansible: the machine name (taken from Vagrantfile) is now set as default limit to ensure that vagrant provision steps only affect the expected machine. DEPRECATIONS: - provisioners/chef-solo: The "nfs" setting has been replaced by `synced_folder_type`. The "nfs" setting will be removed in the next version. - provisioners/puppet: The "nfs" setting has been replaced by `synced_folder_type`. The "nfs" setting will be removed in the next version. FEATURES: - **New provider:** Hyper-V. If you're on a Windows machine with Hyper-V enabled, Vagrant can now manage Hyper-V virtual machines out of the box. - **New guest:** Funtoo (change host name and networks supported) - **New guest:** NetBSD - **New guest:** TinyCore Linux. This allows features such as networking, halting, rsync and more work with Boot2Docker. - **New synced folder type:** rsync - Does a one-time one-directional sync to the guest machine. New commands `vagrant rsync` and `vagrant rsync-auto` can resync the folders. - **New synced folder type:** SMB- Allows bi-directional folder syncing using SMB on Windows hosts with any guest. - Password-based SSH authentication. This lets you use almost any off-the-shelf virtual machine image with Vagrant. Additionally, Vagrant will automatically insert a keypair into the machine. - Plugin versions can now be constrained to a range of versions. Example: `vagrant plugin install foo --plugin-version "> 1.0, < 1.1"` - Host-specific operations now use a "host capabilities" system much like guests have used "guest capabilities" for a few releases now. This allows plugin developers to create pluggable host-specific capabilities and makes further integrating Vagrant with new operating systems even easier. - You can now override provisioners within sub-VM configuration and provider overrides. See documentation for more info. [GH-1113] - providers/virtualbox: Provider-specific configuration `cpus` can be used to set the number of CPUs on the VM [GH-2800] - provisioners/docker: Can now build images using `docker build`. [GH-2615] IMPROVEMENTS: - core: Added "error-exit" type to machine-readable output which contains error information that caused a non-zero exit status. [GH-2999] - command/destroy: confirmation will re-ask question if bad input. [GH-3027] - guests/solaris: More accurate Solaris >= 11, < 11 detection. [GH-2824] - provisioners/ansible: Generates a single inventory file, rather than one per machine. See docs for more info. [GH-2991] - provisioners/ansible: SSH forwarding support. [GH-2952] - provisioners/ansible: Multiple SSH keys can now be attempted [GH-2952] - provisioners/ansible: Disable SSH host key checking by default, which improves the experience. We believe this is a sane default for ephemeral dev machines. - provisioners/chef-solo: New config `synced_folder_type` replaces the `nfs` option. This can be used to set the synced folders the provisioner needs to any type. [GH-2709] - provisioners/chef-solo: `roles_paths` can now be an array of paths in Chef 11.8.0 and newer. [GH-2975] - provisioners/docker: Can start a container without daemonization. - provisioners/docker: Started containers are given names. [GH-3051] - provisioners/puppet: New config `synced_folder_type` replaces the `nfs` option. This can be used to set the synced folders the provisioner needs to any type. [GH-2709] - commands/plugin: `vagrant plugin update` will now update all installed plugins, respecting any constraints set. - commands/plugin: `vagrant plugin uninstall` can now uninstall multiple plugins. - commands/plugin: `vagrant plugin install` can now install multiple plugins. - hosts/redhat: Recognize Korora OS. [GH-2869] - synced\_folders/nfs: If the guest supports it, NFS clients will be automatically installed in the guest. BUG FIXES: - core: If an exception was raised while attempting to connect to SSH for the first time, it would get swallowed. It is properly raised now. - core: Plugin installation does not fail if your local gemrc file has syntax errors. - core: Plugins that fork within certain actions will no longer hang indefinitely. [GH-2756] - core: Windows checks home directory permissions more correctly to warn of potential issues. - core: Synced folders set to the default synced folder explicitly won't be deleted. [GH-2873] - core: Static IPs can end in ".1". A warning is now shown. [GH-2914] - core: Adding boxes that have directories in them works on Windows. - core: Vagrant will not think provisioning is already done if the VM is manually deleted outside of Vagrant. - core: Box file checksums of large files works properly on Windows. [GH-3045] - commands/box: Box add `--force` works with `--provider` flag. [GH-2757] - commands/box: Listing boxes with machine-readable output crash is gone. - commands/plugin: Plugin installation will fail if dependencies conflict, rather than at runtime. - commands/ssh: When using `-c` on Windows, no more TTY errors. - commands/ssh-config: ProxyCommand is included in output if it is set. [GH-2950] - guests/coreos: Restart etcd after configuring networks. [GH-2852] - guests/linux: Don't chown VirtualBox synced folders if mounting as readonly. [GH-2442] - guests/redhat: Set hostname to FQDN, per the documentation for RedHat. [GH-2792] - hosts/bsd: Don't invoke shell for NFS sudo calls. [GH-2808] - hosts/bsd: Sort NFS exports to avoid false validation errors. [GH-2927] - hosts/bsd: No more checkexports NFS errors if you're sharing the same directory. [GH-3023] - hosts/gentoo: Look for systemctl in `/usr/bin` [GH-2858] - hosts/linux: Properly escape regular expression to prune NFS exports, allowing VMware to work properly. [GH-2934] - hosts/opensuse: Start NFS server properly. [GH-2923] - providers/virtualbox: Enabling internal networks by just setting "true" works properly. [GH-2751] - providers/virtualbox: Make more internal interactions with VBoxManage retryable to avoid spurious VirtualBox errors. [GH-2831] - providers/virtualbox: Config validation catches invalid keys. [GH-2843] - providers/virtualbox: Fix network adapter configuration issue if using provider-specific config. [GH-2854] - providers/virtualbox: Bridge network adapters always have their "cable connected" properly. [GH-2906] - provisioners/chef: When chowning folders, don't follow symlinks. - provisioners/chef: Encrypted data bag secrets also in Chef solo are now uploaded to the provisioning path to avoid perm issues. [GH-2845] - provisioners/chef: Encrypted data bag secret is removed from the machine before and after provisioning also with Chef client. [GH-2845] - provisioners/chef: Set `encrypted_data_bag_secret` on the VM to `nil` if the secret is not specified. [GH-2984] - provisioners/chef: Fix loading of the custom configure file. [GH-876] - provisioners/docker: Only add SSH user to docker group if the user isn't already in it. [GH-2838] - provisioners/docker: Configuring autostart works properly with the newest versions of Docker. [GH-2874] - provisioners/puppet: Append default module path to the module paths always. [GH-2677] - provisioners/salt: Setting pillar data doesn't require `deep_merge` plugin anymore. [GH-2348] - provisioners/salt: Options can now set install type and install args. [GH-2766] - provisioners/salt: Fix case when salt would say "options only allowed before install arguments" [GH-3005] - provisioners/shell: Error if script is encoded incorrectly. [GH-3000] - synced\_folders/nfs: NFS entries are pruned on every `vagrant up`, if there are any to prune. [GH-2738] ## 1.4.3 (January 2, 2014) BUG FIXES: - providers/virtualbox: `vagrant package` works properly again. [GH-2739] ## 1.4.2 (December 31, 2013) IMPROVEMENTS: - guests/linux: emit upstart event when NFS folders are mounted. [GH-2705] - provisioners/chef-solo: Encrypted data bag secret is removed from the machine after provisioning. [GH-2712] BUG FIXES: - core: Ctrl-C no longer raises "trap context" exception. - core: The version for `Vagrant.configure` can now be an int. [GH-2689] - core: `Vagrant.has_plugin?` tries to use plugin's gem name before registered plugin name [GH-2617] - core: Fix exception if an EOFError was somehow raised by Ruby while checking a box checksum. [GH-2716] - core: Better error message if your plugin state file becomes corrupt somehow. [GH-2694] - core: Box add will fail early if the box already exists. [GH-2621] - hosts/bsd: Only run `nfsd checkexports` if there is an exports file. [GH-2714] - commands/plugin: Fix exception that could happen rarely when installing a plugin. - providers/virtualbox: Error when packaging if the package already exists _before_ the export is done. [GH-2380] - providers/virtualbox: NFS with static IP works even if VirtualBox guest additions aren't installed (regression). [GH-2674] - synced\_folders/nfs: sudo will only ask for password one at a time when using a parallel provider [GH-2680] ## 1.4.1 (December 18, 2013) IMPROVEMENTS: - hosts/bsd: check NFS exports file for issues prior to exporting - provisioners/ansible: Add ability to use Ansible groups in generated inventory [GH-2606] - provisioners/docker: Add support for using the provisioner with RedHat based guests [GH-2649] - provisioners/docker: Remove "Docker" prefix from Client and Installer classes [GH-2641] BUG FIXES: - core: box removal of a V1 box works - core: `vagrant ssh -c` commands are now executed in the context of a login shell (regression). [GH-2636] - core: specifying `-t` or `-T` to `vagrant ssh -c` as extra args will properly enable/disable a TTY for OpenSSH. [GH-2618] - commands/init: Error if can't write Vagrantfile to directory. [GH-2660] - guests/debian: fix `use_dhcp_assigned_default_route` to work properly. [GH-2648] - guests/debian,ubuntu: fix change\_host\_name for FQDNs with trailing dots [GH-2610] - guests/freebsd: configuring networks in the guest works properly [GH-2620] - guests/redhat: fix configure networks bringing down interfaces that don't exist. [GH-2614] - providers/virtualbox: Don't override NFS exports for all VMs when coming up. [GH-2645] - provisioners/ansible: Array arguments work for raw options [GH-2667] - provisioners/chef-client: Fix node/client deletion when node\_name is not set. [GH-2345] - provisioners/chef-solo: Force remove files to avoid cases where a prompt would be shown to users. [GH-2669] - provisioners/puppet: Don't prepend default module path for Puppet in case Puppet is managing its own paths. [GH-2677] ## 1.4.0 (December 9, 2013) FEATURES: - New provisioner: Docker. Install Docker, pull containers, and run containers easier than ever. - Machine readable output. Vagrant now has machine-friendly output by using the `--machine-readable` flag. - New plugin type: synced folder implementation. This allows new ways of syncing folders to be added as plugins to Vagrant. - The `Vagrant.require_version` function can be used at the top of a Vagrantfile to enforce a minimum/maximum Vagrant version. - Adding boxes via `vagrant box add` and the Vagrantfile both support providing checksums of the box files. - The `--debug` flag can be specified on any command now to get debug-level log output to ease reporting bugs. - You can now specify a memory using `vb.memory` setting with VirtualBox. - Plugin developers can now hook into `environment_plugins_loaded`, which is executed after plugins are loaded but before Vagrantfiles are parsed. - VirtualBox internal networks are now supported. [GH-2020] IMPROVEMENTS: - core: Support resumable downloads [GH-57] - core: owner/group of shared folders can be specified by integers. [GH-2390] - core: the VAGRANT\_NO\_COLOR environmental variable may be used to enable `--no-color` mode globally. [GH-2261] - core: box URL and add date is tracked and shown if `-i` flag is specified for `vagrant box list` [GH-2327] - core: Multiple SSH keys can be specified with `config.ssh.private_key_path` [GH-907] - core: `config.vm.box_url` can be an array of URLs. [GH-1958] - commands/box/add: Can now specify a custom CA cert for verifying certs from a custom CA. [GH-2337] - commands/box/add: Can now specify a client cert when downloading a box. [GH-1889] - commands/init: Add `--output` option for specifying output path, or "-" for stdin. [GH-1364] - commands/provision: Add `--no-parallel` option to disable provider parallelization if the provider supports it. [GH-2404] - commands/ssh: SSH compression is enabled by default. [GH-2456] - commands/ssh: Inline commands specified with "-c" are now executed using OpenSSH rather than pure-Ruby SSH. It is MUCH faster, and stdin works! - communicators/ssh: new configuration `config.ssh.pty` is a boolean for whether you want ot use a PTY for provisioning. - guests/linux: emit upstart event `vagrant-mounted` if upstart is available. [GH-2502] - guests/pld: support changing hostname [GH-2543] - providers/virtualbox: Enable symlinks for VirtualBox 4.1. [GH-2414] - providers/virtualbox: default VM name now includes milliseconds with a random number to try to avoid conflicts in CI environments. [GH-2482] - providers/virtualbox: customizations via VBoxManage are retried, avoiding VirtualBox flakiness [GH-2483] - providers/virtualbox: NFS works with DHCP host-only networks now. [GH-2560] - provisioners/ansible: allow files for extra vars [GH-2366] - provisioners/puppet: client cert and private key can now be specified for the puppet server provisioner. [GH-902] - provisioners/puppet: the manifests path can be in the VM. [GH-1805] - provisioners/shell: Added `keep_color` option to not automatically color output based on stdout/stderr. [GH-2505] - provisioners/shell: Arguments can now be an array of args. [GH-1949] - synced\_folders/nfs: Specify `nfs_udp` to false to disable UDP based NFS folders. [GH-2304] BUG FIXES: - core: Make sure machine IDs are always strings. [GH-2434] - core: 100% CPU spike when waiting for SSH is fixed. [GH-2401] - core: Command lookup works on systems where PATH is not valid UTF-8 [GH-2514] - core: Human-friendly error if box metadata.json becomes corrupted. [GH-2305] - core: Don't load Vagrantfile on `vagrant plugin` commands, allowing Vagrantfiles that use plugins to work. [GH-2388] - core: global flags are ignored past the "--" on the CLI. [GH-2491] - core: provisoining will properly happen if `up` failed. [GH-2488] - guests/freebsd: Mounting NFS folders works. [GH-2400] - guests/freebsd: Uses `sh` by default for shell. [GH-2485] - guests/linux: upstart events listening for `vagrant-mounted` won't wait for jobs to complete, fixing issues with blocking during vagrant up [GH-2564] - guests/redhat: `DHCP_HOSTNAME` is set to the hostname, not the FQDN. [GH-2441] - guests/redhat: Down interface before messing up configuration file for networking. [GH-1577] - guests/ubuntu: "localhost" is preserved when changing hostnames. [GH-2383] - hosts/bsd: Don't set mapall if maproot is set in NFS. [GH-2448] - hosts/gentoo: Support systemd for NFS startup. [GH-2382] - providers/virtualbox: Don't start new VM if VirtualBox has transient failure during `up` from suspended. [GH-2479] - provisioners/chef: Chef client encrypted data bag secrets are now uploaded to the provisioning path to avoid perm issues. [GH-1246] - provisioners/chef: Create/chown the cache and backup folders. [GH-2281] - provisioners/chef: Verify environment paths exist in config validation step. [GH-2381] - provisioners/puppet: Multiple puppet definitions in a Vagrantfile work correctly. - provisioners/salt: Bootstrap on FreeBSD systems work. [GH-2525] - provisioners/salt: Extra args for bootstrap are put in the proper location. [GH-2558] ## 1.3.5 (October 15, 2013) FEATURES: - VirtualBox 4.3 is now supported. [GH-2374] - ESXi is now a supported guest OS. [GH-2347] IMPROVEMENTS: - guests/redhat: Oracle Linux is now supported. [GH-2329] - provisioners/salt: Support running overstate. [GH-2313] BUG FIXES: - core: Fix some places where "no error message" errors were being reported when in fact there were errors. [GH-2328] - core: Disallow hyphens or periods for starting hostnames. [GH-2358] - guests/ubuntu: Setting hostname works properly. [GH-2334] - providers/virtualbox: Retryable VBoxManage commands are properly retried. [GH-2365] - provisioners/ansible: Verbosity won't be blank by default. [GH-2320] - provisioners/chef: Fix exception raised during Chef client node cleanup. [GH-2345] - provisioners/salt: Correct master seed file name. [GH-2359] ## 1.3.4 (October 2, 2013) FEATURES: - provisioners/shell: Specify the `binary` option as true and Vagrant won't automatically replace Windows line endings with Unix ones. [GH-2235] IMPROVEMENTS: - guests/suse: Support installing CFEngine. [GH-2273] BUG FIXES: - core: Don't output `\e[0K` anymore on Windows. [GH-2246] - core: Only modify `DYLD_LIBRARY_PATH` on Mac when executing commands in the installer context. [GH-2231] - core: Clear `DYLD_LIBRARY_PATH` on Mac if the subprocess is executing a setuid or setgid script. [GH-2243] - core: Defined action hook names can be strings now. They are converted to symbols internally. - guests/debian: FQDN is properly set when setting the hostname. [GH-2254] - guests/linux: Fix poor chown command for mounting VirtualBox folders. - guests/linux: Don't raise exception right away if mounting fails, allow retries. [GH-2234] - guests/redhat: Changing hostname changes DHCP_HOSTNAME. [GH-2267] - hosts/arch: Vagrant won't crash on Arch anymore. [GH-2233] - provisioners/ansible: Extra vars are converted to strings. [GH-2244] - provisioners/ansible: Output will show up on a task-by-task basis. [GH-2194] - provisioners/chef: Propagate disabling color if Vagrant has no color enabled. [GH-2246] - provisioners/chef: Delete from chef server exception fixed. [GH-2300] - provisioners/puppet: Work with restrictive umask. [GH-2241] - provisioners/salt: Remove bootstrap definition file on each run in order to avoid permissions issues. [GH-2290] ## 1.3.3 (September 18, 2013) BUG FIXES: - core: Fix issues with dynamic linker not finding symbols on OS X. [GH-2219] - core: Properly clean up machine directories on destroy. [GH-2223] - core: Add a timeout to waiting for SSH connection and server headers on SSH. [GH-2226] ## 1.3.2 (September 17, 2013) IMPROVEMENTS: - provisioners/ansible: Support more verbosity levels, better documentation. [GH-2153] - provisioners/ansible: Add `host_key_checking` configuration. [GH-2203] BUG FIXES: - core: Report the proper invalid state when waiting for the guest machine to be ready - core: `Guest#capability?` now works with strings as well - core: Fix NoMethodError in the new `Vagrant.has_plugin?` method [GH-2189] - core: Convert forwarded port parameters to integers. [GH-2173] - core: Don't spike CPU to 100% while waiting for machine to boot. [GH-2163] - core: Increase timeout for individual SSH connection to 60 seconds. [GH-2163] - core: Call realpath after creating directory so NFS directory creation works. [GH-2196] - core: Don't try to be clever about deleting the machine state directory anymore. Manually done in destroy actions. [GH-2201] - core: Find the root Vagrantfile only if Vagrantfile is a file, not a directory. [GH-2216] - guests/linux: Try `id -g` in addition to `getent` for mounting VirtualBox shared folders [GH-2197] - hosts/arch: NFS exporting works properly, no exceptions. [GH-2161] - hosts/bsd: Use only `sudo` for writing NFS exports. This lets NFS exports work if you have sudo privs but not `su`. [GH-2191] - hosts/fedora: Fix host detection encoding issues. [GH-1977] - hosts/linux: Fix NFS export problems with `no_subtree_check`. [GH-2156] - installer/mac: Vagrant works properly when a library conflicts from homebrew. [GH-2188] - installer/mac: deb/rpm packages now have an epoch of 1 so that new installers don't appear older. [GH-2179] - provisioners/ansible: Default output level is now verbose again. [GH-2194] - providers/virtualbox: Fix an issue where destroy middlewares weren't being properly called. [GH-2200] ## 1.3.1 (September 6, 2013) BUG FIXES: - core: Fix various issues where using the same options hash in a Vagrantfile can cause errors. - core: `VAGRANT_VAGRANTFILE` env var only applies to the project Vagrantfile name. [GH-2130] - core: Fix an issue where the data directory would be deleted too quickly in a multi-VM environment. - core: Handle the case where we get an EACCES cleaning up the .vagrant directory. - core: Fix exception on upgrade warnings from V1 to V2. [GH-2142] - guests/coreos: Proper IP detection. [GH-2146] - hosts/linux: NFS exporting works properly again. [GH-2137] - provisioners/chef: Work even with restrictive umask on user. [GH-2121] - provisioners/chef: Fix environment validation to be less restrictive. - provisioners/puppet: No more "shared folders cannot be found" error. [GH-2134] - provisioners/puppet: Work with restrictive umask on user by testing for folders with sudo. [GH-2121] ## 1.3.0 (September 5, 2013) BACKWARDS INCOMPATIBILITY: - `config.ssh.max_tries` is gone. Instead of maximum tries, Vagrant now uses a simple overall timeout value `config.vm.boot_timeout` to wait for the machine to boot up. - `config.vm.graceful_halt_retry_*` settings are gone. Instead, a single timeout is now used to wait for a graceful halt to work, specified by `config.vm.graceful_halt_timeout`. - The ':extra' flag to shared folders for specifying arbitrary mount options has been replaced with the `:mount_options` flag, which is now an array of mount options. - `vagrant up` will now only run provisioning by default the first time it is run. Subsequent `reload` or `up` will need to explicitly specify the `--provision` flag to provision. [GH-1776] FEATURES: - New command: `vagrant plugin update` to update specific installed plugins. - New provisioner: File provisioner. [GH-2112] - New provisioner: Salt provisioner. [GH-1626] - New guest: Mac OS X guest support. [GH-1914] - New guest: CoreOS guest support. Change host names and configure networks on CoreOS. [GH-2022] - New guest: Solaris 11 guest support. [GH-2052] - Support for environments in the Chef-solo provisioner. [GH-1915] - Provisioners can now define "cleanup" tasks that are executed on `vagrant destroy`. [GH-1302] - Chef Client provisioner will now clean up the node/client using `knife` if configured to do so. - `vagrant up` has a `--no-destroy-on-error` flag that will not destroy the VM if a fatal error occurs. [GH-2011] - NFS: Arbitrary mount options can be specified using the `mount_options` option on synced folders. [GH-1029] - NFS: Arbitrary export options can be specified using `bsd__nfs_options` and `linux__nfs_options`. [GH-1029] - Static IP can now be set on public networks. [GH-1745] - Add `Vagrant.has_plugin?` method for use in Vagrantfile to check if a plugin is installed. [GH-1736] - Support for remote shell provisioning scripts [GH-1787] IMPROVEMENTS: - core: add `--color` to any Vagrant command to FORCE color output. [GH-2027] - core: "config.vm.host_name" works again, just an alias to hostname. - core: Reboots via SSH are now handled gracefully (without exception). - core: Mark `disabled` as true on forwarded port to disable. [GH-1922] - core: NFS exports are now namespaced by user ID, so pruning NFS won't remove exports from other users. [GH-1511] - core: "vagrant -v" no longer loads the Vagrantfile - commands/box/remove: Fix stack trace that happens if no provider is specified. [GH-2100] - commands/plugin/install: Post install message of a plugin will be shown if available. [GH-1986] - commands/status: cosmetic improvement to better align names and statuses [GH-2016] - communicators/ssh: Support a proxy_command. [GH-1537] - guests/openbsd: support configuring networks, changing host name, and mounting NFS. [GH-2086] - guests/suse: Supports private/public networks. [GH-1689] - hosts/fedora: Support RHEL as a host. [GH-2088] - providers/virtualbox: "post-boot" customizations will run directly after boot, and before waiting for SSH. [GH-2048] - provisioners/ansible: Many more configuration options. [GH-1697] - provisioners/ansible: Ansible `inventory_path` can be a directory now. [GH-2035] - provisioners/ansible: Extra verbose option by setting `config.verbose` to `extra`. [GH-1979] - provisioners/ansible: `inventory_path` will be auto-generated if not specified. [GH-1907] - provisioners/puppet: Add `nfs` option to puppet provisioner. [GH-1308] - provisioners/shell: Set the `privileged` option to false to run without sudo. [GH-1370] BUG FIXES: - core: Clean up ".vagrant" folder more effectively. - core: strip newlines off of ID file values [GH-2024] - core: Multiple forwarded ports with different protocols but the same host port can be specified. [GH-2059] - core: `:nic_type` option for private networks is respected. [GH-1704] - commands/up: provision-with validates the provisioners given. [GH-1957] - guests/arch: use systemd way of setting host names. [GH-2041] - guests/debian: Force bring up eth0. Fixes hangs on setting hostname. [GH-2026] - guests/ubuntu: upstart events are properly emitted again. [GH-1717] - hosts/bsd: Nicer error if can't read NFS exports. [GH-2038] - hosts/fedora: properly detect later CentOS versions. [GH-2008] - providers/virtualbox: VirtualBox 4.2 now supports up to 36 network adapters. [GH-1886] - provisioners/ansible: Execute ansible with a cwd equal to the path where the Vagrantfile is. [GH-2051] - provisioners/all: invalid config keys will be properly reported. [GH-2117] - provisioners/ansible: No longer report failure on every run. [GH-2007] - provisioners/ansible: Properly handle extra vars with spaces. [GH-1984] - provisioners/chef: Formatter option works properly. [GH-2058] - provisioners/chef: Create/chown the provisioning folder before reading contents. [GH-2121] - provisioners/puppet: mount synced folders as root to avoid weirdness - provisioners/puppet: Run from the correct working directory. [GH-1967] with Puppet. [GH-2015] - providers/virtualbox: Use `getent` to get the group ID instead of `id` in case the name doesn't have a user. [GH-1801] - providers/virtualbox: Will only set the default name of the VM on initial `up`. [GH-1817] ## 1.2.7 (July 28, 2013) BUG FIXES: - On Windows, properly convert synced folder host path to a string so that separator replacement works properly. - Use `--color=false` for no color in Puppet to support older versions properly. [GH-2000] - Make sure the hostname configuration is a string. [GH-1999] - cURL downloads now contain a user agent which fixes some issues with downloading Vagrant through proxies. [GH-2003] - `vagrant plugin install` will now always properly show the actual installed gem name. [GH-1834] ## 1.2.6 (July 26, 2013) BUG FIXES: - Box collections with multiple formats work properly by converting the supported formats to symbols. [GH-1990] ## 1.2.5 (July 26, 2013) FEATURES: - `vagrant help ` now works. [GH-1578] - Added `config.vm.box_download_insecure` to allow the box_url setting to point to an https site that won't be validated. [GH-1712] - VirtualBox VBoxManage customizations can now be specified to run pre-boot (the default and existing functionality, pre-import, or post-boot. [GH-1247] - VirtualBox no longer destroys unused network interfaces by default. This didn't work across multi-user systems and required admin privileges on Windows, so it has been disabled by default. It can be enabled using the VirtualBox provider-specific `destroy_unused_network_interfaces` configuration by setting it to true. [GH-1324] IMPROVEMENTS: - Remote commands that fail will now show the stdout/stderr of the command that failed. [GH-1203] - Puppet will run without color if the UI is not colored. [GH-1344] - Chef supports the "formatter" configuration for setting the formatter. [GH-1250] - VAGRANT_DOTFILE_PATH environmental variable reintroduces the functionality removed in 1.1 from "config.dotfile_name" [GH-1524] - Vagrant will show an error if VirtualBox 4.2.14 is running. - Added provider to BoxNotFound error message. [GH-1692] - If Ansible fails to run properly, show an error message. [GH-1699] - Adding a box with the `--provider` flag will now allow a box for any of that provider's supported formats. - NFS mounts enable UDP by default, resulting in higher performance. (Because mount is over local network, packet loss is not an issue) [GH-1706] BUG FIXES: - `box_url` now handles the case where the provider doesn't perfectly match the provider in use, but the provider supports it. [GH-1752] - Fix uninitialized constant error when configuring Arch Linux network. [GH-1734] - Debian/Ubuntu change hostname works properly if eth0 is configured with hot-plugging. [GH-1929] - NFS exports with improper casing on Mac OS X work properly. [GH-1202] - Shared folders overriding '/vagrant' in multi-VM environments no longer all just use the last value. [GH-1935] - NFS export fsid's are now 32-bit integers, rather than UUIDs. This lets NFS exports work with Linux kernels older than 2.6.20. [GH-1127] - NFS export allows access from all private networks on the VM. [GH-1204] - Default VirtualBox VM name now contains the machine name as defined in the Vagrantfile, helping differentiate multi-VM. [GH-1281] - NFS works properly on CentOS hosts. [GH-1394] - Solaris guests actually shut down properly. [GH-1506] - All provisioners only output newlines when the provisioner sends a newline. This results in the output looking a lot nicer. - Sharing folders works properly if ".profile" contains an echo. [GH-1677] - `vagrant ssh-config` IdentityFile is only wrapped in quotes if it contains a space. [GH-1682] - Shared folder target path can be a Windows path. [GH-1688] - Forwarded ports don't auto-correct by default, and will raise an error properly if they collide. [GH-1701] - Retry SSH on ENETUNREACH error. [GH-1732] - NFS is silently ignored on Windows. [GH-1748] - Validation so that private network static IP does not end in ".1" [GH-1750] - With forward agent enabled and sudo being used, Vagrant will automatically discover and set `SSH_AUTH_SOCK` remotely so that forward agent works properly despite misconfigured sudoers. [GH-1307] - Synced folder paths on Windows containing '\' are replaced with '/' internally so that they work properly. - Unused config objects are finalized properly. [GH-1877] - Private networks work with Fedora guests once again. [GH-1738] - Default internal encoding of strings in Vagrant is now UTF-8, allowing detection of Fedora to work again (which contained a UTF-8 string). [GH-1977] ## 1.2.4 (July 16, 2013) FEATURES: - Chef solo and client provisioning now support a `custom_config_path` setting that accepts a path to a Ruby file to load as part of Chef configuration, allowing you to override any setting available. [GH-876] - CFEngine provisioner: you can now specify the package name to install, so CFEngine enterprise is supported. [GH-1920] IMPROVEMENTS: - `vagrant box remove` works with only the name of the box if that box exists only backed by one provider. [GH-1032] - `vagrant destroy` returns exit status 1 if any of the confirmations are declined. [GH-923] - Forwarded ports can specify a host IP and guest IP to bind to. [GH-1121] - You can now set the "ip" of a private network that uses DHCP. This will change the subnet and such that the DHCP server uses. - Add `file_cache_path` support for chef_solo. [GH-1897] BUG FIXES: - VBoxManage or any other executable missing from PATH properly reported. Regression from 1.2.2. [GH-1928] - Boxes downloaded as part of `vagrant up` are now done so _prior_ to config validation. This allows Vagrantfiles to references files that may be in the box itself. [GH-1061] - Chef removes dna.json and encrypted data bag secret file prior to uploading. [GH-1111] - NFS synced folders exporting sub-directories of other exported folders now works properly. [GH-785] - NFS shared folders properly dereference symlinks so that the real path is used, avoiding mount errors [GH-1101] - SSH channel is closed after the exit status is received, potentially eliminating any SSH hangs. [GH-603] - Fix regression where VirtualBox detection wasn't working anymore. [GH-1918] - NFS shared folders with single quotes in their name now work properly. [GH-1166] - Debian/Ubuntu request DHCP renewal when hostname changes, which will fix issues with FQDN detecting. [GH-1929] - SSH adds the "DSAAuthentication=yes" option in case that is disabled on the user's system. [GH-1900] ## 1.2.3 (July 9, 2013) FEATURES: - Puppet provisioner now supports Hiera by specifying a `hiera_config_path`. - Added a `working_directory` configuration option to the Puppet apply provisioner so you can specify the working directory when `puppet` is called, making it friendly to Hiera data and such. [GH-1670] - Ability to specify the host IP to bind forwarded ports to. [GH-1785] IMPROVEMENTS: - Setting hostnames works properly on OmniOS. [GH-1672] - Better VBoxManage error detection on Windows systems. This avoids some major issues where Vagrant would sometimes "lose" your VM. [GH-1669] - Better detection of missing VirtualBox kernel drivers on Linux systems. [GH-1671] - More precise detection of Ubuntu/Debian guests so that running Vagrant within an LXC container works properly now. - Allow strings in addition to symbols to more places in V1 configuration as well as V2 configuration. - Add `ARPCHECK=0` to RedHat OS family network configuration. [GH-1815] - Add SSH agent forwarding sample to initial Vagrantfile. [GH-1808] - VirtualBox: Only configure networks if there are any to configure. This allows linux's that don't implement this capability to work with Vagrant. [GH-1796] - Default SSH forwarded port now binds to 127.0.0.1 so only local connections are allowed. [GH-1785] - Use `netctl` for Arch Linux network configuration. [GH-1760] - Improve fedora host detection regular expression. [GH-1913] - SSH shows a proper error on EHOSTUNREACH. [GH-1911] BUG FIXES: - Ignore "guest not ready" errors when attempting to graceful halt and carry on checks whether the halt succeeded. [GH-1679] - Handle the case where a roles path for Chef solo isn't properly defined. [GH-1665] - Finding V1 boxes now works properly again to avoid "box not found" errors. [GH-1691] - Setting hostname on SLES 11 works again. [GH-1781] - `config.vm.guest` properly forces guests again. [GH-1800] - The `read_ip_address` capability for linux properly reads the IP of only the first network interface. [GH-1799] - Validate that an IP is given for a private network. [GH-1788] - Fix uninitialized constant error for Gentoo plugin. [GH-1698] ## 1.2.2 (April 23, 2013) FEATURES: - New `DestroyConfirm` built-in middleware for providers so they can more easily conform to the `destroy` action. IMPROVEMENTS: - No longer an error if the Chef run list is empty. It is now a warning. [GH-1620] - Better locking around handling the `box_url` parameter for parallel providers. - Solaris guest is now properly detected on SmartOS, OmniOS, etc. [GH-1639] - Guest addition version detection is more robust, attempting other routes to get the version, and also retrying a few times. [GH-1575] BUG FIXES: - `vagrant package --base` works again. [GH-1615] - Box overrides specified in provider config overrides no longer fail to detect the box. [GH-1617] - In a multi-machine environment, a box not found won't be downloaded multiple times. [GH-1467] - `vagrant box add` with a file path now works correctly on Windows when a drive letter is specified. - DOS line endings are converted to Unix line endings for the shell provisioner automatically. [GH-1495] ## 1.2.1 (April 17, 2013) FEATURES: - Add a `--[no-]parallel` flag to `vagrant up` to enable/disable parallelism. Vagrant will parallelize by default. IMPROVEMENTS: - Get rid of arbitrary 4 second sleep when connecting via SSH. The issue it was attempting to work around may be gone now. BUG FIXES: - Chef solo run list properly set. [GH-1608] - Follow 30x redirects when downloading boxes. [GH-1607] - Chef client config defaults are done properly. [GH-1609] - VirtualBox mounts shared folders with the proper owner/group. [GH-1611] - Use the Mozilla CA cert bundle for cURL so SSL validation works properly. ## 1.2.0 (April 16, 2013) BACKWARDS INCOMPATIBILITIES: - WINDOWS USERS: Vagrant now defaults to using the 'USERPROFILE' environmental variable for the home directory if it is set. This means that the default location for the Vagrant home directory is now `%USERPROFILE%/.vagrant.d`. On Cygwin, this will cause existing Cygwin users to "lose" their boxes. To work around this, either set `VAGRANT_HOME` to your Cygwin ".vagrant.d" folder or move your ".vagrant.d" folder to `USERPROFILE`. The latter is recommended for long-term support. - The constant `Vagrant::Environment::VAGRANT_HOME` was removed in favor of `Vagrant::Environment#default_vagrant_home`. FEATURES: - Providers can now parallelize! If they explicitly support it, Vagrant will run "up" and other commands in parallel. For providers such AWS, this means that your instances will come up in parallel. VirtualBox does not support this mode. - Box downloads are now done via `curl` rather than Ruby's built-in HTTP library. This results in massive speedups, support for SSL verification, FTP downloads, and more. - `config.vm.provider` now takes an optional second parameter to the block, allowing you to override any configuration value. These overrides are applied last, and therefore override any other configuration value. Note that while this feature is available, the "Vagrant way" is instead to use box manifests to ensure that the "box" for every provider matches, so these sorts of overrides are unnecessary. - A new "guest capabilities" system to replace the old "guest" system. This new abstraction allows plugins to define "capabilities" that certain guest operating systems can implement. This allows greater flexibility in doing guest-specific behavior. - Ansible provisioner support. [GH-1465] - Providers can now support multiple box formats by specifying the `box_format:` option. - CFEngine provisioner support. - `config.ssh.default` settings introduced to set SSH defaults that providers can still override. [GH-1479] IMPROVEMENTS: - Full Windows support in cmd.exe, PowerShell, Cygwin, and MingW based environments. - By adding the "disabled" boolean flag to synced folders you can disable them altogether. [GH-1004] - Specify the default provider with the `VAGRANT_DEFAULT_PROVIDER` environmental variable. [GH-1478] - Invalid settings are now caught and shown in a user-friendly way. [GH-1484] - Detect PuTTY Link SSH client on Windows and show an error. [GH-1518] - `vagrant ssh` in Cygwin won't output DOS path file warnings. - Add `--rtcuseutc on` as a sane default for VirtualBox. [GH-912] - SSH will send keep-alive packets every 5 seconds by default to keep connections alive. Can be disabled with `config.ssh.keep_alive`. [GH-516] - Show a message on `vagrant up` if the machine is already running. [GH-1558] - "Running provisioner" output now shoes the provisioner shortcut name, rather than the less-than-helpful class name. - Shared folders with the same guest path will overwrite each other. No more shared folder IDs. - Shell provisioner outputs script it is running. [GH-1568] - Automatically merge forwarded ports that share the same host port. BUG FIXES: - The `:mac` option for host-only networks is respected. [GH-1536] - Don't preserve modified time when untarring boxes. [GH-1539] - Forwarded port auto-correct will not auto-correct to a port that is also in use. - Cygwin will always output color by default. Specify `--no-color` to override this. - Assume Cygwin has a TTY for asking for input. [GH-1430] - Expand Cygwin paths to Windows paths for calls to VBoxManage and for VirtualBox shared folders. - Output the proper clear line text for shells in Cygwin when reporting dynamic progress. - When using `Builder` instances for hooks, the builders will be merged for the proper before/after chain. [GH-1555] - Use the Vagrant temporary directory again for temporary files since they can be quite large and were messing with tmpfs. [GH-1442] - Fix issue parsing extra SSH args in `vagrant ssh` in multi-machine environments. [GH-1545] - Networks come back up properly on RedHat systems after reboot. [GH-921] - `config.ssh` settings override all detected SSH settings (regression). [GH-1479] - `ssh-config` won't raise an exception if the VirtualBox machine is not created. [GH-1562] - Multiple machines defined in the same Vagrantfile with the same name will properly merge. - More robust hostname checking for RedHat. [GH-1566] - Cookbook path existence for Chef is no longer an error, so that things like librarian and berkshelf plugins work properly. [GH-1570] - Chef solo provisioner uses proper SSH username instead of hardcoded config. [GH-1576] - Shell provisioner takes ownership of uploaded files properly so that they can also be manually executed later. [GH-1576] ## 1.1.6 (April 3, 2013) BUG FIXES: - Fix SSH re-use connection logic to drop connection if an error occurs. ## 1.1.5 (April 2, 2013) IMPROVEMENTS: - More robust SSH connection close detection. - Don't load `vagrant plugin` installed plugins when in a Bundler environment. This happens during plugin development. This will make Vagrant errors much quieter when developing plugins. - Vagrant will detect Bundler environments, make assumptions that you're developing plugins, and will quiet its error output a bit. - More comprehensive synced folder configuration validation. - VBoxManage errors now show the output from the command so that users can potentially know what is wrong. BUG FIXES: - Proper error message if invalid provisioner is used. [GH-1515] - Don't error on graceful halt if machine just shut down very quickly. [GH-1505] - Error message if private key for SSH isn't owned by the proper user. [GH-1503] - Don't error too early when `config.vm.box` is not properly set. - Show a human-friendly error if VBoxManage is not found (exit status 126). [GH-934] - Action hook prepend/append will only prepend or append once. - Retry SSH on Errno::EACCES. - Show an error if an invalid network type is used. - Don't share Chef solo folder if it doesn't exist on host. ## 1.1.4 (March 25, 2013) BUG FIXES: - Default forwarded port adapter for VirtualBox should be 1. ## 1.1.3 (March 25, 2013) IMPROVEMENTS: - Puppet apply provisioner now retains the default module path even while specifying custom module paths. [GH-1207] - Re-added DHCP support for host-only networks. [GH-1466] - Ability to specify a plugin version, plugin sources, and pre-release versions using `--plugin-version`, `--plugin-source`, and `--plugin-prerelease`. [GH-1461] - Move VirtualBox guest addition checks to after the machine boots. [GH-1179] - Removed `Vagrant::TestHelpers` because it doesn't really work anymore. - Add PLX linux guest support. [GH-1490] BUG FIXES: - Attempt to re-establish SSH connection on `Net::SSH::Disconnect` - Allow any value that can convert to a string for `Vagrant.plugin` - Chef solo `recipe_url` works properly again. [GH-1467] - Port collision detection works properly in VirtualBox with auto-corrected ports. [GH-1472] - Fix obscure error when temp directory is world writable when adding boxes. - Improved error handling around network interface detection for VirtualBox [GH-1480] ## 1.1.2 (March 18, 2013) BUG FIXES: - When not specifying a cookbooks_path for chef-solo, an error won't be shown if "cookbooks" folder is missing. - Fix typo for exception when no host-only network with NFS. [GH-1448] - Use UNSET_VALUE/nil with args on shell provisioner by default since `[]` was too truthy. [GH-1447] ## 1.1.1 (March 18, 2013) IMPROVEMENTS: - Don't load plugins on any `vagrant plugin` command, so that errors are avoided. [GH-1418] - An error will be shown if you forward a port to the same host port multiple times. - Automatically convert network, provider, and provisioner names to symbols internally in case people define them as strings. - Using newer versions of net-ssh and net-scp. [GH-1436] BUG FIXES: - Quote keys to StringBlockEditor so keys with spaces, parens, and so on work properly. - When there is no route to host for SSH, re-establish a new connection. - `vagrant package` once again works, no more nil error. [GH-1423] - Human friendly error when "metadata.json" is missing in a box. - Don't use the full path to the manifest file with the Puppet provisioner because it exposes a bug with Puppet path lookup on VMware. - Fix bug in VirtualBox provider where port forwarding just didn't work if you attempted to forward to a port under 1024. [GH-1421] - Fix cross-device box adds for Windows. [GH-1424] - Fix minor issues with defaults of configuration of the shell provisioner. - Fix Puppet server using "host_name" instead of "hostname" [GH-1444] - Raise a proper error if no hostonly network is found for NFS with VirtualBox. [GH-1437] ## 1.1.0 (March 14, 2013) BACKWARDS INCOMPATIBILITIES: - Vagrantfiles from 1.0.x that _do not use_ any plugins are fully backwards compatible. If plugins are used, they must be removed prior to upgrading. The new plugin system in place will avoid this issue in the future. - Lots of changes introduced in the form of a new configuration version and format, but this is _opt-in_. Old Vagrantfile format continues to be supported, as promised. To use the new features that will be introduced throughout the 1.x series, you'll have to upgrade at some point. FEATURES: - Groundwork for **providers**, alternate backends for Vagrant that allow Vagrant to power systems other than VirtualBox. Much improvement and change will come to this throughout the 1.x lifecycle. The API will continue to change, features will be added, and more. Specifically, a revamped system for handling shared folders gracefully across providers will be introduced in a future release. - New plugin system which adds much more structure and stability to the overall API. The goal of this system is to make it easier to write powerful plugins for Vagrant while providing a backwards-compatible API so that plugins will always _load_ (though they will almost certainly not be _functional_ in future versions of Vagrant). - Plugins are now installed and managed using the `vagrant plugin` interface. - Allow "file://" URLs for box URLs. [GH-1087] - Emit "vagrant-mount" upstart event when NFS shares are mounted. [GH-1118] - Add a VirtualBox provider config `auto_nat_dns_proxy` which when set to false will not attempt to automatically manage NAT DNS proxy settings with VirtualBox. [GH-1313] - `vagrant provision` accepts the `--provision-with` flag [GH-1167] - Set the name of VirtualBox machines with `virtualbox.name` in the VirtualBox provider config. [GH-1126] - `vagrant ssh` will execute an `ssh` binary on Windows if it is on your PATH. [GH-933] - The environmental variable `VAGRANT_VAGRANTFILE` can be used to specify an alternate Vagrantfile filename. IMPROVEMENTS / BUG FIXES: - Vagrant works much better in Cygwin environments on Windows by properly resolving Cygwin paths. [GH-1366] - Improve the SSH "ready?" check by more gracefully handling timeouts. [GH-841] - Human friendly error if connection times out for HTTP downloads. [GH-849] - Detect when the VirtualBox installation is incomplete and error. [GH-846] - Detect when kernel modules for VirtualBox need to be installed on Gentoo systems and report a user-friendly error. [GH-710] - All `vagrant` commands that can take a target VM name can take one even if you're not in a multi-VM environment. [GH-894] - Hostname is set before networks are setup to avoid very slow `sudo` speeds on CentOS. [GH-922] - `config.ssh.shell` now includes the flags to pass to it, such as `-l` [GH-917] - The check for whether a port is open or not is more complete by catching ENETUNREACH errors. [GH-948] - SSH uses LogLevel FATAL so that errors are still shown. - Sending a SIGINT (Ctrl-C) very early on when executing `vagrant` no longer results in an ugly stack trace. - Chef JSON configuration output is now pretty-printed to be human readable. [GH-1146] that SSHing succeeds when booting a machine. - VMs in the "guru meditation" state can be destroyed now using `vagrant destroy`. - Fix issue where changing SSH key permissions didn't properly work. [GH-911] - Fix issue where Vagrant didn't properly detect VBoxManage on Windows if VBOX_INSTALL_PATH contained multiple paths. [GH-885] - Fix typo in setting host name for Gentoo guests. [GH-931] - Files that are included with `vagrant package --include` now properly preserve file attributes on earlier versions of Ruby. [GH-951] - Multiple interfaces now work with Arch linux guests. [GH-957] - Fix issue where subprocess execution would always spin CPU of Ruby process to 100%. [GH-832] - Fix issue where shell provisioner would sometimes never end. [GH-968] - Fix issue where puppet would reorder module paths. [GH-964] - When console input is asked for (destroying a VM, bridged interfaces, etc.), keystrokes such as ctrl-D and ctrl-C are more gracefully handled. [GH-1017] - Fixed bug where port check would use "localhost" on systems where "localhost" is not available. [GH-1057] - Add missing translation for "saving" state on VirtualBox. [GH-1110] - Proper error message if the remote end unexpectedly resets the connection while downloading a box over HTTP. [GH-1090] - Human-friendly error is raised if there are permission issues when using SCP to upload files. [GH-924] - Box adding doesn't use `/tmp` anymore which can avoid some cross-device copy issues. [GH-1199] - Vagrant works properly in folders with strange characters. [GH-1223] - Vagrant properly handles "paused" VirtualBox machines. [GH-1184] - Better behavior around permissions issues when copying insecure private key. [GH-580] ## 1.0.7 (March 13, 2013) - Detect if a newer version of Vagrant ran and error if it did, because we're not forward-compatible. - Check for guest additions version AFTER booting. [GH-1179] - Quote IdentityFile in `ssh-config` so private keys with spaces in the path work. [GH-1322] - Fix issue where multiple Puppet module paths can be re-ordered [GH-964] - Shell provisioner won't hang on Windows anymore due to unclosed tempfile. [GH-1040] - Retry setting default VM name, since it sometimes fails first time. [GH-1368] - Support setting hostname on Suse [GH-1063] ## 1.0.6 (December 21, 2012) - Shell provisioner outputs proper line endings on Windows [GH-1164] - SSH upload opens file to stream which fixes strange upload issues. - Check for proper exit codes for Puppet, since multiple exit codes can mean success. [GH-1180] - Fix issue where DNS doesn't resolve properly for 12.10. [GH-1176] - Allow hostname to be a substring of the box name for Ubuntu [GH-1163] - Use `puppet agent` instead of `puppetd` to be Puppet 3.x compatible. [GH-1169] - Work around bug in VirtualBox exposed by bug in OS X 10.8 where VirtualBox executables couldn't handle garbage being injected into stdout by OS X. ## 1.0.5 (September 18, 2012) - Work around a critical bug in VirtualBox 4.2.0 on Windows that causes Vagrant to not work. [GH-1130] - Plugin loading works better on Windows by using the proper file path separator. - NFS works on Fedora 16+. [GH-1140] - NFS works with newer versions of Arch hosts that use systemd. [GH-1142] ## 1.0.4 (September 13, 2012) - VirtualBox 4.2 driver. [GH-1120] - Correct `ssh-config` help to use `--host`, not `-h`. - Use "127.0.0.1" instead of "localhost" for port checking to fix problem where "localhost" is not properly setup. [GH-1057] - Disable read timeout on Net::HTTP to avoid `rbuf_fill` error. [GH-1072] - Retry SSH on `EHOSTUNREACH` errors. - Add missing translation for "saving" state. [GH-1110] ## 1.0.3 (May 1, 2012) - Don't enable NAT DNS proxy on machines where resolv.conf already points to localhost. This allows Vagrant to work once again with Ubuntu 12.04. [GH-909] ## 1.0.2 (March 25, 2012) - Provisioners will still mount folders and such if `--no-provision` is used, so that `vagrant provision` works. [GH-803] - Nicer error message if an unsupported SSH key type is used. [GH-805] - Gentoo guests can now have their host names changed. [GH-796] - Relative paths can be used for the `config.ssh.private_key_path` setting. [GH-808] - `vagrant ssh` now works on Solaris, where `IdentitiesOnly` was not an available option. [GH-820] - Output works properly in the face of broken pipes. [GH-819] - Enable Host IO Cache on the SATA controller by default. - Chef-solo provisioner now supports encrypted data bags. [GH-816] - Enable the NAT DNS proxy by default, allowing your DNS to continue working when you switch networks. [GH-834] - Checking for port forwarding collisions also checks for other applications that are potentially listening on that port as well. [GH-821] - Multiple VM names can be specified for the various commands now. For example: `vagrant up web db service`. [GH-795] - More robust error handling if a VM fails to boot. The error message is much clearer now. [GH-825] ## 1.0.1 (March 11, 2012) - Installers are now bundled with Ruby 1.9.3p125. Previously they were bundled with 1.9.3p0. This actually fixes some IO issues with Windows. - Windows installer now outputs a `vagrant` binary that will work in msys or Cygwin environments. - Fix crashing issue which manifested itself in multi-VM environments. - Add missing `rubygems` require in `environment.rb` to avoid possible load errors. [GH-781] - `vagrant destroy` shows a nice error when called without a TTY (and hence can't confirm). [GH-779] - Fix an issue with the `:vagrantfile_name` option to `Vagrant::Environment` not working properly. [GH-778] - `VAGRANT_CWD` environmental variable can be used to set the CWD to something other than the current directory. - Downloading boxes from servers that don't send a content-length now works properly. [GH-788] - The `:facter` option now works for puppet server. [GH-790] - The `--no-provision` and `--provision-with` flags are available to `vagrant reload` now. - `:openbsd` guest which supports only halting at the moment. [GH-773] - `ssh-config -h` now shows help, instead of assuming a host is being specified. For host, you can still use `--host`. [GH-793] ## 1.0.0 (March 6, 2012) - `vagrant gem` should now be used to install Vagrant plugins that are gems. This installs the gems to a private gem folder that Vagrant adds to its own load path. This isolates Vagrant-related gems from system gems. - Plugin loading no longer happens right when Vagrant is loaded, but when a Vagrant environment is loaded. I don't anticipate this causing any problems but it is a backwards incompatible change should a plugin depend on this (but I don't see any reason why they would). - `vagrant destroy` now asks for confirmation by default. This can be overridden with the `--force` flag. [GH-699] - Fix issue with Puppet config inheritance. [GH-722] - Fix issue where starting a VM on some systems was incorrectly treated as failing. [GH-720] - It is now an error to specify the packaging `output` as a directory. [GH-730] - Unix-style line endings are used properly for guest OS. [GH-727] - Retry certain VirtualBox operations, since they intermittently fail. [GH-726] - Fix issue where Vagrant would sometimes "lose" a VM if an exception occurred. [GH-725] - `vagrant destroy` destroys virtual machines in reverse order. [GH-739] - Add an `fsid` option to Linux NFS exports. [GH-736] - Fix edge case where an exception could be raised in networking code. [GH-742] - Add missing translation for the "guru meditation" state. [GH-745] - Check that VirtualBox exists before certain commands. [GH-746] - NIC type can be defined for host-only network adapters. [GH-750] - Fix issue where re-running chef-client would sometimes cause problems due to file permissions. [GH-748] - FreeBSD guests can now have their hostnames changed. [GH-757] - FreeBSD guests now support host only networking and bridged networking. [GH-762] - `VM#run_action` is now public so plugin-devs can hook into it. - Fix crashing bug when attempting to run commands on the "primary" VM in a multi-VM environment. [GH-761] - With puppet you can now specify `:facter` as a dictionary of facts to override what is generated by Puppet. [GH-753] - Automatically convert all arguments to `customize` to strings. - openSUSE host system. [GH-766] - Fix subprocess IO deadlock which would occur on Windows. [GH-765] - Fedora 16 guest support. [GH-772] ## 0.9.7 (February 9, 2012) - Fix regression where all subprocess IO simply didn't work with Windows. [GH-721] ## 0.9.6 (February 7, 2012) - Fix strange issue with inconsistent childprocess reads on JRuby. [GH-711] - `vagrant ssh` does a direct `exec()` syscall now instead of going through the shell. This makes it so things like shell expansion oddities no longer cause problems. [GH-715] - Fix crashing case if there are no ports to forward. - Fix issue surrounding improper configuration of host only networks on RedHat guests. [GH-719] - NFS should work properly on Gentoo. [GH-706] ## 0.9.5 (February 5, 2012) - Fix crashing case when all network options are `:auto_config false`. [GH-689] - Type of network adapter can be specified with `:nic_type`. [GH-690] - The NFS version can be specified with the `:nfs_version` option on shared folders. [GH-557] - Greatly improved FreeBSD guest and host support. [GH-695] - Fix instability with RedHat guests and host only and bridged networks. [GH-698] - When using bridged networking, only list the network interfaces that are up as choices. [GH-701] - More intelligent handling of the `certname` option for puppet server. [GH-702] - You may now explicitly set the network to bridge to in the Vagrantfile using the `:bridge` parameter. [GH-655] ## 0.9.4 (January 28, 2012) - Important internal changes to middlewares that make plugin developer's lives much easier. [GH-684] - Match VM names that have parens, brackets, etc. - Detect when the VirtualBox kernel module is not loaded and error. [GH-677] - Set `:auto_config` to false on any networking option to not automatically configure it on the guest. [GH-663] - NFS shared folder guest paths can now contain shell expansion characters such as `~`. - NFS shared folders with a `:create` flag will have their host folders properly created if they don't exist. [GH-667] - Fix the precedence for Arch, Ubuntu, and FreeBSD host classes so they are properly detected. [GH-683] - Fix issue where VM import sometimes made strange VirtualBox folder layouts. [GH-669] - Call proper `id` command on Solaris. [GH-679] - More accurate VBoxManage error detection. - Shared folders can now be marked as transient using the `:transient` flag. [GH-688] ## 0.9.3 (January 24, 2012) - Proper error handling for not enough arguments to `box` commands. - Fix issue causing crashes with bridged networking. [GH-673] - Ignore host only network interfaces that are "down." [GH-675] - Use "printf" instead of "echo" to determine shell expanded files paths which is more generally POSIX compliant. [GH-676] ## 0.9.2 (January 20, 2012) - Support shell expansions in shared folder guest paths again. [GH-656] - Fix issue where Chef solo always expected the host to have a "cookbooks" folder in their directory. [GH-638] - Fix `forward_agent` not working when outside of blocks. [GH-651] - Fix issue causing custom guest implementations to not load properly. - Filter clear screen character out of output on SSH. - Log output now goes on `stderr`, since it is utility information. - Get rid of case where a `NoMethodError` could be raised while determining VirtualBox version. [GH-658] - Debian/Ubuntu uses `ifdown` again, instead of `ifconfig xxx down`, since the behavior seems different/wrong. - Give a nice error if `:vagrant` is used as a JSON key, since Vagrant uses this. [GH-661] - If there is only one bridgable interface, use that without asking the user. [GH-655] - The shell will have color output if ANSICON is installed on Windows. [GH-666] ## 0.9.1 (January 18, 2012) - Use `ifconfig device down` instead of `ifdown`. [GH-649] - Clearer invalid log level error. [GH-645] - Fix exception raised with NFS `recover` method. - Fix `ui` `NoMethodError` exception in puppet server. - Fix `vagrant box help` on Ruby 1.8.7. [GH-647] ## 0.9.0 (January 17, 2012) - VirtualBox 4.0 support backported in addition to supporting VirtualBox 4.1. - `config.vm.network` syntax changed so that the first argument is now the type of argument. Previously where you had `config.vm.network "33.33.33.10"` you should now put `config.vm.network :hostonly, "33.33.33.10"`. This is in order to support bridged networking, as well. - `config.vm.forward_port` no longer requires a name parameter. - Bridged networking. `config.vm.network` with `:bridged` as the option will setup a bridged network. - Host only networks can be configured with DHCP now. Specify `:dhcp` as the IP and it will be done. - `config.vm.customize` now takes a command to send to `VBoxManage`, so any arbitrary command can be sent. The older style of passing a block no longer works and Vagrant will give a proper error message if it notices this old-style being used. - `config.ssh.forwarded_port_key` is gone. Vagrant no longer cares about forwarded port names for any reason. Please use `config.ssh.guest_port` (more below). - `config.ssh.forwarded_port_destination` has been replaced by `config.ssh.guest_port` which more accurately reflects what it is used for. Vagrant will automatically scan forwarded ports that match the guest port to find the SSH port. - Logging. The entire Vagrant source has had logging sprinkled throughout to make debugging issues easier. To enable logging, set the VAGRANT_LOG environmental variable to the log level you wish to see. By default, logging is silent. - `system` renamed to `guest` throughout the source. Any `config.vm.system` configurations must be changed to `config.vm.guest` - Puppet provisioner no longer defaults manifest to "box.pp." Instead, it is now "default.pp" - All Vagrant commands that take a VM name in a Multi-VM environment can now be given a regular expression. If the name starts and ends with a "/" then it is assumed to be a regular expression. [GH-573] - Added a "--plain" flag to `vagrant ssh` which will cause Vagrant to not perform any authentication. It will simply `ssh` into the proper IP and port of the virtual machine. - If a shared folder now has a `:create` flag set to `true`, the path on the host will be created if it doesn't exist. - Added `--force` flag to `box add`, which will overwite any existing boxes if they exist. [GH-631] - Added `--provision-with` to `up` which configures what provisioners run, by shortcut. [GH-367] - Arbitrary mount options can be passed with `:extra` to any shared folders. [GH-551] - Options passed after a `--` to `vagrant ssh` are now passed directly to `ssh`. [GH-554] - Ubuntu guests will now emit a `vagrant-mounted` upstart event after shared folders are mounted. - `attempts` is a new option on chef client and chef solo provisioners. This will run the provisioner multiple times until erroring about failing convergence. [GH-282] - Removed Thor as a dependency for the command line interfaces. This resulted in general speed increases across all command line commands. - Linux uses `shutdown -h` instead of `halt` to hopefully more consistently power off the system. [GH-575] - Tweaks to SSH to hopefully be more reliable in coming up. - Helpful error message when SCP is unavailable in the guest. [GH-568] - Error message for improperly packaged box files. [GH-198] - Copy insecure private key to user-owned directory so even `sudo` installed Vagrant installations work. [GH-580] - Provisioner stdout/stderr is now color coded based on stdout/stderr. stdout is green, stderr is red. [GH-595] - Chef solo now prompts users to run a `reload` if shared folders are not found on the VM. [GH-253] - "--no-provision" once again works for certain commands. [GH-591] - Resuming a VM from a saved state will show an error message if there would be port collisions. [GH-602] - `vagrant ssh -c` will now exit with the same exit code as the command run. [GH-598] - `vagrant ssh -c` will now send stderr to stderr and stdout to stdout on the host machine, instead of all output to stdout. - `vagrant box add` path now accepts unexpanded shell paths such as `~/foo` and will properly expand them. [GH-633] - Vagrant can now be interrupted during the "importing" step. - NFS exports will no longer be cleared when an expected error occurs. [GH-577] ## 0.8.10 (December 10, 2011) - Revert the SSH tweaks made in 0.8.8. It affected stability ## 0.8.8 (December 1, 2011) - Mount shared folders shortest to longest to avoid mounting subfolders first. [GH-525] - Support for basic HTTP auth in the URL for boxes. - Solaris support for host only networks. [GH-533] - `vagrant init` respects `Vagrant::Environment` cwd. [GH-528] - `vagrant` commands will not output color when stdout is not a TTY. - Fix issue where `box_url` set with multiple VMs could cause issues. [GH-564] - Chef provisioners no longer depend on a "v-root" share being available. [GH-556] - NFS should work for FreeBSD hosts now. [GH-510] - SSH executed methods respect `config.ssh.max_tries`. [GH-508] - `vagrant box add` now respects the "no_proxy" environmental variable. [GH-502] - Tweaks that should make "Waiting for VM to boot" slightly more reliable. - Add comments to Vagrantfile to make it detected as Ruby file for `vi` and `emacs`. [GH-515] - More correct guest addition version checking. [GH-514] - Chef solo support on Windows is improved. [GH-542] - Put encrypted data bag secret into `/tmp` by default so that permissions are almost certainly guaranteed. [GH-512] ## 0.8.7 (September 13, 2011) - Fix regression with remote paths from chef-solo. [GH-431] - Fix issue where Vagrant crashes if `.vagrant` file becomes invalid. [GH-496] - Issue a warning instead of an error for attempting to forward a port <= 1024. [GH-487] ## 0.8.6 (August 28, 2011) - Fix issue with download progress not properly clearing the line. [GH-476] - NFS should work properly on Fedora. [GH-450] - Arguments can be specified to the `shell` provisioner via the `args` option. [GH-475] - Vagrant behaves much better when there are "inaccessible" VMs. [GH-453] ## 0.8.5 (August 15, 2011) Note: 0.8.3 and 0.8.4 was yanked due to RubyGems encoding issue. - Fix SSH `exec!` to inherit proper `$PATH`. [GH-426] - Chef client now accepts an empty (`nil`) run list again. [GH-429] - Fix incorrect error message when running `provision` on halted VM. [GH-447] - Checking guest addition versions now ignores OSE. [GH-438] - Chef solo from a remote URL fixed. [GH-431] - Arch linux support: host only networks and changing the host name. [GH-439] [GH-448] - Chef solo `roles_path` and `data_bags_path` can only be single paths. [GH-446] - Fix `virtualbox_not_detected` error message to require 4.1.x. [GH-458] - Add shortname (`hostname -s`) for hostname setting on RHEL systems. [GH-456] - `vagrant ssh -c` output no longer has a prefix and respects newlines from the output. [GH-462] ## 0.8.2 (July 22, 2011) - Fix issue with SSH disconnects not reconnecting. - Fix chef solo simply not working with roles/data bags. [GH-425] - Multiple chef solo provisioners now work together. - Update Puppet provisioner so no deprecation warning is shown. [GH-421] - Removed error on "provisioner=" in config, as this has not existed for some time now. - Add better validation for networking. ## 0.8.1 (July 20, 2011) - Repush of 0.8.0 to fix a Ruby 1.9.2 RubyGems issue. ## 0.8.0 (July 20, 2011) - VirtualBox 4.1 support _only_. Previous versions of VirtualBox are supported by earlier versions of Vagrant. - Performance optimizations in `virtualbox` gem. Huge speed gains. - `:chef_server` provisioner is now `:chef_client`. [GH-359] - SSH connection is now cached after first access internally, speeding up `vagrant up`, `reload`, etc. quite a bit. - Actions which modify the VM now occur much more quickly, greatly speeding up `vagrant up`, `reload`, etc. - SUSE host only networking support. [GH-369] - Show nice error message for invalid HTTP responses for HTTP downloader. [GH-403] - New `:inline` option for shell provisioner to provide inline scripts as a string. [GH-395] - Host only network now properly works on multiple adapters. [GH-365] - Can now specify owner/group for regular shared folders. [GH-350] - `ssh_config` host name will use VM name if given. [GH-332] - `ssh` `-e` flag changed to `-c` to align with `ssh` standard behavior. [GH-323] - Forward agent and forward X11 settings properly appear in `ssh_config` output. [GH-105] - Chef JSON can now be set with `chef.json =` instead of the old `merge` technique. [GH-314] - Provisioner configuration is no longer cleared when the box needs to be downloaded during an `up`. [GH-308] - Multiple Chef provisioners no longer overwrite cookbook folders. [GH-407] - `package` won't delete previously existing file. [GH-408] - Vagrantfile can be lowercase now. [GH-399] - Only one copy of Vagrant may be running at any given time. [GH-364] - Default home directory for Vagrant moved to `~/.vagrant.d` [GH-333] - Specify a `forwarded_port_destination` for SSH configuration and SSH port searching will fall back to that if it can't find any other port. [GH-375] ## 0.7.8 (July 19, 2011) - Make sure VirtualBox version check verifies that it is 4.0.x. ## 0.7.7 (July 12, 2011) - Fix crashing bug with Psych and Ruby 1.9.2. [GH-411] ## 0.7.6 (July 2, 2011) - Run Chef commands in a single command. [GH-390] - Add `nfs` option for Chef to mount Chef folders via NFS. [GH-378] - Add translation for `aborted` state in VM. [GH-371] - Use full paths with the Chef provisioner so that restart cookbook will work. [GH-374] - Add "--no-color" as an argument and no colorized output will be used. [GH-379] - Added DEVICE option to the RedHat host only networking entry, which allows host only networking to work even if the VM has multiple NICs. [GH-382] - Touch the network configuration file for RedHat so that the `sed` works with host only networking. [GH-381] - Load prerelease versions of plugins if available. - Do not load a plugin if it depends on an invalid version of Vagrant. - Encrypted data bag support in Chef server provisioner. [GH-398] - Use the `-H` flag to set the proper home directory for `sudo`. [GH-370] ## 0.7.5 (May 16, 2011) - `config.ssh.port` can be specified and takes highest precedence if specified. Otherwise, Vagrant will still attempt to auto-detect the port. [GH-363] - Get rid of RubyGems deprecations introduced with RubyGems 1.8.x - Search in pre-release gems for plugins as well as release gems. - Support for Chef-solo `data_bags_path` [GH-362] - Can specify path to Chef binary using `binary_path` [GH-342] - Can specify additional environment data for Chef using `binary_env` [GH-342] ## 0.7.4 (May 12, 2011) - Chef environments support (for Chef 0.10) [GH-358] - Suppress the "added to known hosts" message for SSH [GH-354] - Ruby 1.8.6 support [GH-352] - Chef proxy settings now work for chef server [GH-335] ## 0.7.3 (April 19, 2011) - Retry all SSH on Net::SSH::Disconnect in case SSH is just restarting. [GH-313] - Add NFS shared folder support for Arch linux. [GH-346] - Fix issue with unknown terminal type output for sudo commands. - Forwarded port protocol can now be set as UDP. [GH-311] - Chef server file cache path and file backup path can be configured. [GH-310] - Setting hostname should work on Debian now. [GH-307] ## 0.7.2 (February 8, 2011) - Update JSON dependency to 1.5.1, which works with Ruby 1.9 on Windows. - Fix sudo issues on sudo < 1.7.0 (again). - Fix race condition in SSH, which specifically manifested itself in the chef server provisioner. [GH-295] - Change sudo shell to use `bash` (configurable). [GH-301] - Can now set mac address of host only network. [GH-294] - NFS shared folders with spaces now work properly. [GH-293] - Failed SSH commands now show output in error message. [GH-285] ## 0.7.1 (January 28, 2011) - Change error output with references to VirtualBox 3.2 to 4.0. - Internal SSH through net-ssh now uses `IdentitiesOnly` thanks to upstream net-ssh fix. - Fix issue causing warnings to show with `forwardx11` enabled for SSH. [GH-279] - FreeBSD support for host only networks, NFS, halting, etc. [GH-275] - Make SSH commands which use sudo compatible with sudo < 1.7.0. [GH-278] - Fix broken puppet server provisioner which called a nonexistent method. - Default SSH host changed from `localhost` to `127.0.0.1` since `localhost` is not always loopback. - New `shell` provisioner which simply uploads and executes a script as root on the VM. - Gentoo host only networking no longer fails if already setup. [GH-286] - Set the host name of your guest OS with `config.vm.host_name` [GH-273] - `vagrant ssh-config` now outputs the configured `config.ssh.host` ## 0.7.0 (January 19, 2011) - VirtualBox 4.0 support. Support for VirtualBox 3.2 is _dropped_, since the API is so different. Stay with the 0.6.x series if you have VirtualBox 3.2.x. - Puppet server provisioner. [GH-262] - Use numeric uid/gid in mounting shared folders to increase portability. [GH-252] - HTTP downloading follows redirects. [GH-163] - Downloaders have clearer output to note what they're doing. - Shared folders with no guest path are not automounted. [GH-184] - Boxes downloaded during `vagrant up` reload the Vagrantfile config, which fixes a problem with box settings not being properly loaded. [GH-231] - `config.ssh.forward_x11` to enable the ForwardX11 SSH option. [GH-255] - Vagrant source now has a `contrib` directory where contributions of miscellaneous addons for Vagrant will be added. - Vagrantfiles are now loaded only once (instead of 4+ times) [GH-238] - Ability to move home vagrant dir (~/.vagrant) by setting VAGRANT_HOME environmental variable. - Removed check and error for the "OSE" version of VirtualBox, since with VirtualBox 4 this distinction no longer exists. - Ability to specify proxy settings for chef. [GH-169] - Helpful error message shown if NFS mounting fails. [GH-135] - Gentoo guests now support host only networks. [GH-240] - RedHat (CentOS included) guests now support host only networks. [GH-260] - New Vagrantfile syntax for enabling and configuring provisioners. This change is not backwards compatible. [GH-265] - Provisioners are now RVM-friendly, meaning if you installed chef or puppet with an RVM managed Ruby, Vagrant now finds then. [GH-254] - Changed the unused host only network destroy mechanism to check for uselessness after the VM is destroyed. This should result in more accurate checks. - Networks are no longer disabled upon halt/destroy. With the above change, its unnecessary. - Puppet supports `module_path` configuration to mount local modules directory as a shared folder and configure puppet with it. [GH-270] - `ssh-config` now outputs `127.0.0.1` as the host instead of `localhost`. ## 0.6.9 (December 21, 2010) - Puppet provisioner. [GH-223] - Solaris system configurable to use `sudo`. - Solaris system registered, so it can be set with `:solaris`. - `vagrant package` include can be a directory name, which will cause the contents to be recursively copied into the package. [GH-241] - Arbitrary options to puppet binary can be set with `config.puppet.options`. [GH-242] - BSD hosts use proper GNU sed syntax for clearing NFS shares. [GH-243] - Enumerate VMs in a multi-VM environment in order they were defined. [GH-244] - Check for VM boot changed to use `timeout` library, which works better with Windows. - Show special error if VirtualBox not detected on 64-bit Windows. - Show error to Windows users attempting to use host only networking since it doesn't work yet. ## 0.6.8 (November 30, 2010) - Network interfaces are now up/down in distinct commands instead of just restarting "networking." [GH-192] - Add missing translation for chef binary missing. [GH-203] - Fix default settings for Opscode platform and comments. [GH-213] - Blank client name for chef server now uses FQDN by default, instead of "client" [GH-214] - Run list can now be nil, which will cause it to sync with chef server (when chef server is enabled). [GH-214] - Multiple NFS folders now work on linux. [GH-215] - Add translation for state "stuck" which is very rare. [GH-218] - virtualbox gem dependency minimum raised to 0.7.6 to verify FFI < 1.0.0 is used. - Fix issue where box downloading from `vagrant up` didn't reload the box collection. [GH-229] ## 0.6.7 (November 3, 2010) - Added validation to verify that a box is specified. - Proper error message when box is not found for `config.vm.box`. [GH-195] - Fix output of `vagrant status` with multi-vm to be correct. [GH-196] ## 0.6.6 (October 14, 2010) - `vagrant status NAME` works once again. [GH-191] - Conditional validation of Vagrantfile so that some commands don't validate. [GH-188] - Fix "junk" output for ssh-config. [GH-189] - Fix port collision handling with greater than two VMs. [GH-185] - Fix potential infinite loop with root path if bad CWD is given to environment. ## 0.6.5 (October 8, 2010) - Validations on base MAC address to avoid situation described in GH-166, GH-181 from ever happening again. - Properly load sub-VM configuration on first-pass of config loading. Solves a LOT of problems with multi-VM. [GH-166] [GH-181] - Configuration now only validates on final Vagrantfile proc, so multi-VM validates correctly. - A nice error message is given if ".vagrant" is a directory and therefore can't be accessed. [GH-172] - Fix plugin loading in a Rails 2.3.x project. [GH-176] ## 0.6.4 (October 4, 2010) - Default VM name is now properly the parent folder of the working directory of the environment. - Added method to `TestHelpers` to assist with testing new downloaders. - `up --no-provision` works again. This disables provisioning during the boot process. - Action warden doesn't do recovery process on `SystemExit` exceptions, allowing the double ctrl-C to work properly again. [related to GH-166] - Initial Vagrantfile is now heavily commented with various available options. [GH-171] - Box add checks if a box already exists before the download. [GH-170] - NFS no longer attempts to clean exports file if VM is not created, which was causing a stack trace during recovery. [related to GH-166] - Basic validation added for Chef configuration (both solo and server). - Top config class is now available in all `Vagrant::Config::Base` subclasses, which is useful for config validation. - Subcommand help shows proper full command in task listing. [GH-168] - SSH gives error message if `ssh` binary is not found. [GH-161] - SSH gives proper error message if VM is not running. [GH-167] - Fix some issues with undefined constants in command errors. ## 0.6.1, 0.6.2, 0.6.3 (September 27, 2010) A lot of quick releases which all were to fix issues with Ruby 1.8.7 compatibility. ## 0.6.0 (September 27, 2010) - VM name now defaults to the name of the containing folder, plus a timestamp. This should make it easier to identify VMs in the VirtualBox GUI. - Exposed Vagrant test helpers in `Vagrant::TestHelpers` for plugins to easily test themselves against Vagrant environments. - **Plugins** have landed. Plugins are simply gems which have a `vagrant_init.rb` file somewhere in their load path. Please read the documentation on vagrantup.com before attempting to create a plugin (which is very easy) for more information on how it all works and also some guidelines. - `vagrant package` now takes a `--vagrantfile` option to specify a Vagrantfile to package. The `--include` approach for including a Vagrantfile no longer works (previously built boxes will continue to work). - `vagrant package` has new logic with regards to the `--include` option depending on if the file path is relative or absolute (they can be intermixed): * _Relative_ paths are copied directly into the box, preserving their path. So `--include lib/foo` would be in the box as "lib/foo" * _Absolute_ paths are simply copied files into the root of the box. So `--include /lib/foo` would be in the box as "foo" - "vagrant_main" is no longer the default run list. Instead, chef run list starts empty. It is up to you to specify all recipes in the Vagrantfile now. - Fixed various issues with certain action middleware not working if the VM was not created. - SSH connection is retried 5 times if there is a connection refused. Related to GH-140. - If `http_proxy` environmental variable is set, it will be used as the proxy box adding via http. - Remove `config.ssh.password`. It hasn't been used for a few versions now and was only kept around to avoid exceptions in Vagrantfiles. - Configuration is now validated so improper input can be found in Vagrantfiles. - Fixed issue with not detecting Vagrantfile at root directory ("/"). - Vagrant now gives a nice error message if there is a syntax error in any Vagrantfile. [GH-154] - The format of the ".vagrant" file which stores persisted VMs has changed. This is **backwards incompatible**. Will provide an upgrade utility prior to 0.6 launch. - Every [expected] Vagrant error now exits with a clean error message and a unique exit status, and raises a unique exception (if you're scripting Vagrant). - Added I18n gem dependency for pulling strings into clean YML files. Vagrant is now localizable as a side effect! Translations welcome. - Fixed issue with "Waiting for cleanup" message appearing twice in some cases. [GH-145] - Converted CLI to use Thor. As a tradeoff, there are some backwards incompatibilities: * `vagrant package` - The `--include` flag now separates filenames by spaces, instead of by commas. e.g. `vagrant package --include x y z` * `vagrant ssh` - If you specify a command to execute using the `--execute` flag, you may now only specify one command (before you were able to specify an arbitrary amount). e.g. `vagrant ssh -e "echo hello"` * `vagrant ssh-config` has become `vagrant ssh_config` due to a limitation in Thor. ## 0.5.4 (September 7, 2010) - Fix issue with the "exec failed" by running on Tiger as well. - Give an error when downloading a box which already exists prior to actually downloading the box. ## 0.5.3 (August 23, 2010) - Add erubis as a dependency since its rendering of `erb` is sane. - Fixed poorly formatted Vagrantfile after `vagrant init`. [GH-142] - Fixed NFS not working properly with multiple NFS folders. - Fixed chef solo provision to work on Windows. It was expanding a linux path which prepended a drive letter onto it. ## 0.5.2 (August 3, 2010) - `vagrant up` can be used as a way to resume the VM as well (same as `vagrant resume`). [GH-134] - Sudo uses "-E" flag to preserve environment for chef provisioners. This fixes issues with CentOS. [GH-133] - Added "IdentitiesOnly yes" to options when `vagrant ssh` is run to avoid "Too Many Authentication Failures" error. [GH-131] - Fix regression with `package` not working. [GH-132] - Added ability to specify box url in `init`, which populates the Vagrantfile with the proper `config.vm.box_url`. ## 0.5.1 (July 31, 2010) - Allow specifying cookbook paths which exist only on the VM in `config.chef.cookbooks_path`. This is used for specifying cookbook paths when `config.chef.recipe_url` is used. [GH-130] See updated chef solo documentation for more information on this. - No longer show "Disabling host only networks..." if no host only networks are destroyed. Quiets `destroy`, `halt`, etc output a bit. - Updated getting started guide to be more up to date and generic. [GH-125] - Fixed error with doing a `vagrant up` when no Vagrantfile existed. [GH-128] - Fixed NFS erroring when NFS wasn't even enabled if `/etc/exports` doesn't exist. [GH-126] - Fixed `vagrant resume` to properly resume a suspended VM. [GH-122] - Fixed `halt`, `destroy`, `reload` to where they failed if the VM was in a saved state. [GH-123] - Added `config.chef.recipe_url` which allows you to specify a URL to a gzipped tar file for chef solo to download cookbooks. See the [chef-solo docs](https://docs.chef.io/chef_solo.html) for more information. [GH-121] - Added `vagrant box repackage` which repackages boxes which have been added. This is useful in case you want to redistribute a base box you have but may have lost the actual "box" file. [GH-120] ## Previous The changelog began with version 0.5.1 so any changes prior to that can be seen by checking the tagged releases and reading git commit messages. vagrant-2.0.2/Gemfile000066400000000000000000000003531323370221500144470ustar00rootroot00000000000000source "https://rubygems.org" gemspec if File.exist?(File.expand_path("../../vagrant-spec", __FILE__)) gem 'vagrant-spec', path: "../vagrant-spec" else gem 'vagrant-spec', git: "https://github.com/mitchellh/vagrant-spec.git" end vagrant-2.0.2/LICENSE000066400000000000000000000020741323370221500141630ustar00rootroot00000000000000The MIT License Copyright (c) 2010-2018 Mitchell Hashimoto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. vagrant-2.0.2/README.md000066400000000000000000000064031323370221500144350ustar00rootroot00000000000000# Vagrant * Website: [https://www.vagrantup.com/](https://www.vagrantup.com/) * Source: [https://github.com/hashicorp/vagrant](https://github.com/hashicorp/vagrant) * [![Gitter chat](https://badges.gitter.im/mitchellh/vagrant.png)](https://gitter.im/mitchellh/vagrant) * Mailing list: [Google Groups](https://groups.google.com/group/vagrant-up) Vagrant is a tool for building and distributing development environments. Development environments managed by Vagrant can run on local virtualized platforms such as VirtualBox or VMware, in the cloud via AWS or OpenStack, or in containers such as with Docker or raw LXC. Vagrant provides the framework and configuration format to create and manage complete portable development environments. These development environments can live on your computer or in the cloud, and are portable between Windows, Mac OS X, and Linux. ## Quick Start For the quick-start, we'll bring up a development machine on [VirtualBox](https://www.virtualbox.org/) because it is free and works on all major platforms. Vagrant can, however, work with almost any system such as [OpenStack](https://www.openstack.org/), [VMware](https://www.vmware.com/), [Docker](https://docs.docker.com/), etc. First, make sure your development machine has [VirtualBox](https://www.virtualbox.org/) installed. After this, [download and install the appropriate Vagrant package for your OS](https://www.vagrantup.com/downloads.html). To build your first virtual environment: vagrant init hashicorp/precise32 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 the box doesn't already exist on your system. ## Getting Started Guide To learn how to build a fully functional development environment, follow the [getting started guide](https://www.vagrantup.com/docs/getting-started/index.html). ## Installing the Gem from Git If you want the bleeding edge version of Vagrant, we try to keep master pretty stable and you're welcome to give it a shot. Please review the installation page [here](https://www.vagrantup.com/docs/installation/source.html). ## Contributing to Vagrant To install Vagrant from source, please [follow the guide in the Wiki](https://github.com/hashicorp/vagrant/wiki/Installing-Vagrant-from-Source). You can run the test suite with: bundle exec rake This will run the unit test suite, which should come back all green! Then you're good to go! If you want to run Vagrant without having to install the gem, you may use `bundle exec`, like so: bundle exec vagrant help ### Acceptance Tests Vagrant also comes with an acceptance test suite that does black-box tests of various Vagrant components. Note that these tests are **extremely slow** because actual VMs are spun up and down. The full test suite can take hours. Instead, try to run focused component tests. To run the acceptance test suite, first copy `vagrant-spec.config.example.rb` to `vagrant-spec.config.rb` and modify it to valid values. The places you should fill in are clearly marked. Next, see the components that can be tested: ``` $ rake acceptance:components cli provider/virtualbox/basic ... ``` Then, run one of those components: ``` $ rake acceptance:run COMPONENTS="cli" ... ``` vagrant-2.0.2/RELEASE.md000066400000000000000000000027331323370221500145620ustar00rootroot00000000000000# Releasing Vagrant This documents how to release Vagrant. Various steps in this document will require privileged access to private systems, so this document is only targetted at Vagrant core members who have the ability to cut a release. 1. Update `version.txt` to the version you want to release. 1. Update `CHANGELOG.md` to have a header with the release version and date. 1. Commit those changes and also tag the release with the version: ``` $ git tag vX.Y.Z $ git push --tags ``` 1. Trigger an installer creation run within the HashiCorp Bamboo installation. This will take around 45 minutes. 1. Download all the resulting artifacts into the `pkg/dist` folder relative to the Vagrant repository on your local machine. 1. Run `./scripts/sign.sh` with the version that is being created. This must be run from the Vagrant repo root. This will GPG sign and checksum the files. 1. Run the following command to upload the binaries to the releases site: ``` $ hc-releases upload pkg/dist ``` 1. Publish the new index files to the releases site: ``` $ hc-releases publish ``` 1. Update `website/config.rb` to point to the latest version, commit, and push. 1. Tell HashiBot to deploy in ``#deploys` ``` hashibot deploy vagrant ``` 1. Update `version.txt` to append `.dev` and add a new blank entry in the CHANGELOG, commit, and push. 1. Update [Checkpoint](https://checkpoint.hashicorp.com/control) with the new version. vagrant-2.0.2/Rakefile000066400000000000000000000010471323370221500146220ustar00rootroot00000000000000require 'rubygems' require 'bundler/setup' # Immediately sync all stdout so that tools like buildbot can # immediately load in the output. $stdout.sync = true $stderr.sync = true # Load all the rake tasks from the "tasks" folder. This folder # allows us to nicely separate rake tasks into individual files # based on their role, which makes development and debugging easier # than one monolithic file. task_dir = File.expand_path("../tasks", __FILE__) Dir["#{task_dir}/**/*.rake"].each do |task_file| load task_file end task default: "test:unit" vagrant-2.0.2/Vagrantfile000066400000000000000000000051271323370221500153450ustar00rootroot00000000000000# This Vagrantfile can be used to develop Vagrant. Note that VirtualBox # doesn't run in VirtualBox so you can't actually _run_ Vagrant within # the VM created by this Vagrantfile, but you can use it to develop the # Ruby, run unit tests, etc. Vagrant.configure("2") do |config| config.vm.box = "hashicorp/precise64" config.vm.hostname = "vagrant" config.ssh.shell = "bash -c 'BASH_ENV=/etc/profile exec bash'" ["vmware_fusion", "vmware_workstation", "virtualbox"].each do |provider| config.vm.provider provider do |v, override| v.memory = "1024" end end config.vm.provision "shell", inline: $shell config.push.define "www", strategy: "local-exec" do |push| push.script = "scripts/website_push_www.sh" end config.push.define "docs", strategy: "local-exec" do |push| push.script = "scripts/website_push_docs.sh" end end $shell = <<-'CONTENTS' 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 # Update apt apt-get update --quiet # Install basic dependencies apt-get install -qy build-essential bsdtar curl # Import the mpapis public key to verify downloaded releases su -l -c 'gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3' vagrant # Install RVM su -l -c 'curl -sL https://get.rvm.io | bash -s stable' vagrant # Add the vagrant user to the RVM group #usermod -a -G rvm vagrant # Install latest Ruby that complies with Vagrant's version constraint RUBY_VER_LATEST=$(su -l -c 'rvm list known' vagrant | tr '[]-' ' ' | awk "/^ ruby ${RUBY_VER_REQ:0:1}\./ { print \$2 }" | sort | tail -n1) su -l -c "rvm install ${RUBY_VER_LATEST}" vagrant su -l -c "rvm --default use ${RUBY_VER_LATEST}" vagrant # Output the Ruby version (for sanity) su -l -c 'ruby --version' vagrant # Install Git apt-get install -qy git # Upgrade Rubygems su -l -c "rvm ${RUBY_VER_LATEST} do gem update --system" vagrant # Install bundler and prepare to run unit tests su -l -c "gem install bundler -v ${BUNDLER_VER_REQ}" vagrant su -l -c 'cd /vagrant; bundle install' vagrant # Automatically move into the shared folder, but only add the command # if it's not already there. grep -q 'cd /vagrant' /home/vagrant/.bash_profile || echo 'cd /vagrant' >> /home/vagrant/.bash_profile # Touch the marker file so we don't do this again touch ${MARKER_FILE} CONTENTS vagrant-2.0.2/bin/000077500000000000000000000000001323370221500137235ustar00rootroot00000000000000vagrant-2.0.2/bin/vagrant000077500000000000000000000125561323370221500153240ustar00rootroot00000000000000#!/usr/bin/env ruby # Trap interrupts to quit cleanly. This will be overridden at some point # by Vagrant. This is made to catch any interrupts while Vagrant is # initializing which have historically resulted in stack traces. Signal.trap("INT") { abort } # Split arguments by "--" if its there, we'll recombine them later argv = ARGV.dup argv_extra = [] # These will be the options that are passed to initialze the Vagrant # environment. opts = {} if idx = argv.index("--") argv_extra = argv.slice(idx+1, argv.length-2) argv = argv.slice(0, idx) end # Fast path the version of Vagrant if argv.include?("-v") || argv.include?("--version") require_relative "../lib/vagrant/version" puts "Vagrant #{Vagrant::VERSION}" exit 0 end # Disable plugin loading for commands where plugins are not required. This will # also disable loading of the Vagrantfile if it available as the environment # is not required for these commands argv.each_index do |i| arg = argv[i] if !arg.start_with?("-") if ["plugin", "help"].include?(arg) || (arg == "box" && argv[i+1] == "list") opts[:vagrantfile_name] = "" ENV['VAGRANT_NO_PLUGINS'] = "1" end if arg == "plugin" && argv[i+1] != "list" ENV['VAGRANT_DISABLE_PLUGIN_INIT'] = "1" end break end end # Set logging level to `debug`. This is done before loading 'vagrant', as it # sets up the logging system. if argv.include?("--debug") argv.delete("--debug") ENV["VAGRANT_LOG"] = "debug" end # Enable log timestamps if requested if argv.include?("--timestamp") argv.delete("--timestamp") ENV["VAGRANT_LOG_TIMESTAMP"] = "1" end # Convenience flag to enable debug with timestamps if argv.include?("--debug-timestamp") argv.delete("--debug-timestamp") ENV["VAGRANT_LOG"] = "debug" ENV["VAGRANT_LOG_TIMESTAMP"] = "1" end # Stdout/stderr should not buffer output $stdout.sync = true $stderr.sync = true env = nil begin require 'log4r' require 'vagrant' require 'vagrant/bundler' require 'vagrant/cli' require 'vagrant/util/platform' # Schedule the cleanup of things at_exit(&Vagrant::Bundler.instance.method(:deinit)) # Create a logger right away logger = Log4r::Logger.new("vagrant::bin::vagrant") logger.info("`vagrant` invoked: #{ARGV.inspect}") # Disable color in a few cases: # # * --no-color is anywhere in our arguments # * STDOUT is not a TTY # * The terminal doesn't support colors (Windows) # if argv.include?("--no-color") || ENV["VAGRANT_NO_COLOR"] # Delete the argument from the list so that it doesn't # cause any invalid arguments down the road. argv.delete("--no-color") opts[:ui_class] = Vagrant::UI::Basic elsif !Vagrant::Util::Platform.terminal_supports_colors? opts[:ui_class] = Vagrant::UI::Basic elsif !$stdout.tty? && !Vagrant::Util::Platform.cygwin? # Cygwin always reports STDOUT is not a TTY, so we only disable # colors if its not a TTY AND its not Cygwin. opts[:ui_class] = Vagrant::UI::Basic end # Also allow users to force colors. if argv.include?("--color") || ENV["VAGRANT_FORCE_COLOR"] argv.delete("--color") opts[:ui_class] = Vagrant::UI::Colored end # Highest precedence is if we have enabled machine-readable output if argv.include?("--machine-readable") argv.delete("--machine-readable") opts[:ui_class] = Vagrant::UI::MachineReadable end # Default to colored output opts[:ui_class] ||= Vagrant::UI::Colored # Recombine the arguments if !argv_extra.empty? argv << "--" argv += argv_extra end # Create the environment, which is the cwd of wherever the # `vagrant` command was invoked from logger.debug("Creating Vagrant environment") env = Vagrant::Environment.new(opts) # If we are running with the Windows Subsystem for Linux do # some extra setup to allow access to Vagrant managed machines # outside the subsystem if Vagrant::Util::Platform.wsl? recreate_env = Vagrant::Util::Platform.wsl_init(env, logger) if recreate_env logger.info("Re-creating Vagrant environment due to WSL modifications.") env = Vagrant::Environment.new(opts) end end if !Vagrant.in_installer? && !Vagrant.very_quiet? # If we're not in the installer, warn. env.ui.warn(I18n.t("vagrant.general.not_in_installer") + "\n", prefix: false) end begin # Execute the CLI interface, and exit with the proper error code exit_status = env.cli(argv) ensure # Unload the environment so cleanup can be done env.unload end # Exit with the exit status from our CLI command exit(exit_status) rescue Exception => e # It is possible for errors to happen in Vagrant's initialization. In # this case, we don't have access to this class yet, so we check for it. raise if !defined?(Vagrant) || !defined?(Vagrant::Errors) raise if !e.is_a?(Vagrant::Errors::VagrantError) require 'log4r' logger = Log4r::Logger.new("vagrant::bin::vagrant") logger.error("Vagrant experienced an error! Details:") logger.error(e.inspect) logger.error(e.message) logger.error(e.backtrace.join("\n")) if env opts = { prefix: false } env.ui.error e.message, opts if e.message env.ui.machine("error-exit", e.class.to_s, e.message.to_s) else $stderr.puts "Vagrant failed to initialize at a very early stage:\n\n" $stderr.puts e.message end exit e.status_code if e.respond_to?(:status_code) exit 255 # An error occurred with no status code defined end vagrant-2.0.2/contrib/000077500000000000000000000000001323370221500146135ustar00rootroot00000000000000vagrant-2.0.2/contrib/README.md000066400000000000000000000013411323370221500160710ustar00rootroot00000000000000# Vagrant Contrib Miscellaneous contributions which assist people in using Vagrant will make their way into this directory. An up-to-date list of short descriptions for each item will be kept below. ## List of Contrib Items * `bash` - Contains a bash script for improving autocompletion with bash. * `emacs` - Contains a file for enabling Ruby syntax highlighting for `Vagrantfile`s in `emacs`. * `st` - Contains a `.sublime-settings` file for enabling Ruby syntax highlighting for `Vagrantfile`s in Sublime Text. * `sudoers` - Contains the pieces of `/etc/sudoers` configuration to avoid password entry when starting machines. * `vim` - Contains a `.vim` file for enabling Ruby syntax highlighting for `Vagrantfile`s in `vim`. vagrant-2.0.2/contrib/bash/000077500000000000000000000000001323370221500155305ustar00rootroot00000000000000vagrant-2.0.2/contrib/bash/completion.sh000066400000000000000000000122051323370221500202350ustar00rootroot00000000000000#!/bin/bash # (The MIT License) # # Copyright (c) 2014 Kura # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the 'Software'), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. __pwdln() { pwdmod="${PWD}/" itr=0 until [[ -z "$pwdmod" ]];do itr=$(($itr+1)) pwdmod="${pwdmod#*/}" done echo -n $(($itr-1)) } __vagrantinvestigate() { if [ -f "${PWD}/.vagrant" -o -d "${PWD}/.vagrant" ];then echo "${PWD}/.vagrant" return 0 else pwdmod2="${PWD}" for (( i=2; i<=$(__pwdln); i++ ));do pwdmod2="${pwdmod2%/*}" if [ -f "${pwdmod2}/.vagrant" -o -d "${pwdmod2}/.vagrant" ];then echo "${pwdmod2}/.vagrant" return 0 fi done fi return 1 } _vagrant() { cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" commands="box connect destroy docker-exec docker-logs docker-run global-status halt help init list-commands login package plugin provision push rdp reload resume rsync rsync-auto share snapshot ssh ssh-config status suspend up version" if [ $COMP_CWORD == 1 ] then COMPREPLY=($(compgen -W "${commands}" -- ${cur})) return 0 fi if [ $COMP_CWORD == 2 ] then case "$prev" in "init") local box_list=$(find "${VAGRANT_HOME:-${HOME}/.vagrant.d}/boxes" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) COMPREPLY=($(compgen -W "${box_list}" -- ${cur})) return 0 ;; "up") vagrant_state_file=$(__vagrantinvestigate) || return 1 if [[ -d "${vagrant_state_file}" ]] then local vm_list=$(find "${vagrant_state_file}/machines" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) fi local up_commands="--no-provision" COMPREPLY=($(compgen -W "${up_commands} ${vm_list}" -- ${cur})) return 0 ;; "ssh"|"provision"|"reload"|"halt"|"suspend"|"resume"|"ssh-config") vagrant_state_file=$(__vagrantinvestigate) || return 1 if [[ -f "${vagrant_state_file}" ]] then running_vm_list=$(grep 'active' "${vagrant_state_file}" | sed -e 's/"active"://' | tr ',' '\n' | cut -d '"' -f 2 | tr '\n' ' ') else running_vm_list=$(find "${vagrant_state_file}" -type f -name "id" | awk -F"/" '{print $(NF-2)}') fi COMPREPLY=($(compgen -W "${running_vm_list}" -- ${cur})) return 0 ;; "box") box_commands="add help list outdated prune remove repackage update" COMPREPLY=($(compgen -W "${box_commands}" -- ${cur})) return 0 ;; "plugin") plugin_commands="install license list uninstall update" COMPREPLY=($(compgen -W "${plugin_commands}" -- ${cur})) return 0 ;; "help") COMPREPLY=($(compgen -W "${commands}" -- ${cur})) return 0 ;; "snapshot") snapshot_commands="delete list pop push restore save" COMPREPLY=($(compgen -W "${snapshot_commands}" -- ${cur})) return 0 ;; *) ;; esac fi if [ $COMP_CWORD == 3 ] then action="${COMP_WORDS[COMP_CWORD-2]}" case "$action" in "up") if [ "$prev" == "--no-provision" ] then if [[ -d "${vagrant_state_file}" ]] then local vm_list=$(find "${vagrant_state_file}/machines" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) fi COMPREPLY=($(compgen -W "${vm_list}" -- ${cur})) return 0 fi ;; "box") case "$prev" in "remove"|"repackage") local box_list=$(find "${VAGRANT_HOME:-${HOME}/.vagrant.d}/boxes" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) COMPREPLY=($(compgen -W "${box_list}" -- ${cur})) return 0 ;; *) ;; esac ;; *) ;; esac fi } complete -F _vagrant vagrant vagrant-2.0.2/contrib/emacs/000077500000000000000000000000001323370221500157035ustar00rootroot00000000000000vagrant-2.0.2/contrib/emacs/vagrant.el000066400000000000000000000006441323370221500176730ustar00rootroot00000000000000;;-------------------------------------------------------------------- ;; Teach emacs to syntax highlight Vagrantfile as Ruby. ;; ;; Installation: Copy the line below into your emacs configuration, ;; or drop this file anywhere in your "~/.emacs.d" directory and be ;; sure to "load" it. ;;-------------------------------------------------------------------- (add-to-list 'auto-mode-alist '("Vagrantfile$" . ruby-mode)) vagrant-2.0.2/contrib/st/000077500000000000000000000000001323370221500152415ustar00rootroot00000000000000vagrant-2.0.2/contrib/st/Ruby.sublime-settings000066400000000000000000000000511323370221500213760ustar00rootroot00000000000000{ "extensions": [ "Vagrantfile" ] } vagrant-2.0.2/contrib/sudoers/000077500000000000000000000000001323370221500162775ustar00rootroot00000000000000vagrant-2.0.2/contrib/sudoers/linux-fedora000066400000000000000000000011071323370221500206160ustar00rootroot00000000000000Cmnd_Alias VAGRANT_EXPORTS_ADD = /usr/bin/tee -a /etc/exports Cmnd_Alias VAGRANT_NFSD_CHECK = /usr/bin/systemctl status --no-pager nfs-server.service Cmnd_Alias VAGRANT_NFSD_START = /usr/bin/systemctl start nfs-server.service Cmnd_Alias VAGRANT_NFSD_APPLY = /usr/sbin/exportfs -ar Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /bin/sed -r -e * d -ibak /*/exports Cmnd_Alias VAGRANT_EXPORTS_REMOVE_2 = /bin/cp /*/exports /etc/exports %vagrant ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD_CHECK, VAGRANT_NFSD_START, VAGRANT_NFSD_APPLY, VAGRANT_EXPORTS_REMOVE, VAGRANT_EXPORTS_REMOVE_2 vagrant-2.0.2/contrib/sudoers/linux-suse000066400000000000000000000006351323370221500203420ustar00rootroot00000000000000Cmnd_Alias VAGRANT_CHOWN = /usr/bin/chown 0\:0 /tmp/vagrant[a-z0-9-]* Cmnd_Alias VAGRANT_MV = /usr/bin/mv -f /tmp/vagrant[a-z0-9-]* /etc/exports Cmnd_Alias VAGRANT_START = /sbin/service nfsserver start Cmnd_Alias VAGRANT_STATUS = /sbin/service nfsserver status Cmnd_Alias VAGRANT_APPLY = /usr/sbin/exportfs -ar %vagrant ALL=(root) NOPASSWD: VAGRANT_CHOWN, VAGRANT_MV, VAGRANT_START, VAGRANT_STATUS, VAGRANT_APPLY vagrant-2.0.2/contrib/sudoers/linux-ubuntu000066400000000000000000000010071323370221500206770ustar00rootroot00000000000000# These work with Ubuntu - they might need tweaking for other distributions Cmnd_Alias VAGRANT_EXPORTS_CHOWN = /bin/chown 0\:0 /tmp/* Cmnd_Alias VAGRANT_EXPORTS_MV = /bin/mv -f /tmp/* /etc/exports Cmnd_Alias VAGRANT_NFSD_CHECK = /etc/init.d/nfs-kernel-server status Cmnd_Alias VAGRANT_NFSD_START = /etc/init.d/nfs-kernel-server start Cmnd_Alias VAGRANT_NFSD_APPLY = /usr/sbin/exportfs -ar %sudo ALL=(root) NOPASSWD: VAGRANT_EXPORTS_CHOWN, VAGRANT_EXPORTS_MV, VAGRANT_NFSD_CHECK, VAGRANT_NFSD_START, VAGRANT_NFSD_APPLY vagrant-2.0.2/contrib/sudoers/osx000066400000000000000000000014731323370221500170400ustar00rootroot00000000000000Cmnd_Alias VAGRANT_EXPORTS_ADD = /usr/bin/tee -a /etc/exports Cmnd_Alias VAGRANT_NFSD = /sbin/nfsd restart Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /usr/bin/sed -E -e /*/ d -ibak /etc/exports Cmnd_Alias VAGRANT_SMB_ADD = /usr/sbin/sharing -a * -S * -s * -g * -n * Cmnd_Alias VAGRANT_SMB_REMOVE = /usr/sbin/sharing -r * Cmnd_Alias VAGRANT_SMB_LIST = /usr/sbin/sharing -l Cmnd_Alias VAGRANT_SMB_PLOAD = /bin/launchctl load -w /System/Library/LaunchDaemons/com.apple.smb.preferences.plist Cmnd_Alias VAGRANT_SMB_DLOAD = /bin/launchctl load -w /System/Library/LaunchDaemons/com.apple.smbd.plist Cmnd_Alias VAGRANT_SMB_DSTART = /bin/launchctl start com.apple.smbd %admin ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD, VAGRANT_SMB_ADD, VAGRANT_SMB_REMOVE, VAGRANT_SMB_LIST, VAGRANT_SMB_PLOAD, VAGRANT_SMB_DLOAD VAGRANT_SMB_DSTARTvagrant-2.0.2/contrib/vim/000077500000000000000000000000001323370221500154065ustar00rootroot00000000000000vagrant-2.0.2/contrib/vim/vagrantfile.vim000066400000000000000000000003431323370221500204250ustar00rootroot00000000000000" Teach vim to syntax highlight Vagrantfile as ruby " " Install: $HOME/.vim/plugin/vagrant.vim " Author: Brandon Philips augroup vagrant au! au BufRead,BufNewFile Vagrantfile set filetype=ruby augroup END vagrant-2.0.2/keys/000077500000000000000000000000001323370221500141265ustar00rootroot00000000000000vagrant-2.0.2/keys/README.md000066400000000000000000000007151323370221500154100ustar00rootroot00000000000000# Insecure Keypair These keys are the "insecure" public/private keypair we offer to [base box creators](https://www.vagrantup.com/docs/boxes/base.html) for use in their base boxes so that vagrant installations can automatically SSH into the boxes. If you're working with a team or company or with a custom box and you want more secure SSH, you should create your own keypair and configure the private key in the Vagrantfile with `config.ssh.private_key_path` vagrant-2.0.2/keys/vagrant000066400000000000000000000032131323370221500155120ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzI w+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoP kcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2 hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NO Td0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcW yLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQIBIwKCAQEA4iqWPJXtzZA68mKd ELs4jJsdyky+ewdZeNds5tjcnHU5zUYE25K+ffJED9qUWICcLZDc81TGWjHyAqD1 Bw7XpgUwFgeUJwUlzQurAv+/ySnxiwuaGJfhFM1CaQHzfXphgVml+fZUvnJUTvzf TK2Lg6EdbUE9TarUlBf/xPfuEhMSlIE5keb/Zz3/LUlRg8yDqz5w+QWVJ4utnKnK iqwZN0mwpwU7YSyJhlT4YV1F3n4YjLswM5wJs2oqm0jssQu/BT0tyEXNDYBLEF4A sClaWuSJ2kjq7KhrrYXzagqhnSei9ODYFShJu8UWVec3Ihb5ZXlzO6vdNQ1J9Xsf 4m+2ywKBgQD6qFxx/Rv9CNN96l/4rb14HKirC2o/orApiHmHDsURs5rUKDx0f9iP cXN7S1uePXuJRK/5hsubaOCx3Owd2u9gD6Oq0CsMkE4CUSiJcYrMANtx54cGH7Rk EjFZxK8xAv1ldELEyxrFqkbE4BKd8QOt414qjvTGyAK+OLD3M2QdCQKBgQDtx8pN CAxR7yhHbIWT1AH66+XWN8bXq7l3RO/ukeaci98JfkbkxURZhtxV/HHuvUhnPLdX 3TwygPBYZFNo4pzVEhzWoTtnEtrFueKxyc3+LjZpuo+mBlQ6ORtfgkr9gBVphXZG YEzkCD3lVdl8L4cw9BVpKrJCs1c5taGjDgdInQKBgHm/fVvv96bJxc9x1tffXAcj 3OVdUN0UgXNCSaf/3A/phbeBQe9xS+3mpc4r6qvx+iy69mNBeNZ0xOitIjpjBo2+ dBEjSBwLk5q5tJqHmy/jKMJL4n9ROlx93XS+njxgibTvU6Fp9w+NOFD/HvxB3Tcz 6+jJF85D5BNAG3DBMKBjAoGBAOAxZvgsKN+JuENXsST7F89Tck2iTcQIT8g5rwWC P9Vt74yboe2kDT531w8+egz7nAmRBKNM751U/95P9t88EDacDI/Z2OwnuFQHCPDF llYOUI+SpLJ6/vURRbHSnnn8a/XG+nzedGH5JGqEJNQsz+xT2axM0/W/CRknmGaJ kda/AoGANWrLCz708y7VYgAtW2Uf1DPOIYMdvo6fxIB5i9ZfISgcJ/bbCUkFrhoH +vq/5CIWxCPp0f85R4qxxQ5ihxJ0YDQT9Jpx4TMss4PSavPaBH3RXow5Ohe+bYoQ NE5OgEXk2wVfZczCZpigBKbKZHNYcelXtTt/nP3rsCuGcM4h53s= -----END RSA PRIVATE KEY----- vagrant-2.0.2/keys/vagrant.pub000066400000000000000000000006311323370221500163000ustar00rootroot00000000000000ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key vagrant-2.0.2/lib/000077500000000000000000000000001323370221500137215ustar00rootroot00000000000000vagrant-2.0.2/lib/vagrant.rb000066400000000000000000000315441323370221500157170ustar00rootroot00000000000000require "vagrant/shared_helpers" require "rubygems" require "log4r" require "vagrant/util" # Enable logging if it is requested. We do this before # anything else so that we can setup the output before # any logging occurs. if ENV["VAGRANT_LOG"] && ENV["VAGRANT_LOG"] != "" # Require Log4r and define the levels we'll be using require 'log4r/config' Log4r.define_levels(*Log4r::Log4rConfig::LogLevels) level = nil begin level = Log4r.const_get(ENV["VAGRANT_LOG"].upcase) rescue NameError # This means that the logging constant wasn't found, # which is fine. We just keep `level` as `nil`. But # we tell the user. level = nil end # Some constants, such as "true" resolve to booleans, so the # above error checking doesn't catch it. This will check to make # sure that the log level is an integer, as Log4r requires. level = nil if !level.is_a?(Integer) if !level # We directly write to stderr here because the VagrantError system # is not setup yet. $stderr.puts "Invalid VAGRANT_LOG level is set: #{ENV["VAGRANT_LOG"]}" $stderr.puts "" $stderr.puts "Please use one of the standard log levels: debug, info, warn, or error" exit 1 end # Set the logging level on all "vagrant" namespaced # logs as long as we have a valid level. if level logger = Log4r::Logger.new("vagrant") logger.outputters = Log4r::Outputter.stderr logger.level = level base_formatter = Log4r::BasicFormatter.new if ENV["VAGRANT_LOG_TIMESTAMP"] base_formatter = Log4r::PatternFormatter.new( pattern: "%d [%5l] %m", date_pattern: "%F %T" ) end Log4r::Outputter.stderr.formatter = Vagrant::Util::LoggingFormatter.new(base_formatter) logger = nil end end require 'json' require 'pathname' require 'stringio' require 'childprocess' require 'i18n' # OpenSSL must be loaded here since when it is loaded via `autoload` # there are issues with ciphers not being properly loaded. require 'openssl' # Always make the version available require 'vagrant/version' global_logger = Log4r::Logger.new("vagrant::global") global_logger.info("Vagrant version: #{Vagrant::VERSION}") global_logger.info("Ruby version: #{RUBY_VERSION}") global_logger.info("RubyGems version: #{Gem::VERSION}") ENV.each do |k, v| next if k.start_with?("VAGRANT_OLD") global_logger.info("#{k}=#{v.inspect}") if k.start_with?("VAGRANT_") end # We need these components always so instead of an autoload we # just require them explicitly here. require "vagrant/plugin" require "vagrant/registry" module Vagrant autoload :Action, 'vagrant/action' autoload :BatchAction, 'vagrant/batch_action' autoload :Box, 'vagrant/box' autoload :BoxCollection, 'vagrant/box_collection' autoload :CLI, 'vagrant/cli' autoload :Command, 'vagrant/command' autoload :Config, 'vagrant/config' autoload :Driver, 'vagrant/driver' autoload :Environment, 'vagrant/environment' autoload :Errors, 'vagrant/errors' autoload :Guest, 'vagrant/guest' autoload :Host, 'vagrant/host' autoload :Machine, 'vagrant/machine' autoload :MachineIndex, 'vagrant/machine_index' autoload :MachineState, 'vagrant/machine_state' autoload :Plugin, 'vagrant/plugin' autoload :UI, 'vagrant/ui' autoload :Util, 'vagrant/util' # These are the various plugin versions and their components in # a lazy loaded Hash-like structure. PLUGIN_COMPONENTS = Registry.new.tap do |c| c.register(:"1") { Plugin::V1::Plugin } c.register([:"1", :command]) { Plugin::V1::Command } c.register([:"1", :communicator]) { Plugin::V1::Communicator } c.register([:"1", :config]) { Plugin::V1::Config } c.register([:"1", :guest]) { Plugin::V1::Guest } c.register([:"1", :host]) { Plugin::V1::Host } c.register([:"1", :provider]) { Plugin::V1::Provider } c.register([:"1", :provisioner]) { Plugin::V1::Provisioner } c.register(:"2") { Plugin::V2::Plugin } c.register([:"2", :command]) { Plugin::V2::Command } c.register([:"2", :communicator]) { Plugin::V2::Communicator } c.register([:"2", :config]) { Plugin::V2::Config } c.register([:"2", :guest]) { Plugin::V2::Guest } c.register([:"2", :host]) { Plugin::V2::Host } c.register([:"2", :provider]) { Plugin::V2::Provider } c.register([:"2", :provisioner]) { Plugin::V2::Provisioner } c.register([:"2", :push]) { Plugin::V2::Push } c.register([:"2", :synced_folder]) { Plugin::V2::SyncedFolder } end # Configure a Vagrant environment. The version specifies the version # of the configuration that is expected by the block. The block, based # on that version, configures the environment. # # Note that the block isn't run immediately. Instead, the configuration # block is stored until later, and is run when an environment is loaded. # # @param [String] version Version of the configuration def self.configure(version, &block) Config.run(version, &block) end # This checks if a plugin with the given name is available (installed # and enabled). This can be used from the Vagrantfile to easily branch # based on plugin availability. def self.has_plugin?(name, version=nil) return false unless Vagrant.plugins_enabled? if !version # We check the plugin names first because those are cheaper to check return true if plugin("2").manager.registered.any? { |p| p.name == name } end # Make the requirement object version = Gem::Requirement.new([version]) if version # Now check the plugin gem names require "vagrant/plugin/manager" Plugin::Manager.instance.installed_specs.any? do |s| match = s.name == name next match if !version next match && version.satisfied_by?(s.version) end end # Returns a superclass to use when creating a plugin for Vagrant. # Given a specific version, this returns a proper superclass to use # to register plugins for that version. # # Optionally, if you give a specific component, then it will return # the proper superclass for that component as well. # # Plugins and plugin components should subclass the classes returned by # this method. This method lets Vagrant core control these superclasses # and change them over time without affecting plugins. For example, if # the V1 superclass happens to be "Vagrant::V1," future versions of # Vagrant may move it to "Vagrant::Plugins::V1" and plugins will not be # affected. # # @param [String] version # @param [String] component # @return [Class] def self.plugin(version, component=nil) # Build up the key and return a result key = version.to_s.to_sym key = [key, component.to_s.to_sym] if component result = PLUGIN_COMPONENTS.get(key) # If we found our component then we return that return result if result # If we didn't find a result, then raise an exception, depending # on if we got a component or not. raise ArgumentError, "Plugin superclass not found for version/component: " + "#{version} #{component}" end # @deprecated def self.require_plugin(name) puts "Vagrant.require_plugin is deprecated and has no effect any longer." puts "Use `vagrant plugin` commands to manage plugins. This warning will" puts "be removed in the next version of Vagrant." end # This allows a Vagrantfile to specify the version of Vagrant that is # required. You can specify a list of requirements which will all be checked # against the running Vagrant version. # # This should be specified at the _top_ of any Vagrantfile. # # Examples are shown below: # # Vagrant.require_version(">= 1.3.5") # Vagrant.require_version(">= 1.3.5", "< 1.4.0") # Vagrant.require_version("~> 1.3.5") # def self.require_version(*requirements) logger = Log4r::Logger.new("vagrant::root") logger.info("Version requirements from Vagrantfile: #{requirements.inspect}") req = Gem::Requirement.new(*requirements) if req.satisfied_by?(Gem::Version.new(VERSION)) logger.info(" - Version requirements satisfied!") return end raise Errors::VagrantVersionBad, requirements: requirements.join(", "), version: VERSION end # This allows plugin developers to access the original environment before # Vagrant even ran. This is useful when shelling out, especially to other # Ruby processes. # # @return [Hash] def self.original_env {}.tap do |h| ENV.each do |k,v| if k.start_with?("VAGRANT_OLD_ENV") key = k.sub(/^VAGRANT_OLD_ENV_/, "") if !key.empty? h[key] = v end end end end end end # Default I18n to load the en locale I18n.load_path << File.expand_path("templates/locales/en.yml", Vagrant.source_root) if I18n.config.respond_to?(:enforce_available_locales=) # Make sure only available locales are used. This will be the default in the # future but we need this to silence a deprecation warning from 0.6.9 I18n.config.enforce_available_locales = true end if Vagrant.enable_resolv_replace global_logger.info("resolv replacement has been enabled!") else global_logger.warn("resolv replacement has not been enabled!") end # Setup the plugin manager and load any defined plugins require_relative "vagrant/plugin/manager" plugins = Vagrant::Plugin::Manager.instance.installed_plugins global_logger.info("Plugins:") plugins.each do |plugin_name, plugin_info| installed_version = plugin_info["installed_gem_version"] version_constraint = plugin_info["gem_version"] installed_version = 'undefined' if installed_version.to_s.empty? version_constraint = '> 0' if version_constraint.to_s.empty? global_logger.info( " - #{plugin_name} = [installed: " \ "#{installed_version} constraint: " \ "#{version_constraint}]" ) end if Vagrant.plugins_init? begin Vagrant::Bundler.instance.init!(plugins) rescue Exception => e global_logger.error("Plugin initialization error - #{e.class}: #{e}") e.backtrace.each do |backtrace_line| global_logger.debug(backtrace_line) end raise Vagrant::Errors::PluginInitError, message: e.to_s end end # A lambda that knows how to load plugins from a single directory. plugin_load_proc = lambda do |directory| # We only care about directories next false if !directory.directory? # If there is a plugin file in the top-level directory, then load # that up. plugin_file = directory.join("plugin.rb") if plugin_file.file? global_logger.debug("Loading core plugin: #{plugin_file}") load(plugin_file) next true end end # Go through the `plugins` directory and attempt to load any plugins. The # plugins are allowed to be in a directory in `plugins` or at most one # directory deep within the plugins directory. So a plugin can be at # `plugins/foo` or also at `plugins/foo/bar`, but no deeper. Vagrant.source_root.join("plugins").children(true).each do |directory| # Ignore non-directories next if !directory.directory? # Load from this directory, and exit if we successfully loaded a plugin next if plugin_load_proc.call(directory) # Otherwise, attempt to load from sub-directories directory.children(true).each(&plugin_load_proc) end # If we have plugins enabled, then load those if Vagrant.plugins_enabled? begin global_logger.info("Loading plugins!") plugins.each do |plugin_name, plugin_info| if plugin_info["require"].to_s.empty? begin global_logger.debug("Loading plugin `#{plugin_name}` with default require: `#{plugin_name}`") require plugin_name rescue LoadError, Gem::LoadError => load_error if plugin_name.include?("-") begin plugin_slash = plugin_name.gsub("-", "/") global_logger.debug("Failed to load plugin `#{plugin_name}` with default require.") global_logger.debug("Loading plugin `#{plugin_name}` with slash require: `#{plugin_slash}`") require plugin_slash rescue LoadError, Gem::LoadError global_logger.warn("Failed to load plugin `#{plugin_name}`. Assuming library and moving on.") end end end else global_logger.debug("Loading plugin `#{plugin_name}` with custom require: `#{plugin_info["require"]}`") require plugin_info["require"] end global_logger.debug("Successfully loaded plugin `#{plugin_name}`.") end if defined?(::Bundler) global_logger.debug("Bundler detected in use. Loading `:plugins` group.") ::Bundler.require(:plugins) end rescue Exception => e global_logger.error("Plugin loading error: #{e.class} - #{e}") e.backtrace.each do |backtrace_line| global_logger.debug(backtrace_line) end raise Vagrant::Errors::PluginLoadError, message: e.to_s end else global_logger.debug("Plugin loading is currently disabled.") end vagrant-2.0.2/lib/vagrant/000077500000000000000000000000001323370221500153635ustar00rootroot00000000000000vagrant-2.0.2/lib/vagrant/action.rb000066400000000000000000000060571323370221500171750ustar00rootroot00000000000000require 'vagrant/action/builder' module Vagrant module Action autoload :Runner, 'vagrant/action/runner' autoload :Warden, 'vagrant/action/warden' # Builtin contains middleware classes that are shipped with Vagrant-core # and are thus available to all plugins as a "standard library" of sorts. module Builtin autoload :BoxAdd, "vagrant/action/builtin/box_add" autoload :BoxCheckOutdated, "vagrant/action/builtin/box_check_outdated" autoload :BoxRemove, "vagrant/action/builtin/box_remove" autoload :Call, "vagrant/action/builtin/call" autoload :Confirm, "vagrant/action/builtin/confirm" autoload :ConfigValidate, "vagrant/action/builtin/config_validate" autoload :DestroyConfirm, "vagrant/action/builtin/destroy_confirm" autoload :EnvSet, "vagrant/action/builtin/env_set" autoload :GracefulHalt, "vagrant/action/builtin/graceful_halt" autoload :HandleBox, "vagrant/action/builtin/handle_box" autoload :HandleBoxUrl, "vagrant/action/builtin/handle_box_url" autoload :HandleForwardedPortCollisions, "vagrant/action/builtin/handle_forwarded_port_collisions" autoload :IsEnvSet, "vagrant/action/builtin/is_env_set" autoload :IsState, "vagrant/action/builtin/is_state" autoload :Lock, "vagrant/action/builtin/lock" autoload :Message, "vagrant/action/builtin/message" autoload :PrepareClone, "vagrant/action/builtin/prepare_clone" autoload :Provision, "vagrant/action/builtin/provision" autoload :ProvisionerCleanup, "vagrant/action/builtin/provisioner_cleanup" autoload :SetHostname, "vagrant/action/builtin/set_hostname" autoload :SSHExec, "vagrant/action/builtin/ssh_exec" autoload :SSHRun, "vagrant/action/builtin/ssh_run" autoload :SyncedFolders, "vagrant/action/builtin/synced_folders" autoload :SyncedFolderCleanup, "vagrant/action/builtin/synced_folder_cleanup" autoload :WaitForCommunicator, "vagrant/action/builtin/wait_for_communicator" end module General autoload :Package, 'vagrant/action/general/package' end # This is the action that will add a box from a URL. This middleware # sequence is built-in to Vagrant. Plugins can hook into this like any # other middleware sequence. This is particularly useful for provider # plugins, which can hook in to do things like verification of boxes # that are downloaded. def self.action_box_add Builder.new.tap do |b| b.use Builtin::BoxAdd end end # This actions checks if a box is outdated in a given Vagrant # environment for a single machine. def self.action_box_outdated Builder.new.tap do |b| b.use Builtin::BoxCheckOutdated end end # This is the action that will remove a box given a name (and optionally # a provider). This middleware sequence is built-in to Vagrant. Plugins # can hook into this like any other middleware sequence. def self.action_box_remove Builder.new.tap do |b| b.use Builtin::BoxRemove end end end end vagrant-2.0.2/lib/vagrant/action/000077500000000000000000000000001323370221500166405ustar00rootroot00000000000000vagrant-2.0.2/lib/vagrant/action/builder.rb000066400000000000000000000124621323370221500206200ustar00rootroot00000000000000module Vagrant module Action # Action builder which provides a nice DSL for building up # a middleware sequence for Vagrant actions. This code is based # heavily off of `Rack::Builder` and `ActionDispatch::MiddlewareStack` # in Rack and Rails, respectively. # # Usage # # Building an action sequence is very easy: # # app = Vagrant::Action::Builder.new.tap do |b| # b.use MiddlewareA # b.use MiddlewareB # end # # Vagrant::Action.run(app) # class Builder # This is the stack of middlewares added. This should NOT be used # directly. # # @return [Array] attr_reader :stack # This is a shortcut for a middleware sequence with only one item # in it. For a description of the arguments and the documentation, please # see {#use} instead. # # @return [Builder] def self.build(middleware, *args, &block) new.use(middleware, *args, &block) end def initialize @stack = [] end # Implement a custom copy that copies the stack variable over so that # we don't clobber that. def initialize_copy(original) super @stack = original.stack.dup end # Returns a mergeable version of the builder. If `use` is called with # the return value of this method, then the stack will merge, instead # of being treated as a separate single middleware. def flatten lambda do |env| self.call(env) end end # Adds a middleware class to the middleware stack. Any additional # args and a block, if given, are saved and passed to the initializer # of the middleware. # # @param [Class] middleware The middleware class def use(middleware, *args, &block) if middleware.kind_of?(Builder) # Merge in the other builder's stack into our own self.stack.concat(middleware.stack) else self.stack << [middleware, args, block] end self end # Inserts a middleware at the given index or directly before the # given middleware object. def insert(index, middleware, *args, &block) index = self.index(index) unless index.is_a?(Integer) raise "no such middleware to insert before: #{index.inspect}" unless index if middleware.kind_of?(Builder) middleware.stack.reverse.each do |stack_item| stack.insert(index, stack_item) end else stack.insert(index, [middleware, args, block]) end end alias_method :insert_before, :insert # Inserts a middleware after the given index or middleware object. def insert_after(index, middleware, *args, &block) index = self.index(index) unless index.is_a?(Integer) raise "no such middleware to insert after: #{index.inspect}" unless index insert(index + 1, middleware, *args, &block) end # Replaces the given middlware object or index with the new # middleware. def replace(index, middleware, *args, &block) if index.is_a?(Integer) delete(index) insert(index, middleware, *args, &block) else insert_before(index, middleware, *args, &block) delete(index) end end # Deletes the given middleware object or index def delete(index) index = self.index(index) unless index.is_a?(Integer) stack.delete_at(index) end # Runs the builder stack with the given environment. def call(env) to_app(env).call(env) end # Returns the numeric index for the given middleware object. # # @param [Object] object The item to find the index for # @return [Integer] def index(object) stack.each_with_index do |item, i| return i if item[0] == object return i if item[0].respond_to?(:name) && item[0].name == object end nil end # Converts the builder stack to a runnable action sequence. # # @param [Hash] env The action environment hash # @return [Object] A callable object def to_app(env) app_stack = nil # If we have action hooks, then we apply them if env[:action_hooks] builder = self.dup # These are the options to pass into hook application. options = {} # If we already ran through once and did append/prepends, # then don't do it again. if env[:action_hooks_already_ran] options[:no_prepend_or_append] = true end # Specify that we already ran, so in the future we don't repeat # the prepend/append hooks. env[:action_hooks_already_ran] = true # Apply all the hooks to the new builder instance env[:action_hooks].each do |hook| hook.apply(builder, options) end # The stack is now the result of the new builder app_stack = builder.stack.dup end # If we don't have a stack then default to using our own app_stack ||= stack.dup # Wrap the middleware stack with the Warden to provide a consistent # and predictable behavior upon exceptions. Warden.new(app_stack, env) end end end end vagrant-2.0.2/lib/vagrant/action/builtin/000077500000000000000000000000001323370221500203065ustar00rootroot00000000000000vagrant-2.0.2/lib/vagrant/action/builtin/box_add.rb000066400000000000000000000450451323370221500222430ustar00rootroot00000000000000require "digest/sha1" require "log4r" require "pathname" require "uri" require "vagrant/box_metadata" require "vagrant/util/downloader" require "vagrant/util/file_checksum" require "vagrant/util/platform" module Vagrant module Action module Builtin # This middleware will download a remote box and add it to the # given box collection. class BoxAdd # This is the size in bytes that if a file exceeds, is considered # to NOT be metadata. METADATA_SIZE_LIMIT = 20971520 # This is the amount of time to "resume" downloads if a partial box # file already exists. RESUME_DELAY = 24 * 60 * 60 def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::box_add") end def call(env) @download_interrupted = false unless env[:box_name].nil? begin if URI.parse(env[:box_name]).kind_of?(URI::HTTP) env[:ui].warn(I18n.t("vagrant.box_add_url_warn")) end rescue URI::InvalidURIError # do nothing end end url = Array(env[:box_url]).map do |u| u = u.gsub("\\", "/") if Util::Platform.windows? && u =~ /^[a-z]:/i # On Windows, we need to be careful about drive letters u = "file:///#{URI.escape(u)}" end if u =~ /^[a-z0-9]+:.*$/i && !u.start_with?("file://") # This is not a file URL... carry on next u end # Expand the path and try to use that, if possible p = File.expand_path(URI.unescape(u.gsub(/^file:\/\//, ""))) p = Util::Platform.cygwin_windows_path(p) next "file://#{URI.escape(p.gsub("\\", "/"))}" if File.file?(p) u end # If we received a shorthand URL ("mitchellh/precise64"), # then expand it properly. expanded = false url.each_index do |i| next if url[i] !~ /^[^\/]+\/[^\/]+$/ if !File.file?(url[i]) server = Vagrant.server_url env[:box_server_url] raise Errors::BoxServerNotSet if !server expanded = true url[i] = "#{server}/#{url[i]}" end end # Call the hook to transform URLs into authenticated URLs. # In the case we don't have a plugin that does this, then it # will just return the same URLs. hook_env = env[:hook].call( :authenticate_box_url, box_urls: url.dup) authed_urls = hook_env[:box_urls] if !authed_urls || authed_urls.length != url.length raise "Bad box authentication hook, did not generate proper results." end # Test if any of our URLs point to metadata is_metadata_results = authed_urls.map do |u| begin metadata_url?(u, env) rescue Errors::DownloaderError => e e end end if expanded && url.length == 1 is_error = is_metadata_results.find do |b| b.is_a?(Errors::DownloaderError) end if is_error raise Errors::BoxAddShortNotFound, error: is_error.extra_data[:message], name: env[:box_url], url: url end end is_metadata = is_metadata_results.any? { |b| b === true } if is_metadata && url.length > 1 raise Errors::BoxAddMetadataMultiURL, urls: url.join(", ") end if is_metadata url = [url.first, authed_urls.first] add_from_metadata(url, env, expanded) else add_direct(url, env) end @app.call(env) end # Adds a box file directly (no metadata component, versioning, # etc.) # # @param [Array] urls # @param [Hash] env def add_direct(urls, env) env[:ui].output(I18n.t("vagrant.box_adding_direct")) name = env[:box_name] if !name || name == "" raise Errors::BoxAddNameRequired end if env[:box_version] raise Errors::BoxAddDirectVersion end provider = env[:box_provider] provider = Array(provider) if provider box_add( urls, name, "0", provider, nil, env, checksum: env[:box_checksum], checksum_type: env[:box_checksum_type], ) end # Adds a box given that the URL is a metadata document. # # @param [String | Array] url The URL of the metadata for # the box to add. If this is an array, then it must be a two-element # array where the first element is the original URL and the second # element is an authenticated URL. # @param [Hash] env # @param [Bool] expanded True if the metadata URL was expanded with # a Atlas server URL. def add_from_metadata(url, env, expanded) original_url = env[:box_url] provider = env[:box_provider] provider = Array(provider) if provider version = env[:box_version] authenticated_url = url if url.is_a?(Array) # We have both a normal URL and "authenticated" URL. Split # them up. authenticated_url = url[1] url = url[0] end display_original_url = Util::CredentialScrubber.scrub(Array(original_url).first) display_url = Util::CredentialScrubber.scrub(url) env[:ui].output(I18n.t( "vagrant.box_loading_metadata", name: display_original_url)) if original_url != url env[:ui].detail(I18n.t( "vagrant.box_expanding_url", url: display_url)) end metadata = nil begin metadata_path = download( authenticated_url, env, json: true, ui: false) return if @download_interrupted File.open(metadata_path) do |f| metadata = BoxMetadata.new(f) end rescue Errors::DownloaderError => e raise if !expanded raise Errors::BoxAddShortNotFound, error: e.extra_data[:message], name: display_original_url, url: display_url ensure metadata_path.delete if metadata_path && metadata_path.file? end if env[:box_name] && metadata.name != env[:box_name] raise Errors::BoxAddNameMismatch, actual_name: metadata.name, requested_name: env[:box_name] end metadata_version = metadata.version( version || ">= 0", provider: provider) if !metadata_version if provider && !metadata.version(">= 0", provider: provider) raise Errors::BoxAddNoMatchingProvider, name: metadata.name, requested: provider, url: display_url else raise Errors::BoxAddNoMatchingVersion, constraints: version || ">= 0", name: metadata.name, url: display_url, versions: metadata.versions.join(", ") end end metadata_provider = nil if provider # If a provider was specified, make sure we get that specific # version. provider.each do |p| metadata_provider = metadata_version.provider(p) break if metadata_provider end elsif metadata_version.providers.length == 1 # If we have only one provider in the metadata, just use that # provider. metadata_provider = metadata_version.provider( metadata_version.providers.first) else providers = metadata_version.providers.sort choice = 0 options = providers.map do |p| choice += 1 "#{choice}) #{p}" end.join("\n") # We have more than one provider, ask the user what they want choice = env[:ui].ask(I18n.t( "vagrant.box_add_choose_provider", options: options) + " ", prefix: false) choice = choice.to_i if choice while !choice || choice <= 0 || choice > providers.length choice = env[:ui].ask(I18n.t( "vagrant.box_add_choose_provider_again") + " ", prefix: false) choice = choice.to_i if choice end metadata_provider = metadata_version.provider( providers[choice-1]) end provider_url = metadata_provider.url if provider_url != authenticated_url # Authenticate the provider URL since we're using auth hook_env = env[:hook].call(:authenticate_box_url, box_urls: [provider_url]) authed_urls = hook_env[:box_urls] if !authed_urls || authed_urls.length != 1 raise "Bad box authentication hook, did not generate proper results." end provider_url = authed_urls[0] end box_add( [[provider_url, metadata_provider.url]], metadata.name, metadata_version.version, metadata_provider.name, url, env, checksum: metadata_provider.checksum, checksum_type: metadata_provider.checksum_type, ) end protected # Shared helper to add a box once you know various details # about it. Shared between adding via metadata or by direct. # # @param [Array] urls # @param [String] name # @param [String] version # @param [String] provider # @param [Hash] env # @return [Box] def box_add(urls, name, version, provider, md_url, env, **opts) env[:ui].output(I18n.t( "vagrant.box_add_with_version", name: name, version: version, providers: Array(provider).join(", "))) # Verify the box we're adding doesn't already exist if provider && !env[:box_force] box = env[:box_collection].find( name, provider, version) if box raise Errors::BoxAlreadyExists, name: name, provider: provider, version: version end end # Now we have a URL, we have to download this URL. box = nil begin box_url = nil urls.each do |url| show_url = nil if url.is_a?(Array) show_url = url[1] url = url[0] end begin box_url = download(url, env, show_url: show_url) break rescue Errors::DownloaderError => e # If we don't have multiple URLs, just raise the error raise if urls.length == 1 env[:ui].error(I18n.t( "vagrant.box_download_error", message: e.message)) box_url = nil end end if opts[:checksum] && opts[:checksum_type] env[:ui].detail(I18n.t("vagrant.actions.box.add.checksumming")) validate_checksum( opts[:checksum_type], opts[:checksum], box_url) end # Add the box! box = env[:box_collection].add( box_url, name, version, force: env[:box_force], metadata_url: md_url, providers: provider) ensure # Make sure we delete the temporary file after we add it, # unless we were interrupted, in which case we keep it around # so we can resume the download later. if !@download_interrupted @logger.debug("Deleting temporary box: #{box_url}") begin box_url.delete if box_url rescue Errno::ENOENT # Not a big deal, the temp file may not actually exist end end end env[:ui].success(I18n.t( "vagrant.box_added", name: box.name, version: box.version, provider: box.provider)) # Store the added box in the env for future middleware env[:box_added] = box box end # Returns the download options for the download. # # @return [Hash] def downloader(url, env, **opts) opts[:ui] = true if !opts.key?(:ui) temp_path = env[:tmp_path].join("box" + Digest::SHA1.hexdigest(url)) @logger.info("Downloading box: #{url} => #{temp_path}") if File.file?(url) || url !~ /^[a-z0-9]+:.*$/i @logger.info("URL is a file or protocol not found and assuming file.") file_path = File.expand_path(url) file_path = Util::Platform.cygwin_windows_path(file_path) file_path = file_path.gsub("\\", "/") file_path = "/#{file_path}" if !file_path.start_with?("/") url = "file://#{file_path}" end # If the temporary path exists, verify it is not too old. If its # too old, delete it first because the data may have changed. if temp_path.file? delete = false if env[:box_clean] @logger.info("Cleaning existing temp box file.") delete = true elsif temp_path.mtime.to_i < (Time.now.to_i - RESUME_DELAY) @logger.info("Existing temp file is too old. Removing.") delete = true end temp_path.unlink if delete end downloader_options = {} downloader_options[:ca_cert] = env[:box_download_ca_cert] downloader_options[:ca_path] = env[:box_download_ca_path] downloader_options[:continue] = true downloader_options[:insecure] = env[:box_download_insecure] downloader_options[:client_cert] = env[:box_download_client_cert] downloader_options[:headers] = ["Accept: application/json"] if opts[:json] downloader_options[:ui] = env[:ui] if opts[:ui] downloader_options[:location_trusted] = env[:box_download_location_trusted] Util::Downloader.new(url, temp_path, downloader_options) end def download(url, env, **opts) opts[:ui] = true if !opts.key?(:ui) d = downloader(url, env, **opts) # Download the box to a temporary path. We store the temporary # path as an instance variable so that the `#recover` method can # access it. if opts[:ui] show_url = opts[:show_url] show_url ||= url display_url = Util::CredentialScrubber.scrub(show_url) translation = "vagrant.box_downloading" # Adjust status message when 'downloading' a local box. if show_url.start_with?("file://") translation = "vagrant.box_unpacking" end env[:ui].detail(I18n.t( translation, url: display_url)) if File.file?(d.destination) env[:ui].info(I18n.t("vagrant.actions.box.download.resuming")) end end begin d.download! rescue Errors::DownloaderInterrupted # The downloader was interrupted, so just return, because that # means we were interrupted as well. @download_interrupted = true env[:ui].info(I18n.t("vagrant.actions.box.download.interrupted")) end Pathname.new(d.destination) end # Tests whether the given URL points to a metadata file or a # box file without completely downloading the file. # # @param [String] url # @return [Boolean] true if metadata def metadata_url?(url, env) d = downloader(url, env, json: true, ui: false) # If we're downloading a file, cURL just returns no # content-type (makes sense), so we just test if it is JSON # by trying to parse JSON! uri = URI.parse(d.source) if uri.scheme == "file" url = uri.path url ||= uri.opaque #7570 Strip leading slash left in front of drive letter by uri.path Util::Platform.windows? && url.gsub!(/^\/([a-zA-Z]:)/, '\1') url = URI.unescape(url) begin File.open(url, "r") do |f| if f.size > METADATA_SIZE_LIMIT # Quit early, don't try to parse the JSON of gigabytes # of box files... return false end BoxMetadata.new(f) end return true rescue Errors::BoxMetadataMalformed return false rescue Errno::EINVAL # Actually not sure what causes this, but its always # in a case that isn't true. return false rescue Errno::EISDIR return false rescue Errno::ENOENT return false end end # If this isn't HTTP, then don't do the HEAD request if !uri.scheme.downcase.start_with?("http") @logger.info("not checking metadata since box URI isn't HTTP") return false end output = d.head match = output.scan(/^Content-Type: (.+?)$/i).last return false if !match !!(match.last.chomp =~ /application\/json/) 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("Expected checksum: #{checksum}") actual = FileChecksum.new(path, checksum_klass).checksum if actual.casecmp(checksum) != 0 raise Errors::BoxChecksumMismatch, actual: actual, expected: checksum end end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/box_check_outdated.rb000066400000000000000000000070311323370221500244520ustar00rootroot00000000000000require "log4r" module Vagrant module Action module Builtin # This middleware checks if there are outdated boxes. By default, # it only checks locally, but if `box_outdated_refresh` is set, it # will refresh the metadata associated with a box. class BoxCheckOutdated def initialize(app, env) @app = app @logger = Log4r::Logger.new( "vagrant::action::builtin::box_check_outdated") end def call(env) machine = env[:machine] if !env[:box_outdated_force] if !machine.config.vm.box_check_update @logger.debug( "Not checking for update: no force and no update config") return @app.call(env) end end if !machine.box # We don't have a box. Just ignore, we can't check for # outdated... @logger.warn("Not checking for update, no box") return @app.call(env) end box = machine.box if box.version == "0" && !box.metadata_url return @app.call(env) end constraints = machine.config.vm.box_version # Have download options specified in the environment override # options specified for the machine. download_options = { ca_cert: env[:ca_cert] || machine.config.vm.box_download_ca_cert, ca_path: env[:ca_path] || machine.config.vm.box_download_ca_path, client_cert: env[:client_cert] || machine.config.vm.box_download_client_cert, insecure: !env[:insecure].nil? ? env[:insecure] : machine.config.vm.box_download_insecure } env[:ui].output(I18n.t( "vagrant.box_outdated_checking_with_refresh", name: box.name)) update = nil begin update = box.has_update?(constraints, download_options: download_options) rescue Errors::BoxMetadataDownloadError => e env[:ui].warn(I18n.t( "vagrant.box_outdated_metadata_download_error", message: e.extra_data[:message])) rescue Errors::VagrantError => e raise if !env[:box_outdated_ignore_errors] env[:ui].detail(I18n.t( "vagrant.box_outdated_metadata_error_single", message: e.message)) end env[:box_outdated] = update != nil if update env[:ui].warn(I18n.t( "vagrant.box_outdated_single", name: update[0].name, provider: box.provider, current: box.version, latest: update[1].version)) else check_outdated_local(env) end @app.call(env) end def check_outdated_local(env) machine = env[:machine] # Make sure we respect the constraints set within the Vagrantfile version = machine.config.vm.box_version version += ", " if version version ||= "" version += "> #{machine.box.version}" box = env[:box_collection].find( machine.box.name, machine.box.provider, version) if box env[:ui].warn(I18n.t( "vagrant.box_outdated_local", name: box.name, old: machine.box.version, new: box.version)) env[:box_outdated] = true return end env[:box_outdated] = false end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/box_remove.rb000066400000000000000000000103521323370221500230010ustar00rootroot00000000000000require "log4r" module Vagrant module Action module Builtin # This middleware will remove a box for a given provider. class BoxRemove def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::box_remove") end def call(env) box_name = env[:box_name] box_provider = env[:box_provider] box_provider = box_provider.to_sym if box_provider box_version = env[:box_version] box_remove_all_versions = env[:box_remove_all_versions] boxes = {} env[:box_collection].all.each do |n, v, p| boxes[n] ||= {} boxes[n][p] ||= [] boxes[n][p] << v end all_box = boxes[box_name] if !all_box raise Errors::BoxRemoveNotFound, name: box_name end all_versions = nil if !box_provider if all_box.length == 1 # There is only one provider, just use that. all_versions = all_box.values.first box_provider = all_box.keys.first else raise Errors::BoxRemoveMultiProvider, name: box_name, providers: all_box.keys.map(&:to_s).sort.join(", ") end else all_versions = all_box[box_provider] if !all_versions raise Errors::BoxRemoveProviderNotFound, name: box_name, provider: box_provider.to_s, providers: all_box.keys.map(&:to_s).sort.join(", ") end end if !box_version if all_versions.length == 1 # There is only one version, just use that. box_version = all_versions.first elsif not box_remove_all_versions # There are multiple versions, we can't choose. raise Errors::BoxRemoveMultiVersion, name: box_name, provider: box_provider.to_s, versions: all_versions.sort.map { |k| " * #{k}" }.join("\n") end elsif !all_versions.include?(box_version) raise Errors::BoxRemoveVersionNotFound, name: box_name, provider: box_provider.to_s, version: box_version, versions: all_versions.sort.map { |k| " * #{k}" }.join("\n") end versions_to_remove = [box_version] versions_to_remove = all_versions if box_remove_all_versions versions_to_remove.sort.each do |version_to_remove| box = env[:box_collection].find( box_name, box_provider, box_version) # Verify that this box is not in use by an active machine, # otherwise warn the user. users = box.in_use?(env[:machine_index]) || [] users = users.find_all { |u| u.valid?(env[:home_path]) } if !users.empty? # Build up the output to show the user. users = users.map do |entry| "#{entry.name} (ID: #{entry.id})" end.join("\n") force_key = :force_confirm_box_remove message = I18n.t( "vagrant.commands.box.remove_in_use_query", name: box.name, provider: box.provider, version: box.version, users: users) + " " # Ask the user if we should do this stack = Builder.new.tap do |b| b.use Confirm, message, force_key end result = env[:action_runner].run(stack, env) if !result[:result] # They said "no", so continue with the next box next end end env[:ui].info(I18n.t("vagrant.commands.box.removing", name: box.name, provider: box.provider, version: box.version)) box.destroy! env[:box_collection].clean(box.name) # Passes on the removed box to the rest of the middleware chain env[:box_removed] = box end @app.call(env) end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/box_update.rb000066400000000000000000000006511323370221500227670ustar00rootroot00000000000000require "log4r" module Vagrant module Action module Builtin # This middleware updates a specific box if there are updates available. class BoxUpdate def initialize(app, env) @app = app @logger = Log4r::Logger.new( "vagrant::action::builtin::box_update") end def call(env) machine = env[:machine] end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/call.rb000066400000000000000000000047601323370221500215550ustar00rootroot00000000000000module Vagrant module Action module Builtin # This middleware class allows a sort of "conditional" run within # a single middlware sequence. It takes another middleware runnable, # runs it with the same environment, then yields the resulting env to a block, # allowing that block to determine the next course of action in the # middleware sequence. # # The first argument to this middleware sequence is anywhere middleware # runnable, whether it be a class, lambda, or something else that # responds to `call`. This middleware runnable is run with the same # environment as this class. # # After running, {Call} takes the environment and yields it to a block # given to initialize the class, along with an instance of {Builder}. # The result is used to build up a new sequence on the given builder. # This builder is then run. class Call # For documentation, read the description of the {Call} class. # # @param [Object] callable A valid middleware runnable object. This # can be a class, a lambda, or an object that responds to `call`. # @yield [result, builder] This block is expected to build on `builder` # which is the next middleware sequence that will be run. def initialize(app, env, callable, *callable_args, &block) raise ArgumentError, "A block must be given to Call" if !block @app = app @callable = callable @callable_args = callable_args @block = block @child_app = nil end def call(env) runner = Runner.new # Build the callable that we'll run callable = Builder.build(@callable, *@callable_args) # Run our callable with our environment new_env = runner.run(callable, env) # Build our new builder based on the result builder = Builder.new @block.call(new_env, builder) # Append our own app onto the builder so we slide the new # stack into our own chain... builder.use @app @child_app = builder.to_app(new_env) final_env = runner.run(@child_app, new_env) # Merge the environment into our original environment env.merge!(final_env) end def recover(env) # Call back into our compiled application and recover it. @child_app.recover(env) if @child_app end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/config_validate.rb000066400000000000000000000014021323370221500237460ustar00rootroot00000000000000require "vagrant/util/template_renderer" module Vagrant module Action module Builtin # This class validates the configuration and raises an exception # if there are any validation errors. class ConfigValidate def initialize(app, env) @app = app end def call(env) if !env.key?(:config_validate) || env[:config_validate] errors = env[:machine].config.validate(env[:machine]) if errors && !errors.empty? raise Errors::ConfigInvalid, errors: Util::TemplateRenderer.render( "config/validation_failed", errors: errors) end end @app.call(env) end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/confirm.rb000066400000000000000000000030521323370221500222700ustar00rootroot00000000000000module Vagrant module Action module Builtin # This class asks the user to confirm some sort of question with # a "Y/N" question. The only parameter is the text to ask the user. # The result is placed in `env[:result]` so that it can be used # with the {Call} class. class Confirm # For documentation, read the description of the {Confirm} class. # # @param [String] message The message to ask the user. # @param [Symbol] force_key The key that if present and true in # the environment hash will skip the confirmation question. def initialize(app, env, message, force_key=nil, **opts) @app = app @message = message @force_key = force_key @allowed = opts[:allowed] end def call(env) choice = nil # If we have a force key set and we're forcing, then set # the result to "Y" choice = "Y" if @force_key && env[@force_key] if !choice while true # If we haven't chosen yes, then ask the user via TTY choice = env[:ui].ask(@message) # If we don't have an allowed set just exit break if !@allowed break if @allowed.include?(choice) end end # The result is only true if the user said "Y" env[:result] = choice && choice.upcase == "Y" env["#{@force_key}_result".to_sym] = env[:result] @app.call(env) end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/destroy_confirm.rb000066400000000000000000000012671323370221500240470ustar00rootroot00000000000000require_relative "confirm" module Vagrant module Action module Builtin # This class asks the user to confirm the destruction of a machine # that Vagrant manages. This is provided as a built-in on top of # {Confirm} because it sets up the proper keys and such so that # `vagrant destroy -f` works properly. class DestroyConfirm < Confirm def initialize(app, env) force_key = :force_confirm_destroy message = I18n.t("vagrant.commands.destroy.confirmation", name: env[:machine].name) super(app, env, message, force_key, allowed: ["y", "n", "Y", "N"]) end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/env_set.rb000066400000000000000000000011301323370221500222710ustar00rootroot00000000000000module Vagrant module Action module Builtin # This middleware class allows you to modify the environment hash # in the middle of a middleware sequence. The new environmental data # will take affect at this stage in the middleware and will persist # through. class EnvSet def initialize(app, env, new_env=nil) @app = app @new_env = new_env || {} end def call(env) # Merge in the new data env.merge!(@new_env) # Carry on @app.call(env) end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/graceful_halt.rb000066400000000000000000000061031323370221500234330ustar00rootroot00000000000000require "log4r" require "timeout" module Vagrant module Action module Builtin # This middleware class will attempt to perform a graceful shutdown # of the machine using the guest implementation. This middleware is # compatible with the {Call} middleware so you can branch based on # the result, which is true if the halt succeeded and false otherwise. class GracefulHalt # Note: Any of the arguments can be arrays as well. # # @param [Symbol] target_state The target state ID that means that # the machine was properly shut down. # @param [Symbol] source_state The source state ID that the machine # must be in to be shut down. def initialize(app, env, target_state, source_state=nil) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::graceful_halt") @source_state = source_state @target_state = target_state end def call(env) graceful = true graceful = !env[:force_halt] if env.key?(:force_halt) # By default, we didn't succeed. env[:result] = false if graceful && @source_state @logger.info("Verifying source state of machine: #{@source_state.inspect}") # If we're not in the proper source state, then we don't # attempt to halt the machine current_state = env[:machine].state.id if current_state != @source_state @logger.info("Invalid source state, not halting: #{current_state}") graceful = false end end # Only attempt to perform graceful shutdown under certain cases # checked above. if graceful env[:ui].output(I18n.t("vagrant.actions.vm.halt.graceful")) begin env[:machine].guest.capability(:halt) @logger.debug("Waiting for target graceful halt state: #{@target_state}") begin Timeout.timeout(env[:machine].config.vm.graceful_halt_timeout) do while env[:machine].state.id != @target_state sleep 1 end end rescue Timeout::Error # Don't worry about it, we catch the case later. end rescue Errors::GuestCapabilityNotFound # This happens if insert_public_key is called on a guest that # doesn't support it. This will block a destroy so we let it go. rescue Errors::MachineGuestNotReady env[:ui].detail(I18n.t("vagrant.actions.vm.halt.guest_not_ready")) end # The result of this matters on whether we reached our # proper target state or not. env[:result] = env[:machine].state.id == @target_state if env[:result] @logger.info("Gracefully halted.") else @logger.info("Graceful halt failed.") end end @app.call(env) end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/handle_box.rb000066400000000000000000000074721323370221500227500ustar00rootroot00000000000000require "thread" require "log4r" module Vagrant module Action module Builtin # This built-in middleware handles the `box` setting by verifying # the box is already installed, dowloading the box if it isn't, # updating the box if it is requested, etc. class HandleBox @@big_lock = Mutex.new @@small_locks = Hash.new { |h,k| h[k] = Mutex.new } def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::handle_box") end def call(env) machine = env[:machine] if !machine.config.vm.box @logger.info("Skipping HandleBox because no box is set") return @app.call(env) end # Acquire a lock for this box to handle multi-threaded # environments. lock = nil @@big_lock.synchronize do lock = @@small_locks[machine.config.vm.box] end box_updated = false lock.synchronize do if machine.box @logger.info("Machine already has box. HandleBox will not run.") next end handle_box(env) box_updated = true end if box_updated # Reload the environment and set the VM to be the new loaded VM. new_machine = machine.vagrantfile.machine( machine.name, machine.provider_name, machine.env.boxes, machine.data_dir, machine.env) env[:machine].box = new_machine.box env[:machine].config = new_machine.config env[:machine].provider_config = new_machine.provider_config end @app.call(env) end def handle_box(env) machine = env[:machine] # Determine the set of formats that this box can be in box_download_ca_cert = machine.config.vm.box_download_ca_cert box_download_ca_path = machine.config.vm.box_download_ca_path box_download_client_cert = machine.config.vm.box_download_client_cert box_download_insecure = machine.config.vm.box_download_insecure box_download_checksum_type = machine.config.vm.box_download_checksum_type box_download_checksum = machine.config.vm.box_download_checksum box_download_location_trusted = machine.config.vm.box_download_location_trusted box_formats = machine.provider_options[:box_format] || machine.provider_name version_ui = machine.config.vm.box_version version_ui ||= ">= 0" env[:ui].output(I18n.t( "vagrant.box_auto_adding", name: machine.config.vm.box)) env[:ui].detail("Box Provider: #{Array(box_formats).join(", ")}") env[:ui].detail("Box Version: #{version_ui}") begin env[:action_runner].run(Vagrant::Action.action_box_add, env.merge({ box_name: machine.config.vm.box, box_url: machine.config.vm.box_url || machine.config.vm.box, box_server_url: machine.config.vm.box_server_url, box_provider: box_formats, box_version: machine.config.vm.box_version, box_download_client_cert: box_download_client_cert, box_download_ca_cert: box_download_ca_cert, box_download_ca_path: box_download_ca_path, box_download_insecure: box_download_insecure, box_checksum_type: box_download_checksum_type, box_checksum: box_download_checksum, box_download_location_trusted: box_download_location_trusted, })) rescue Errors::BoxAlreadyExists # Just ignore this, since it means the next part will succeed! # This can happen in a multi-threaded environment. end end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/handle_box_url.rb000066400000000000000000000006251323370221500236230ustar00rootroot00000000000000module Vagrant module Action module Builtin class HandleBoxUrl < HandleBox def call(env) env[:ui].warn("HandleBoxUrl middleware is deprecated. Use HandleBox instead.") env[:ui].warn("This is a bug with the provider. Please contact the creator") env[:ui].warn("of the provider you use to fix this.") super end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb000066400000000000000000000225551323370221500274360ustar00rootroot00000000000000require "set" require "log4r" require "vagrant/util/is_port_open" module Vagrant module Action module Builtin # This middleware class will detect and handle collisions with # forwarded ports, whether that means raising an error or repairing # them automatically. # # Parameters it takes from the environment hash: # # * `:port_collision_repair` - If true, it will attempt to repair # port collisions. If false, it will raise an exception when # there is a collision. # # * `:port_collision_extra_in_use` - An array of ports that are # considered in use. # # * `:port_collision_remap` - A hash remapping certain host ports # to other host ports. # class HandleForwardedPortCollisions include Util::IsPortOpen def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::handle_port_collisions") end def call(env) @leased = [] @machine = env[:machine] # Acquire a process-level lock so that we don't choose a port # that someone else also chose. begin env[:machine].env.lock("fpcollision") do handle(env) end rescue Errors::EnvironmentLockedError sleep 1 retry end @app.call(env) # Always run the recover method so that we release leases recover(env) end def recover(env) lease_release end protected def handle(env) @logger.info("Detecting any forwarded port collisions...") # Get the extra ports we consider in use extra_in_use = env[:port_collision_extra_in_use] || {} # If extras are provided as an Array (previous behavior) convert # to Hash as expected for IP aliasing support if extra_in_use.is_a?(Array) extra_in_use = Hash[extra_in_use.map{|port| [port, Set.new(["*"])]}] end # Get the remap remap = env[:port_collision_remap] || {} # Determine the handler we'll use if we have any port collisions repair = !!env[:port_collision_repair] # The method we'll use to check if a port is open. port_checker = env[:port_collision_port_check] port_checker ||= method(:port_check) # Log out some of our parameters @logger.debug("Extra in use: #{extra_in_use.inspect}") @logger.debug("Remap: #{remap.inspect}") @logger.debug("Repair: #{repair.inspect}") # Determine a list of usable ports for repair usable_ports = Set.new(env[:machine].config.vm.usable_port_range) usable_ports.subtract(extra_in_use.keys) # Pass one, remove all defined host ports from usable ports with_forwarded_ports(env) do |options| usable_ports.delete(options[:host]) end # Pass two, detect/handle any collisions with_forwarded_ports(env) do |options| guest_port = options[:guest] host_port = options[:host] host_ip = options[:host_ip] if options[:disabled] @logger.debug("Skipping disabled port #{host_port}.") next end if options[:protocol] && options[:protocol] != "tcp" @logger.debug("Skipping #{host_port} because UDP protocol.") next end if remap[host_port] remap_port = remap[host_port] @logger.debug("Remap port override: #{host_port} => #{remap_port}") host_port = remap_port end # If the port is open (listening for TCP connections) in_use = is_forwarded_already(extra_in_use, host_port, host_ip) || call_port_checker(port_checker, host_ip, host_port) || lease_check(host_ip, host_port) if in_use if !repair || !options[:auto_correct] raise Errors::ForwardPortCollision, guest_port: guest_port.to_s, host_port: host_port.to_s end @logger.info("Attempting to repair FP collision: #{host_port}") repaired_port = nil while !usable_ports.empty? # Attempt to repair the forwarded port repaired_port = usable_ports.to_a.sort[0] usable_ports.delete(repaired_port) # If the port is in use, then we can't use this either... in_use = is_forwarded_already(extra_in_use, repaired_port, host_ip) || call_port_checker(port_checker, host_ip, repaired_port) || lease_check(host_ip, repaired_port) if in_use @logger.info("Repaired port also in use: #{repaired_port}. Trying another...") next end # We have a port so break out break end # If we have no usable ports then we can't repair if !repaired_port && usable_ports.empty? raise Errors::ForwardPortAutolistEmpty, vm_name: env[:machine].name, guest_port: guest_port.to_s, host_port: host_port.to_s end # Modify the args in place options[:host] = repaired_port @logger.info("Repaired FP collision: #{host_port} to #{repaired_port}") # Notify the user env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.fixed_collision", host_port: host_port.to_s, guest_port: guest_port.to_s, new_port: repaired_port.to_s)) end end end def lease_check(host_ip=nil, host_port) # Check if this port is "leased". We use a leasing system of # about 60 seconds to avoid any forwarded port collisions in # a highly parallelized environment. leasedir = @machine.env.data_dir.join("fp-leases") leasedir.mkpath if host_ip.nil? lease_file_name = host_port.to_s else lease_file_name = "#{host_ip.gsub('.','_')}_#{host_port.to_s}" end invalid = false oldest = Time.now.to_i - 60 leasedir.children.each do |child| # Delete old, invalid leases while we're looking if child.file? && child.mtime.to_i < oldest child.delete end if child.basename.to_s == lease_file_name invalid = true end end # If its invalid, then the port is "open" and in use return true if invalid # Otherwise, create the lease leasedir.join(lease_file_name).open("w+") do |f| f.binmode f.write(Time.now.to_i.to_s + "\n") end # Add to the leased array so we unlease it right away @leased << lease_file_name # Things look good to us! false end def lease_release leasedir = @machine.env.data_dir.join("fp-leases") @leased.each do |port| path = leasedir.join(port) path.delete if path.file? end end # This functions checks to see if the current instance's hostport and # hostip for forwarding is in use by the virtual machines created # previously. def is_forwarded_already(extra_in_use, hostport, hostip) hostip = '*' if hostip.nil? || hostip.empty? # ret. false if none of the VMs we spun up had this port forwarded. return false if not extra_in_use.has_key?(hostport) # ret. true if the user has requested to bind on all interfaces but # we already have a rule in one the VMs we spun up. if hostip == '*' if extra_in_use.fetch(hostport).size != 0 return true else return false end end return extra_in_use.fetch(hostport).include?(hostip) end def port_check(host_ip, host_port) # If no host_ip is specified, intention taken to be list on all interfaces. # If platform is windows, default back to localhost only test_host_ip = host_ip || "0.0.0.0" begin is_port_open?(test_host_ip, host_port) rescue Errno::EADDRNOTAVAIL if !host_ip && test_host_ip == "0.0.0.0" test_host_ip = "127.0.0.1" retry else raise end end end def with_forwarded_ports(env) env[:machine].config.vm.networks.each do |type, options| # Ignore anything but forwarded ports next if type != :forwarded_port yield options end end def call_port_checker(port_checker, host_ip, host_port) call_args = [host_ip, host_port] # Trim args if checker method does not support inclusion of host_ip call_args = call_args.slice(call_args.size - port_checker.arity.abs, port_checker.arity.abs) port_checker[*call_args] end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/is_env_set.rb000066400000000000000000000011401323370221500227650ustar00rootroot00000000000000module Vagrant module Action module Builtin # This middleware is meant to be used with Call and can check if # a variable in env is set. class IsEnvSet def initialize(app, env, key, **opts) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::is_env_set") @key = key end def call(env) @logger.debug("Checking if env is set: '#{@key}'") env[:result] = !!env[@key] @logger.debug(" - Result: #{env[:result].inspect}") @app.call(env) end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/is_state.rb000066400000000000000000000021031323370221500224420ustar00rootroot00000000000000module Vagrant module Action module Builtin # This middleware is meant to be used with Call and can check if # a machine is in the given state ID. class IsState # Note: Any of the arguments can be arrays as well. # # @param [Symbol] target_state The target state ID that means that # the machine was properly shut down. # @param [Symbol] source_state The source state ID that the machine # must be in to be shut down. def initialize(app, env, check, **opts) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::is_state") @check = check @invert = !!opts[:invert] end def call(env) @logger.debug("Checking if machine state is '#{@check}'") state = env[:machine].state.id @logger.debug("-- Machine state: #{state}") env[:result] = @check == state env[:result] = !env[:result] if @invert @app.call(env) end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/lock.rb000066400000000000000000000040141323370221500215620ustar00rootroot00000000000000require "log4r" module Vagrant module Action module Builtin # This class creates a multi-process lock using `flock`. The lock # is active for the remainder of the middleware stack. class Lock def initialize(app, env, options=nil) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::lock") @options ||= options || {} raise ArgumentError, "Please specify a lock path" if !@options[:path] raise ArgumentError, "Please specify an exception." if !@options[:exception] end def call(env) lock_path = @options[:path] lock_path = lock_path.call(env) if lock_path.is_a?(Proc) env_key = "has_lock_#{lock_path}" if !env[env_key] # If we already have the key in our environment we assume the # lock is held by our middleware stack already and we allow # nesting. File.open(lock_path, "w+") do |f| # The file locking fails only if it returns "false." If it # succeeds it returns a 0, so we must explicitly check for # the proper error case. @logger.info("Locking: #{lock_path}") if f.flock(File::LOCK_EX | File::LOCK_NB) === false exception = @options[:exception] exception = exception.call(env) if exception.is_a?(Proc) raise exception end # Set that we gained the lock and call deeper into the # middleware, but make sure we UNSET the lock when we leave. begin env[env_key] = true @app.call(env) ensure @logger.info("Unlocking: #{lock_path}") env[env_key] = false f.flock(File::LOCK_UN) end end else # Just call up the middleware because we already hold the lock @app.call(env) end end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/message.rb000066400000000000000000000010411323370221500222530ustar00rootroot00000000000000module Vagrant module Action module Builtin # This middleware simply outputs a message to the UI. class Message def initialize(app, env, message, **opts) @app = app @message = message @opts = opts end def call(env) if !@opts[:post] env[:ui].output(@message) end @app.call(env) if @opts[:post] env[:ui].output(@message) end end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/mixin_provisioners.rb000066400000000000000000000032541323370221500246050ustar00rootroot00000000000000module Vagrant module Action module Builtin module MixinProvisioners # This returns all the instances of the configured provisioners. # This is safe to call multiple times since it will cache the results. # # @return [Array] def provisioner_instances(env) return @_provisioner_instances if @_provisioner_instances # Make the mapping that'll keep track of provisioner => type @_provisioner_types = {} # Get all the configured provisioners @_provisioner_instances = env[:machine].config.vm.provisioners.map do |provisioner| # Instantiate the provisioner klass = Vagrant.plugin("2").manager.provisioners[provisioner.type] # This can happen in the case the configuration isn't validated. next nil if !klass result = klass.new(env[:machine], provisioner.config) # Store in the type map so that --provision-with works properly @_provisioner_types[result] = provisioner.type # Build up the options options = { name: provisioner.name, run: provisioner.run, } # Return the result [result, options] end return @_provisioner_instances.compact end # This will return a mapping of a provisioner instance to its # type. def provisioner_type_map(env) # Call this in order to initial the map if it hasn't been already provisioner_instances(env) # Return the type map @_provisioner_types end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/mixin_synced_folders.rb000066400000000000000000000206101323370221500250410ustar00rootroot00000000000000require "json" require "set" require 'vagrant/util/scoped_hash_override' module Vagrant module Action module Builtin module MixinSyncedFolders include Vagrant::Util::ScopedHashOverride # This goes over all the registered synced folder types and returns # the highest priority implementation that is usable for this machine. def default_synced_folder_type(machine, plugins) ordered = [] # First turn the plugins into an array plugins.each do |key, data| impl = data[0] priority = data[1] ordered << [priority, key, impl] end # Order the plugins by priority. Higher is tried before lower. ordered = ordered.sort { |a, b| b[0] <=> a[0] } allowed_types = machine.config.vm.allowed_synced_folder_types if allowed_types ordered = allowed_types.map do |type| ordered.find do |_, key, impl| key == type end end.compact end # Find the proper implementation ordered.each do |_, key, impl| return key if impl.new.usable?(machine) end return nil end # This finds the options in the env that are set for a given # synced folder type. def impl_opts(name, env) {}.tap do |result| env.each do |k, v| if k.to_s.start_with?("#{name}_") # While I generally don't like the 'rescue' syntax, # we do this to just fall back to the default value # if it isn't dup-able. k = k.dup rescue k v = v.dup rescue v result[k] = v end end end end # This returns the available synced folder implementations. This # is a separate method so that it can be easily stubbed by tests. def plugins @plugins ||= Vagrant.plugin("2").manager.synced_folders end # This saves the synced folders data to the machine data directory. # They can then be retrieved again with `synced_folders` by passing # the `cached` option to it. # # @param [Machine] machine The machine that the folders belong to # @param [Hash] folders The result from a {#synced_folders} call. def save_synced_folders(machine, folders, **opts) if opts[:merge] existing = cached_synced_folders(machine) if existing if opts[:vagrantfile] # Go through and find any cached that were from the # Vagrantfile itself. We remove those if it was requested. existing.each do |impl, fs| fs.each do |id, data| fs.delete(id) if data[:__vagrantfile] end end end folders.each do |impl, fs| existing[impl] ||= {} fs.each do |id, data| existing[impl][id] = data end end folders = existing end end machine.data_dir.join("synced_folders").open("w") do |f| f.write(JSON.dump(folders)) end end # This returns the set of shared folders that should be done for # this machine. It returns the folders in a hash keyed by the # implementation class for the synced folders. # # @return [Hash>] def synced_folders(machine, **opts) return cached_synced_folders(machine) if opts[:cached] config = opts[:config] root = false if !config config = machine.config.vm root = true end config_folders = config.synced_folders folders = {} # Determine all the synced folders as well as the implementation # they're going to use. config_folders.each do |id, data| # Ignore disabled synced folders next if data[:disabled] impl = "" impl = data[:type].to_sym if data[:type] && !data[:type].empty? if impl != "" impl_class = plugins[impl] if !impl_class # This should never happen because configuration validation # should catch this case. But we put this here as an assert raise "Internal error. Report this as a bug. Invalid: #{data[:type]}" end if !opts[:disable_usable_check] if !impl_class[0].new.usable?(machine, true) # Verify that explicitly defined shared folder types are # actually usable. raise Errors::SyncedFolderUnusable, type: data[:type].to_s end end end # Get the data to store data = data.dup if root # If these are the root synced folders (attached directly) # to the Vagrantfile, then we mark it as such. data[:__vagrantfile] = true end # Keep track of this shared folder by the implementation. folders[impl] ||= {} folders[impl][id] = data end # If we have folders with the "default" key, then determine the # most appropriate implementation for this. if folders.key?("") && !folders[""].empty? default_impl = default_synced_folder_type(machine, plugins) if !default_impl types = plugins.to_hash.keys.map { |t| t.to_s }.sort.join(", ") raise Errors::NoDefaultSyncedFolderImpl, types: types end folders[default_impl] ||= {} folders[default_impl].merge!(folders[""]) folders.delete("") end # Apply the scoped hash overrides to get the options folders.dup.each do |impl_name, fs| new_fs = {} fs.each do |id, data| id = data[:id] if data[:id] new_fs[id] = scoped_hash_override(data, impl_name) end folders[impl_name] = new_fs end return folders end # This finds the difference between two lists of synced folder # definitions. # # This will return a hash with three keys: "added", "removed", # and "modified". These will contain a set of IDs of folders # that were added, removed, or modified, respectively. # # The parameters should be results from the {#synced_folders} call. # # @return [hash] def synced_folders_diff(one, two) existing_ids = {} one.each do |impl, fs| fs.each do |id, data| existing_ids[id] = data end end result = Hash.new { |h, k| h[k] = Set.new } two.each do |impl, fs| fs.each do |id, data| existing = existing_ids.delete(id) if !existing result[:added] << id next end # Exists, so we have to compare the host and guestpath, which # is most important... if existing[:hostpath] != data[:hostpath] || existing[:guestpath] != data[:guestpath] result[:modified] << id end end end existing_ids.each do |k, _| result[:removed] << k end result end protected def cached_synced_folders(machine) JSON.parse(machine.data_dir.join("synced_folders").read).tap do |r| # We have to do all sorts of things to make the proper things # symbols and r.keys.each do |k| r[k].each do |ik, v| v.keys.each do |vk| v[vk.to_sym] = v[vk] v.delete(vk) end end r[k.to_sym] = r[k] r.delete(k) end end rescue Errno::ENOENT # If the file doesn't exist, we probably just have a machine created # by a version of Vagrant that didn't cache shared folders. Report no # shared folders to be safe. return {} end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/prepare_clone.rb000066400000000000000000000021071323370221500234510ustar00rootroot00000000000000require "log4r" module Vagrant module Action module Builtin class PrepareClone def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::vm::prepare_clone") end def call(env) # If we aren't cloning, then do nothing if !env[:machine].config.vm.clone return @app.call(env) end # We need to get the machine ID from this Vagrant environment clone_env = env[:machine].env.environment( env[:machine].config.vm.clone) raise Errors::CloneNotFound if !clone_env.root_path # Get the machine itself clone_machine = clone_env.machine( clone_env.primary_machine_name, env[:machine].provider_name) raise Errors::CloneMachineNotFound if !clone_machine.id # Set the ID of the master so we know what to clone from env[:clone_id] = clone_machine.id env[:clone_machine] = clone_machine # Continue @app.call(env) end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/provision.rb000066400000000000000000000121501323370221500226620ustar00rootroot00000000000000require "log4r" require_relative "mixin_provisioners" module Vagrant module Action module Builtin # This class will run the configured provisioners against the # machine. # # This action should be placed BEFORE the machine is booted so it # can do some setup, and then run again (on the return path) against # a running machine. class Provision include MixinProvisioners def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::provision") end def call(env) @env = env # Tracks whether we were configured to provision config_enabled = true config_enabled = env[:provision_enabled] if env.key?(:provision_enabled) # Check if we already provisioned, and if so, disable the rest provision_enabled = true ignore_sentinel = true if env.key?(:provision_ignore_sentinel) ignore_sentinel = env[:provision_ignore_sentinel] end if ignore_sentinel @logger.info("Ignoring sentinel check, forcing provision") end @logger.info("Checking provisioner sentinel file...") sentinel_path = env[:machine].data_dir.join("action_provision") update_sentinel = false if sentinel_path.file? # The sentinel file is in the format of "version:data" so that # we can remain backwards compatible with previous sentinels. # Versions so far: # # Vagrant < 1.5.0: A timestamp. The weakness here was that # if it wasn't cleaned up, it would incorrectly not provision # new machines. # # Vagrant >= 1.5.0: "1.5:ID", where ID is the machine ID. # We compare both so we know whether it is a new machine. # contents = sentinel_path.read.chomp parts = contents.split(":", 2) if parts.length == 1 @logger.info("Old-style sentinel found! Not provisioning.") provision_enabled = false if !ignore_sentinel update_sentinel = true elsif parts[0] == "1.5" && parts[1] == env[:machine].id.to_s @logger.info("Sentinel found! Not provisioning.") provision_enabled = false if !ignore_sentinel else @logger.info("Sentinel found with another machine ID. Removing.") sentinel_path.unlink end end # Store the value so that other actions can use it env[:provision_enabled] = provision_enabled if !env.key?(:provision_enabled) # Ask the provisioners to modify the configuration if needed provisioner_instances(env).each do |p, _| p.configure(env[:machine].config) end # Continue, we need the VM to be booted. @app.call(env) # If we're configured to not provision, notify the user and stop if !config_enabled env[:ui].info(I18n.t("vagrant.actions.vm.provision.disabled_by_config")) return end # If we're not provisioning because of the sentinel, tell the user # but continue trying for the "always" provisioners if !provision_enabled env[:ui].info(I18n.t("vagrant.actions.vm.provision.disabled_by_sentinel")) end # Write the sentinel if we have to if update_sentinel || !sentinel_path.file? @logger.info("Writing provisioning sentinel so we don't provision again") sentinel_path.open("w") do |f| f.write("1.5:#{env[:machine].id}") end end type_map = provisioner_type_map(env) provisioner_instances(env).each do |p, options| type_name = type_map[p] if options[:run] == :never next if env[:provision_types].nil? || !env[:provision_types].include?(options[:name]) else next if env[:provision_types] && \ !env[:provision_types].include?(type_name) && \ !env[:provision_types].include?(options[:name]) # Don't run if sentinel is around and we're not always running next if !provision_enabled && options[:run] != :always end name = type_name if options[:name] name = "#{options[:name]} (#{type_name})" end env[:ui].info(I18n.t( "vagrant.actions.vm.provision.beginning", provisioner: name)) env[:hook].call(:provisioner_run, env.merge( callable: method(:run_provisioner), provisioner: p, provisioner_name: type_name, )) end end # This is pulled out into a separate method so that users can # subclass and implement custom behavior if they'd like to work around # this step. def run_provisioner(env) env[:provisioner].provision end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/provisioner_cleanup.rb000066400000000000000000000030711323370221500247220ustar00rootroot00000000000000require "log4r" require_relative "mixin_provisioners" module Vagrant module Action module Builtin # This action will run the cleanup methods on provisioners and should # be used as part of any Destroy action. class ProvisionerCleanup include MixinProvisioners def initialize(app, env, place=nil) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::provision_cleanup") place ||= :after @place = place.to_sym end def call(env) do_cleanup(env) if @place == :before # Continue, we need the VM to be booted. @app.call(env) do_cleanup(env) if @place == :after end def do_cleanup(env) type_map = provisioner_type_map(env) # Ask the provisioners to modify the configuration if needed provisioner_instances(env).each do |p, _| name = type_map[p].to_s # Check if the subclass defined a cleanup method. The parent # provisioning class defines a `cleanup` method, so we cannot use # `respond_to?` here. Instead, we have to check if _this_ instance # defines a cleanup task. if p.public_methods(false).include?(:cleanup) env[:ui].info(I18n.t("vagrant.provisioner_cleanup", name: name, )) p.cleanup else @logger.debug("Skipping cleanup tasks for `#{name}' - not defined") end end end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/set_hostname.rb000066400000000000000000000014171323370221500233270ustar00rootroot00000000000000require "log4r" module Vagrant module Action module Builtin # This middleware sets the hostname of the guest according to the # "vm.hostname" configuration parameter if it is set. This middleware # should be placed such that the after the @app.call, a booted machine # is available (this generally means BEFORE the boot middleware). class SetHostname def initialize(app, env) @app = app end def call(env) @app.call(env) hostname = env[:machine].config.vm.hostname if !hostname.nil? env[:ui].info I18n.t("vagrant.actions.vm.hostname.setting") env[:machine].guest.capability(:change_host_name, hostname) end end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/ssh_exec.rb000066400000000000000000000024371323370221500224420ustar00rootroot00000000000000require "pathname" require "vagrant/util/ssh" module Vagrant module Action module Builtin # This class will exec into a full fledged SSH console into the # remote machine. This middleware assumes that the VM is running and # ready for SSH, and uses the {Machine#ssh_info} method to retrieve # SSH information necessary to connect. # # Note: If there are any middleware after `SSHExec`, they will **not** # run, since exec replaces the currently running process. class SSHExec # For quick access to the `SSH` class. include Vagrant::Util def initialize(app, env) @app = app end def call(env) # Grab the SSH info from the machine or the environment info = env[:ssh_info] info ||= env[:machine].ssh_info # If the result is nil, then the machine is telling us that it is # not yet ready for SSH, so we raise this exception. raise Errors::SSHNotReady if info.nil? info[:private_key_path] ||= [] if info[:private_key_path].empty? && info[:password] env[:ui].warn(I18n.t("vagrant.ssh_exec_password")) end # Exec! SSH.exec(info, env[:ssh_opts]) end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/ssh_run.rb000066400000000000000000000036741323370221500223260ustar00rootroot00000000000000require "log4r" require "vagrant/util/platform" require "vagrant/util/ssh" require "vagrant/util/shell_quote" module Vagrant module Action module Builtin # This class will run a single command on the remote machine and will # mirror the output to the UI. The resulting exit status of the command # will exist in the `:ssh_run_exit_status` key in the environment. class SSHRun # For quick access to the `SSH` class. include Vagrant::Util def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::ssh_run") end def call(env) # Grab the SSH info from the machine or the environment info = env[:ssh_info] info ||= env[:machine].ssh_info # If the result is nil, then the machine is telling us that it is # not yet ready for SSH, so we raise this exception. raise Errors::SSHNotReady if info.nil? info[:private_key_path] ||= [] if info[:keys_only] && info[:private_key_path].empty? raise Errors::SSHRunRequiresKeys end # Get the command and wrap it in a login shell command = ShellQuote.escape(env[:ssh_run_command], "'") command = "#{env[:machine].config.ssh.shell} -c '#{command}'" # Execute! opts = env[:ssh_opts] || {} opts[:extra_args] ||= [] # Allow the user to specify a tty or non-tty manually, but if they # don't then we default to a TTY if !opts[:extra_args].include?("-t") && !opts[:extra_args].include?("-T") && env[:tty] opts[:extra_args] << "-t" end opts[:extra_args] << command opts[:subprocess] = true env[:ssh_run_exit_status] = Util::SSH.exec(info, opts) # Call the next middleware @app.call(env) end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/synced_folder_cleanup.rb000066400000000000000000000015651323370221500251710ustar00rootroot00000000000000require "log4r" require_relative "mixin_synced_folders" module Vagrant module Action module Builtin # This middleware will run cleanup tasks for synced folders using # the appropriate synced folder plugin. class SyncedFolderCleanup include MixinSyncedFolders def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::synced_folder_cleanup") end def call(env) folders = synced_folders(env[:machine]) # Go through each folder and do cleanup folders.each_key do |impl_name| @logger.info("Invoking synced folder cleanup for: #{impl_name}") plugins[impl_name.to_sym][0].new.cleanup( env[:machine], impl_opts(impl_name, env)) end @app.call(env) end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/synced_folders.rb000066400000000000000000000111251323370221500236360ustar00rootroot00000000000000require "log4r" require 'vagrant/util/platform' require_relative "mixin_synced_folders" module Vagrant module Action module Builtin # This middleware will setup the synced folders for the machine using # the appropriate synced folder plugin. class SyncedFolders include MixinSyncedFolders def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::synced_folders") end def call(env) opts = { cached: !!env[:synced_folders_cached], config: env[:synced_folders_config], } @logger.info("SyncedFolders loading from cache: #{opts[:cached]}") folders = synced_folders(env[:machine], **opts) original_folders = folders folders.each do |impl_name, fs| @logger.info("Synced Folder Implementation: #{impl_name}") fs.each do |id, data| # Log every implementation and their paths @logger.info(" - #{id}: #{data[:hostpath]} => #{data[:guestpath]}") end end # Go through each folder and make sure to create it if # it does not exist on host folders.each do |_, fs| fs.each do |id, data| next if data[:hostpath_exact] data[:hostpath] = File.expand_path( data[:hostpath], env[:root_path]) # Expand the symlink if this is a path that exists if File.file?(data[:hostpath]) data[:hostpath] = File.realpath(data[:hostpath]) end # Create the hostpath if it doesn't exist and we've been told to if !File.directory?(data[:hostpath]) && data[:create] @logger.info("Creating shared folder host directory: #{data[:hostpath]}") begin Pathname.new(data[:hostpath]).mkpath rescue Errno::EACCES raise Vagrant::Errors::SharedFolderCreateFailed, path: data[:hostpath] end end if File.directory?(data[:hostpath]) data[:hostpath] = File.realpath(data[:hostpath]) data[:hostpath] = Util::Platform.fs_real_path(data[:hostpath]).to_s end end end # Build up the instances of the synced folders. We do this once # so that they can store state. folders = folders.map do |impl_name, fs| instance = plugins[impl_name.to_sym][0].new [instance, impl_name, fs] end # Go through each folder and prepare the folders folders.each do |impl, impl_name, fs| if !env[:synced_folders_disable] @logger.info("Invoking synced folder prepare for: #{impl_name}") impl.prepare(env[:machine], fs, impl_opts(impl_name, env)) end end # Continue, we need the VM to be booted. @app.call(env) # Once booted, setup the folder contents folders.each do |impl, impl_name, fs| if !env[:synced_folders_disable] @logger.info("Invoking synced folder enable: #{impl_name}") impl.enable(env[:machine], fs, impl_opts(impl_name, env)) next end # We're disabling synced folders to_disable = {} fs.each do |id, data| next if !env[:synced_folders_disable].include?(id) to_disable[id] = data end @logger.info("Invoking synced folder disable: #{impl_name}") to_disable.each do |id, _| @logger.info(" - Disabling: #{id}") end impl.disable(env[:machine], to_disable, impl_opts(impl_name, env)) end # If we disabled folders, we have to delete some from the # save, so we load the entire cached thing, and delete them. if env[:synced_folders_disable] all = synced_folders(env[:machine], cached: true) all.each do |impl, fs| fs.keys.each do |id| if env[:synced_folders_disable].include?(id) fs.delete(id) end end end save_synced_folders(env[:machine], all) else save_opts = { merge: true } save_opts[:vagrantfile] = true if !opts[:config] # Save the synced folders save_synced_folders(env[:machine], original_folders, **save_opts) end end end end end end vagrant-2.0.2/lib/vagrant/action/builtin/wait_for_communicator.rb000066400000000000000000000045141323370221500252310ustar00rootroot00000000000000module Vagrant module Action module Builtin # This waits for the communicator to be ready for a set amount of # time. class WaitForCommunicator def initialize(app, env, states=nil) @app = app @states = states end def call(env) # Wait for ready in a thread so that we can continually check # for interrupts. ready_thr = Thread.new do Thread.current[:result] = env[:machine].communicate.wait_for_ready( env[:machine].config.vm.boot_timeout) end # Start a thread that verifies the VM stays in a good state. states_thr = Thread.new do Thread.current[:result] = true # Otherwise, periodically verify the VM isn't in a bad state. while true state = env[:machine].state.id # Used to report invalid states Thread.current[:last_known_state] = state # Check if we have the proper state so we can break out if @states && !@states.include?(state) Thread.current[:result] = false break end # Sleep a bit so we don't hit 100% CPU constantly. sleep 1 end end # Wait for a result or an interrupt env[:ui].output(I18n.t("vagrant.boot_waiting")) while ready_thr.alive? && states_thr.alive? sleep 1 return if env[:interrupted] end # Join so that they can raise exceptions if there were any ready_thr.join if !ready_thr.alive? states_thr.join if !states_thr.alive? # If it went into a bad state, then raise an error if !states_thr[:result] raise Errors::VMBootBadState, valid: @states.join(", "), invalid: states_thr[:last_known_state] end # If it didn't boot, raise an error if !ready_thr[:result] raise Errors::VMBootTimeout end env[:ui].output(I18n.t("vagrant.boot_completed")) # Make sure our threads are all killed ready_thr.kill states_thr.kill @app.call(env) ensure ready_thr.kill states_thr.kill end end end end end vagrant-2.0.2/lib/vagrant/action/general/000077500000000000000000000000001323370221500202555ustar00rootroot00000000000000vagrant-2.0.2/lib/vagrant/action/general/package.rb000066400000000000000000000163431323370221500222040ustar00rootroot00000000000000require 'fileutils' require "pathname" require 'vagrant/util/safe_chdir' require 'vagrant/util/subprocess' require 'vagrant/util/presence' module Vagrant module Action module General # A general packaging (tar) middleware. Given the following options, # it will do the right thing: # # * package.output - The filename of the outputted package. # * package.include - An array of files to include in the package. # * package.directory - The directory which contains the contents to # compress into the package. # # This middleware always produces the final file in the current working # directory (FileUtils.pwd) class Package include Util # Perform sanity validations that the provided output filepath is sane. # In particular, this function validates: # # - The output path is a regular file (not a directory or symlink) # - No file currently exists at the given path # - A directory of package files was actually provided (internal) # # @param [String] output path to the output file # @param [String] directory path to a directory containing the files def self.validate!(output, directory) filename = File.basename(output.to_s) output = fullpath(output) if File.directory?(output) raise Vagrant::Errors::PackageOutputDirectory end if File.exist?(output) raise Vagrant::Errors::PackageOutputExists, filename: filename end if !Vagrant::Util::Presence.present?(directory) || !File.directory?(directory) raise Vagrant::Errors::PackageRequiresDirectory end end # Calculate the full path of the given path, relative to the current # working directory (where the command was run). # # @param [String] output the relative path def self.fullpath(output) File.expand_path(output, Dir.pwd) end # The path to the final output file. # @return [String] attr_reader :fullpath def initialize(app, env) @app = app env["package.files"] ||= {} env["package.output"] ||= "package.box" @fullpath = self.class.fullpath(env["package.output"]) end def call(env) @env = env self.class.validate!(env["package.output"], env["package.directory"]) package_with_folder_path if env["package.output"].include?(File::SEPARATOR) raise Errors::PackageOutputDirectory if File.directory?(fullpath) @app.call(env) @env[:ui].info I18n.t("vagrant.actions.general.package.compressing", fullpath: fullpath) copy_include_files setup_private_key compress end def package_with_folder_path folder_path = File.expand_path("..", @fullpath) create_box_folder(folder_path) unless File.directory?(folder_path) end def create_box_folder(folder_path) @env[:ui].info(I18n.t("vagrant.actions.general.package.box_folder", folder_path: folder_path)) FileUtils.mkdir_p(folder_path) end def recover(env) @env = env # There are certain exceptions that we don't delete the file for. ignore_exc = [Errors::PackageOutputDirectory, Errors::PackageOutputExists] ignore_exc.each do |exc| return if env["vagrant.error"].is_a?(exc) end # Cleanup any packaged files if the packaging failed at some point. File.delete(fullpath) if File.exist?(fullpath) end # This method copies the include files (passed in via command line) # to the temporary directory so they are included in a sub-folder within # the actual box def copy_include_files include_directory = Pathname.new(@env["package.directory"]).join("include") @env["package.files"].each do |from, dest| # We place the file in the include directory to = include_directory.join(dest) @env[:ui].info I18n.t("vagrant.actions.general.package.packaging", file: from) FileUtils.mkdir_p(to.parent) # Copy direcotry contents recursively. if File.directory?(from) FileUtils.cp_r(Dir.glob(from), to.parent, preserve: true) else FileUtils.cp(from, to, preserve: true) end end rescue Errno::EEXIST => e raise if !e.to_s.include?("symlink") # The directory contains symlinks. Show a nicer error. raise Errors::PackageIncludeSymlink end # Compress the exported file into a package def compress # Get the output path. We have to do this up here so that the # pwd returns the proper thing. output_path = fullpath.to_s # Switch into that directory and package everything up Util::SafeChdir.safe_chdir(@env["package.directory"]) do # Find all the files in our current directory and tar it up! files = Dir.glob(File.join(".", "*")) # Package! Util::Subprocess.execute("bsdtar", "-czf", output_path, *files) end end # This will copy the generated private key into the box and use # it for SSH by default. We have to do this because we now generate # random keypairs on boot, so packaged boxes would stop working # without this. def setup_private_key # If we don't have machine, we do nothing (weird) return if !@env[:machine] # If we don't have a data dir, we also do nothing (base package) return if !@env[:machine].data_dir # If we don't have a generated private key, we do nothing path = @env[:machine].data_dir.join("private_key") if !path.file? # If we have a private key that was copied into this box, # then we copy that. This is a bit of a heuristic and can be a # security risk if the key is named the correct thing, but # we'll take that risk for dev environments. (@env[:machine].config.ssh.private_key_path || []).each do |p| # If we have the correctly named key, copy it if File.basename(p) == "vagrant_private_key" path = Pathname.new(p) break end end end # If we still have no matching key, do nothing return if !path.file? # Copy it into our box directory dir = Pathname.new(@env["package.directory"]) new_path = dir.join("vagrant_private_key") FileUtils.cp(path, new_path) # Append it to the Vagrantfile (or create a Vagrantfile) vf_path = dir.join("Vagrantfile") mode = "w+" mode = "a" if vf_path.file? vf_path.open(mode) do |f| f.binmode f.puts f.puts %Q[Vagrant.configure("2") do |config|] f.puts %Q[ config.ssh.private_key_path = File.expand_path("../vagrant_private_key", __FILE__)] f.puts %Q[end] end end end end end end vagrant-2.0.2/lib/vagrant/action/general/package_setup_files.rb000066400000000000000000000025671323370221500246110ustar00rootroot00000000000000module Vagrant module Action module General class PackageSetupFiles def initialize(app, env) @app = app env["package.include"] ||= [] env["package.vagrantfile"] ||= nil end def call(env) files = {} env["package.include"].each do |file| source = Pathname.new(file) dest = nil # If the source is relative then we add the file as-is to the include # directory. Otherwise, we copy only the file into the root of the # include directory. Kind of strange, but seems to match what people # expect based on history. if source.relative? dest = source else dest = source.basename end # Assign the mapping files[file] = dest end if env["package.vagrantfile"] # Vagrantfiles are treated special and mapped to a specific file files[env["package.vagrantfile"]] = "_Vagrantfile" end # Verify the mapping files.each do |from, _| raise Vagrant::Errors::PackageIncludeMissing, file: from if !File.exist?(from) end # Save the mapping env["package.files"] = files @app.call(env) end end end end end vagrant-2.0.2/lib/vagrant/action/general/package_setup_folders.rb000066400000000000000000000016361323370221500251410ustar00rootroot00000000000000require "fileutils" require_relative "package" module Vagrant module Action module General class PackageSetupFolders include Vagrant::Util::Presence def initialize(app, env) @app = app end def call(env) env["package.output"] ||= "package.box" env["package.directory"] ||= Dir.mktmpdir("vagrant-package-", env[:tmp_path]) # Match up a couple environmental variables so that the other parts of # Vagrant will do the right thing. env["export.temp_dir"] = env["package.directory"] Vagrant::Action::General::Package.validate!( env["package.output"], env["package.directory"]) @app.call(env) end def recover(env) dir = env["package.directory"] if File.exist?(dir) FileUtils.rm_rf(dir) end end end end end end vagrant-2.0.2/lib/vagrant/action/hook.rb000066400000000000000000000061731323370221500201340ustar00rootroot00000000000000module Vagrant module Action # This class manages hooks into existing {Builder} stacks, and lets you # add and remove middleware classes. This is the primary method by which # plugins can hook into built-in middleware stacks. class Hook # This is a hash of the middleware to prepend to a certain # other middleware. # # @return [Hash>] attr_reader :before_hooks # This is a hash of the middleware to append to a certain other # middleware. # # @return [Hash>] attr_reader :after_hooks # This is a list of the hooks to just prepend to the beginning # # @return [Array] attr_reader :prepend_hooks # This is a list of the hooks to just append to the end # # @return [Array] attr_reader :append_hooks def initialize @before_hooks = Hash.new { |h, k| h[k] = [] } @after_hooks = Hash.new { |h, k| h[k] = [] } @prepend_hooks = [] @append_hooks = [] end # Add a middleware before an existing middleware. # # @param [Class] existing The existing middleware. # @param [Class] new The new middleware. def before(existing, new, *args, &block) @before_hooks[existing] << [new, args, block] end # Add a middleware after an existing middleware. # # @param [Class] existing The existing middleware. # @param [Class] new The new middleware. def after(existing, new, *args, &block) @after_hooks[existing] << [new, args, block] end # Append a middleware to the end of the stack. Note that if the # middleware sequence ends early, then the new middleware won't # be run. # # @param [Class] new The middleware to append. def append(new, *args, &block) @append_hooks << [new, args, block] end # Prepend a middleware to the beginning of the stack. # # @param [Class] new The new middleware to prepend. def prepend(new, *args, &block) @prepend_hooks << [new, args, block] end # This applies the given hook to a builder. This should not be # called directly. # # @param [Builder] builder def apply(builder, options=nil) options ||= {} if !options[:no_prepend_or_append] # Prepends first @prepend_hooks.each do |klass, args, block| builder.insert(0, klass, *args, &block) end # Appends @append_hooks.each do |klass, args, block| builder.use(klass, *args, &block) end end # Before hooks @before_hooks.each do |key, list| next if !builder.index(key) list.each do |klass, args, block| builder.insert_before(key, klass, *args, &block) end end # After hooks @after_hooks.each do |key, list| next if !builder.index(key) list.each do |klass, args, block| builder.insert_after(key, klass, *args, &block) end end end end end end vagrant-2.0.2/lib/vagrant/action/runner.rb000066400000000000000000000046401323370221500205020ustar00rootroot00000000000000require 'log4r' require 'vagrant/action/hook' require 'vagrant/util/busy' module Vagrant module Action class Runner @@reported_interrupt = false def initialize(globals=nil, &block) @globals = globals || {} @lazy_globals = block @logger = Log4r::Logger.new("vagrant::action::runner") end def run(callable_id, options=nil) callable = callable_id if !callable.kind_of?(Builder) if callable_id.kind_of?(Class) || callable_id.respond_to?(:call) callable = Builder.build(callable_id) end end if !callable || !callable.respond_to?(:call) raise ArgumentError, "Argument to run must be a callable object or registered action." end # Create the initial environment with the options given environment = {} environment.merge!(@globals) environment.merge!(@lazy_globals.call) if @lazy_globals environment.merge!(options || {}) # Setup the action hooks hooks = Vagrant.plugin("2").manager.action_hooks(environment[:action_name]) if !hooks.empty? @logger.info("Preparing hooks for middleware sequence...") environment[:action_hooks] = hooks.map do |hook_proc| Hook.new.tap do |h| hook_proc.call(h) end end @logger.info("#{environment[:action_hooks].length} hooks defined.") end # Run the action chain in a busy block, marking the environment as # interrupted if a SIGINT occurs, and exiting cleanly once the # chain has been run. ui = environment[:ui] if environment.key?(:ui) int_callback = lambda do if environment[:interrupted] ui.error I18n.t("vagrant.actions.runner.exit_immediately") if ui abort end ui.warn I18n.t("vagrant.actions.runner.waiting_cleanup") if ui && !@@reported_interrupt environment[:interrupted] = true @@reported_interrupt = true end # 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) } # Return the environment in case there are things in there that # the caller wants to use. environment end end end end vagrant-2.0.2/lib/vagrant/action/warden.rb000066400000000000000000000070051323370221500204470ustar00rootroot00000000000000require "log4r" module Vagrant module Action # The action warden is a middleware which injects itself between # every other middleware, watching for exceptions which are raised # and performing proper cleanup on every action by calling the `recover` # method. The warden therefore allows middlewares to not worry about # exceptional events, and by providing a simple callback, can clean up # in any erroneous case. # # Warden will "just work" behind the scenes, and is not of particular # interest except to those who are curious about the internal workings # of Vagrant. class Warden attr_accessor :actions, :stack def initialize(actions, env) @stack = [] @actions = actions.map { |m| finalize_action(m, env) } @logger = Log4r::Logger.new("vagrant::action::warden") @last_error = nil end def call(env) return if @actions.empty? begin # Call the next middleware in the sequence, appending to the stack # of "recoverable" middlewares in case something goes wrong! raise Errors::VagrantInterrupt if env[:interrupted] action = @actions.shift @logger.info("Calling IN action: #{action}") @stack.unshift(action).first.call(env) raise Errors::VagrantInterrupt if env[:interrupted] @logger.info("Calling OUT action: #{action}") rescue SystemExit # This means that an "exit" or "abort" was called. In these cases, # we just exit immediately. raise rescue Exception => e # We guard this so that the Warden only outputs this once for # an exception that bubbles up. if e != @last_error @logger.error("Error occurred: #{e}") @last_error = e end env["vagrant.error"] = e # Something went horribly wrong. Start the rescue chain then # reraise the exception to properly kick us out of limbo here. recover(env) raise end end # We implement the recover method ourselves in case a Warden is # embedded within another Warden. To recover, we just do our own # recovery process on our stack. def recover(env) @logger.info("Beginning recovery process...") @stack.each do |act| if act.respond_to?(:recover) @logger.info("Calling recover: #{act}") act.recover(env) end end @logger.info("Recovery complete.") # Clear stack so that warden down the middleware chain doesn't # rescue again. @stack.clear end # A somewhat confusing function which simply initializes each # middleware properly to call the next middleware in the sequence. def finalize_action(action, env) klass, args, block = action # Default the arguments to an empty array. Otherwise in Ruby 1.8 # a `nil` args will actually pass `nil` into the class. args ||= [] 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) elsif klass.respond_to?(:call) # Make it a lambda which calls the item then forwards # up the chain lambda do |e| klass.call(e) self.call(e) end else raise "Invalid action: #{action.inspect}" end end end end end vagrant-2.0.2/lib/vagrant/batch_action.rb000066400000000000000000000125541323370221500203350ustar00rootroot00000000000000require 'thread' require "log4r" module Vagrant # This class executes multiple actions as a single batch, parallelizing # the action calls if possible. class BatchAction def initialize(allow_parallel=true) @actions = [] @allow_parallel = allow_parallel @logger = Log4r::Logger.new("vagrant::batch_action") end # Add an action to the batch of actions that will be run. # # This will **not** run the action now. The action will be run # when {#run} is called. # # @param [Machine] machine The machine to run the action on # @param [Symbol] action The action to run # @param [Hash] options Any additional options to send in. def action(machine, action, options=nil) @actions << [machine, action, options] end # Custom runs a custom proc against a machine. # # @param [Machine] machine The machine to run against. def custom(machine, &block) @actions << [machine, block, nil] end # Run all the queued up actions, parallelizing if possible. # # This will parallelize if and only if the provider of every machine # supports parallelization and parallelization is possible from # initialization of the class. def run par = false if @allow_parallel par = true @logger.info("Enabling parallelization by default.") end if par @actions.each do |machine, _, _| if !machine.provider_options[:parallel] @logger.info("Disabling parallelization because provider doesn't support it: #{machine.provider_name}") par = false break end end end if par && @actions.length <= 1 @logger.info("Disabling parallelization because only executing one action") par = false end @logger.info("Batch action will parallelize: #{par.inspect}") threads = [] @actions.each do |machine, action, options| @logger.info("Starting action: #{machine} #{action} #{options}") # Create the new thread to run our action. This is basically just # calling the action but also contains some error handling in it # as well. thread = Thread.new do Thread.current[:error] = nil # Record our pid when we started in order to figure out if # we've forked... start_pid = Process.pid begin if action.is_a?(Proc) action.call(machine) else machine.send(:action, action, options) end rescue Exception => e # If we're not parallelizing, then raise the error. We also # don't raise the error if we've forked, because it'll hang # the process. raise if !par && Process.pid == start_pid # Store the exception that will be processed later Thread.current[:error] = e # We can only do the things below if we do not fork, otherwise # it'll hang the process. if Process.pid == start_pid # Let the user know that this process had an error early # so that they see it while other things are happening. machine.ui.error(I18n.t("vagrant.general.batch_notify_error")) end end # If we forked during the process run, we need to do a hard # exit here. Ruby's fork only copies the running process (which # would be us), so if we return from this thread, it results # in a zombie Ruby process. if Process.pid != start_pid # We forked. exit_status = true if Thread.current[:error] # We had an error, print the stack trace and exit immediately. exit_status = false error = Thread.current[:error] @logger.error(error.inspect) @logger.error(error.message) @logger.error(error.backtrace.join("\n")) end Process.exit!(exit_status) end end # Set some attributes on the thread for later thread[:machine] = machine if !par thread.join(THREAD_MAX_JOIN_TIMEOUT) while thread.alive? end threads << thread end errors = [] threads.each do |thread| # Wait for the thread to complete thread.join(THREAD_MAX_JOIN_TIMEOUT) while thread.alive? # If the thread had an error, then store the error to show later if thread[:error] e = thread[:error] # If the error isn't a Vagrant error, then store the backtrace # as well. if !thread[:error].is_a?(Errors::VagrantError) e = thread[:error] message = e.message message += "\n" message += "\n#{e.backtrace.join("\n")}" errors << I18n.t("vagrant.general.batch_unexpected_error", machine: thread[:machine].name, message: message) else errors << I18n.t("vagrant.general.batch_vagrant_error", machine: thread[:machine].name, message: thread[:error].message) end end end if !errors.empty? raise Errors::BatchMultiError, message: errors.join("\n\n") end end end end vagrant-2.0.2/lib/vagrant/box.rb000066400000000000000000000135461323370221500165110ustar00rootroot00000000000000require 'fileutils' require "tempfile" require "json" require "log4r" require "vagrant/box_metadata" require "vagrant/util/downloader" require "vagrant/util/platform" require "vagrant/util/safe_chdir" require "vagrant/util/subprocess" module Vagrant # Represents a "box," which is a package Vagrant environment that is used # as a base image when creating a new guest machine. class Box include Comparable # The box name. This is the logical name used when adding the box. # # @return [String] attr_reader :name # This is the provider that this box is built for. # # @return [Symbol] attr_reader :provider # The version of this box. # # @return [String] attr_reader :version # This is the directory on disk where this box exists. # # @return [Pathname] attr_reader :directory # This is the metadata for the box. This is read from the "metadata.json" # file that all boxes require. # # @return [Hash] attr_reader :metadata # This is the URL to the version info and other metadata for this # box. # # @return [String] attr_reader :metadata_url # This is used to initialize a box. # # @param [String] name Logical name of the box. # @param [Symbol] provider The provider that this box implements. # @param [Pathname] directory The directory where this box exists on # disk. def initialize(name, provider, version, directory, **opts) @name = name @version = version @provider = provider @directory = directory @metadata_url = opts[:metadata_url] metadata_file = directory.join("metadata.json") raise Errors::BoxMetadataFileNotFound, name: @name if !metadata_file.file? begin @metadata = JSON.parse(directory.join("metadata.json").read) rescue JSON::ParserError raise Errors::BoxMetadataCorrupted, name: @name end @logger = Log4r::Logger.new("vagrant::box") end # This deletes the box. This is NOT undoable. def destroy! # Delete the directory to delete the box. FileUtils.rm_r(@directory) # Just return true always true rescue Errno::ENOENT # This means the directory didn't exist. Not a problem. return true end # Checks if this box is in use according to the given machine # index and returns the entries that appear to be using the box. # # The entries returned, if any, are not tested for validity # with {MachineIndex::Entry#valid?}, so the caller should do that # if the caller cares. # # @param [MachineIndex] index # @return [Array] def in_use?(index) results = [] index.each do |entry| box_data = entry.extra_data["box"] next if !box_data # If all the data matches, record it if box_data["name"] == self.name && box_data["provider"] == self.provider.to_s && box_data["version"] == self.version.to_s results << entry end end return nil if results.empty? results end # Loads the metadata URL and returns the latest metadata associated # with this box. # # @param [Hash] download_options Options to pass to the downloader. # @return [BoxMetadata] def load_metadata(**download_options) tf = Tempfile.new("vagrant-load-metadata") tf.close url = @metadata_url if File.file?(url) || url !~ /^[a-z0-9]+:.*$/i url = File.expand_path(url) url = Util::Platform.cygwin_windows_path(url) url = "file:#{url}" end opts = { headers: ["Accept: application/json"] }.merge(download_options) Util::Downloader.new(url, tf.path, **opts).download! BoxMetadata.new(File.open(tf.path, "r")) rescue Errors::DownloaderError => e raise Errors::BoxMetadataDownloadError, message: e.extra_data[:message] ensure tf.unlink if tf end # Checks if the box has an update and returns the metadata, version, # and provider. If the box doesn't have an update that satisfies the # constraints, it will return nil. # # This will potentially make a network call if it has to load the # metadata from the network. # # @param [String] version Version constraints the update must # satisfy. If nil, the version constrain defaults to being a # larger version than this box. # @return [Array] def has_update?(version=nil, download_options: {}) if !@metadata_url raise Errors::BoxUpdateNoMetadata, name: @name end version += ", " if version version ||= "" version += "> #{@version}" md = self.load_metadata(download_options) newer = md.version(version, provider: @provider) return nil if !newer [md, newer, newer.provider(@provider)] end # This repackages this box and outputs it to the given path. # # @param [Pathname] path The full path (filename included) of where # to output this box. # @return [Boolean] true if this succeeds. def repackage(path) @logger.debug("Repackaging box '#{@name}' to: #{path}") Util::SafeChdir.safe_chdir(@directory) do # Find all the files in our current directory and tar it up! files = Dir.glob(File.join(".", "**", "*")).select { |f| File.file?(f) } # Package! Util::Subprocess.execute("bsdtar", "-czf", path.to_s, *files) end @logger.info("Repackaged box '#{@name}' successfully: #{path}") true end # Implemented for comparison with other boxes. Comparison is # implemented by comparing names and providers. def <=>(other) return super if !other.is_a?(self.class) # Comparison is done by composing the name and provider "#{@name}-#{@version}-#{@provider}" <=> "#{other.name}-#{other.version}-#{other.provider}" end end end vagrant-2.0.2/lib/vagrant/box_collection.rb000066400000000000000000000406151323370221500207210ustar00rootroot00000000000000require "digest/sha1" require "fileutils" require "monitor" require "tmpdir" require "log4r" require "vagrant/util/platform" require "vagrant/util/subprocess" module Vagrant # Represents a collection a boxes found on disk. This provides methods # for accessing/finding individual boxes, adding new boxes, or deleting # boxes. class BoxCollection TEMP_PREFIX = "vagrant-box-add-temp-".freeze VAGRANT_SLASH = "-VAGRANTSLASH-".freeze VAGRANT_COLON = "-VAGRANTCOLON-".freeze # The directory where the boxes in this collection are stored. # # A box collection matches a very specific folder structure that Vagrant # expects in order to easily manage and modify boxes. The folder structure # is the following: # # COLLECTION_ROOT/BOX_NAME/PROVIDER/metadata.json # # Where: # # * COLLECTION_ROOT - This is the root of the box collection, and is # the directory given to the initializer. # * BOX_NAME - The name of the box. This is a logical name given by # the user of Vagrant. # * PROVIDER - The provider that the box was built for (VirtualBox, # VMware, etc.). # * metadata.json - A simple JSON file that at the bare minimum # contains a "provider" key that matches the provider for the # box. This metadata JSON, however, can contain anything. # # @return [Pathname] attr_reader :directory # Initializes the collection. # # @param [Pathname] directory The directory that contains the collection # of boxes. def initialize(directory, options=nil) options ||= {} @directory = directory @hook = options[:hook] @lock = Monitor.new @temp_root = options[:temp_dir_root] @logger = Log4r::Logger.new("vagrant::box_collection") end # This adds a new box to the system. # # There are some exceptional cases: # * BoxAlreadyExists - The box you're attempting to add already exists. # * BoxProviderDoesntMatch - If the given box provider doesn't match the # actual box provider in the untarred box. # * BoxUnpackageFailure - An invalid tar file. # # Preconditions: # * File given in `path` must exist. # # @param [Pathname] path Path to the box file on disk. # @param [String] name Logical name for the box. # @param [String] version The version of this box. # @param [Array] providers The providers that this box can # be a part of. This will be verified with the `metadata.json` and is # meant as a basic check. If this isn't given, then whatever provider # the box represents will be added. # @param [Boolean] force If true, any existing box with the same name # and provider will be replaced. def add(path, name, version, **opts) providers = opts[:providers] providers = Array(providers) if providers provider = nil # A helper to check if a box exists. We store this in a variable # since we call it multiple times. check_box_exists = lambda do |box_formats| box = find(name, box_formats, version) next if !box if !opts[:force] @logger.error( "Box already exists, can't add: #{name} v#{version} #{box_formats.join(", ")}") raise Errors::BoxAlreadyExists, name: name, provider: box_formats.join(", "), version: version end # We're forcing, so just delete the old box @logger.info( "Box already exists, but forcing so removing: " + "#{name} v#{version} #{box_formats.join(", ")}") box.destroy! end with_collection_lock do log_provider = providers ? providers.join(", ") : "any provider" @logger.debug("Adding box: #{name} (#{log_provider}) from #{path}") # Verify the box doesn't exist early if we're given a provider. This # can potentially speed things up considerably since we don't need # to unpack any files. check_box_exists.call(providers) if providers # Create a temporary directory since we're not sure at this point if # the box we're unpackaging already exists (if no provider was given) with_temp_dir do |temp_dir| # Extract the box into a temporary directory. @logger.debug("Unpacking box into temporary directory: #{temp_dir}") result = Util::Subprocess.execute( "bsdtar", "-v", "-x", "-m", "-C", temp_dir.to_s, "-f", path.to_s) if result.exit_code != 0 raise Errors::BoxUnpackageFailure, output: result.stderr.to_s end # If we get a V1 box, we want to update it in place if v1_box?(temp_dir) @logger.debug("Added box is a V1 box. Upgrading in place.") temp_dir = v1_upgrade(temp_dir) end # We re-wrap ourselves in the safety net in case we upgraded. # If we didn't upgrade, then this is still safe because the # helper will only delete the directory if it exists with_temp_dir(temp_dir) do |final_temp_dir| # Get an instance of the box we just added before it is finalized # in the system so we can inspect and use its metadata. box = Box.new(name, nil, version, final_temp_dir) # Get the provider, since we'll need that to at the least add it # to the system or check that it matches what is given to us. box_provider = box.metadata["provider"] if providers found = providers.find { |p| p.to_sym == box_provider.to_sym } if !found @logger.error("Added box provider doesnt match expected: #{log_provider}") raise Errors::BoxProviderDoesntMatch, expected: log_provider, actual: box_provider end else # Verify the box doesn't already exist check_box_exists.call([box_provider]) end # We weren't given a provider, so store this one. provider = box_provider.to_sym # Create the directory for this box, not including the provider root_box_dir = @directory.join(dir_name(name)) box_dir = root_box_dir.join(version) box_dir.mkpath @logger.debug("Box directory: #{box_dir}") # This is the final directory we'll move it to final_dir = box_dir.join(provider.to_s) if final_dir.exist? @logger.debug("Removing existing provider directory...") final_dir.rmtree end # Move to final destination final_dir.mkpath # Recursively move individual files from the temporary directory # to the final location. We do this instead of moving the entire # directory to avoid issues on Windows. [GH-1424] copy_pairs = [[final_temp_dir, final_dir]] while !copy_pairs.empty? from, to = copy_pairs.shift from.children(true).each do |f| dest = to.join(f.basename) # We don't copy entire directories, so create the # directory and then add to our list to copy. if f.directory? dest.mkpath copy_pairs << [f, dest] next end # Copy the single file @logger.debug("Moving: #{f} => #{dest}") FileUtils.mv(f, dest) end end if opts[:metadata_url] root_box_dir.join("metadata_url").open("w") do |f| f.write(opts[:metadata_url]) end end end end end # Return the box find(name, provider, version) end # This returns an array of all the boxes on the system, given by # their name and their provider. # # @return [Array] Array of `[name, version, provider]` of the boxes # installed on this system. def all results = [] with_collection_lock do @logger.debug("Finding all boxes in: #{@directory}") @directory.children(true).each do |child| # Ignore non-directories, since files are not interesting to # us in our folder structure. next if !child.directory? box_name = undir_name(child.basename.to_s) # Otherwise, traverse the subdirectories and see what versions # we have. child.children(true).each do |versiondir| next if !versiondir.directory? next if versiondir.basename.to_s.start_with?(".") version = versiondir.basename.to_s versiondir.children(true).each do |provider| # 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})") results << [box_name, version, provider_name] else @logger.debug("Invalid box, ignoring: #{provider}") end end end end end # Sort the list to group like providers and properly ordered versions results.sort_by! do |box_result| [box_result[0], box_result[2], Gem::Version.new(box_result[1])] end results end # Find a box in the collection with the given name and provider. # # @param [String] name Name of the box (logical name). # @param [Array] providers Providers that the box implements. # @param [String] version Version constraints to adhere to. Example: # "~> 1.0" or "= 1.0, ~> 1.1" # @return [Box] The box found, or `nil` if not found. def find(name, providers, version) providers = Array(providers) # Build up the requirements we have requirements = version.to_s.split(",").map do |v| Gem::Requirement.new(v.strip) end with_collection_lock do box_directory = @directory.join(dir_name(name)) if !box_directory.directory? @logger.info("Box not found: #{name} (#{providers.join(", ")})") return nil end # Keep a mapping of Gem::Version mangled versions => directories. # ie. 0.1.0.pre.alpha.2 => 0.1.0-alpha.2 # This is so we can sort version numbers properly here, but still # refer to the real directory names in path checks below and pass an # unmangled version string to Box.new version_dir_map = {} versions = box_directory.children(true).map do |versiondir| next if !versiondir.directory? next if versiondir.basename.to_s.start_with?(".") version = Gem::Version.new(versiondir.basename.to_s) version_dir_map[version.to_s] = versiondir.basename.to_s version end.compact # Traverse through versions with the latest version first versions.sort.reverse.each do |v| if !requirements.all? { |r| r.satisfied_by?(v) } # Unsatisfied version requirements next end versiondir = box_directory.join(version_dir_map[v.to_s]) providers.each do |provider| provider_dir = versiondir.join(provider.to_s) next if !provider_dir.directory? @logger.info("Box found: #{name} (#{provider})") metadata_url = nil metadata_url_file = box_directory.join("metadata_url") metadata_url = metadata_url_file.read if metadata_url_file.file? if metadata_url && @hook hook_env = @hook.call( :authenticate_box_url, box_urls: [metadata_url]) metadata_url = hook_env[:box_urls].first end return Box.new( name, provider, version_dir_map[v.to_s], provider_dir, metadata_url: metadata_url, ) end end end nil end # This upgrades a v1.1 - v1.4 box directory structure up to a v1.5 # directory structure. This will raise exceptions if it fails in any # way. def upgrade_v1_1_v1_5 with_collection_lock do temp_dir = Pathname.new(Dir.mktmpdir(TEMP_PREFIX, @temp_root)) @directory.children(true).each do |boxdir| # Ignore all non-directories because they can't be boxes next if !boxdir.directory? box_name = boxdir.basename.to_s # If it is a v1 box, then we need to upgrade it first if v1_box?(boxdir) upgrade_dir = v1_upgrade(boxdir) FileUtils.mv(upgrade_dir, boxdir.join("virtualbox")) end # Create the directory for this box new_box_dir = temp_dir.join(dir_name(box_name), "0") new_box_dir.mkpath # Go through each provider and move it boxdir.children(true).each do |providerdir| FileUtils.cp_r(providerdir, new_box_dir.join(providerdir.basename)) end end # Move the folder into place @directory.rmtree FileUtils.mv(temp_dir.to_s, @directory.to_s) end end # Cleans the directory for a box by removing the folders that are # empty. def clean(name) return false if exists?(name) path = File.join(directory, dir_name(name)) FileUtils.rm_rf(path) end protected # Returns the directory name for the box of the given name. # # @param [String] name # @return [String] def dir_name(name) name = name.dup name.gsub!(":", VAGRANT_COLON) if Util::Platform.windows? name.gsub!("/", VAGRANT_SLASH) name end # Returns the directory name for the box cleaned up def undir_name(name) name = name.dup name.gsub!(VAGRANT_COLON, ":") name.gsub!(VAGRANT_SLASH, "/") name end # This checks if the given directory represents a V1 box on the # system. # # @param [Pathname] dir Directory where the box is unpacked. # @return [Boolean] def v1_box?(dir) # We detect a V1 box given by whether there is a "box.ovf" which # is a heuristic but is pretty accurate. dir.join("box.ovf").file? end # This upgrades the V1 box contained unpacked in the given directory # and returns the directory of the upgraded version. This is # _destructive_ to the contents of the old directory. That is, the # contents of the old V1 box will be destroyed or moved. # # Preconditions: # * `dir` is a valid V1 box. Verify with {#v1_box?} # # @param [Pathname] dir Directory where the V1 box is unpacked. # @return [Pathname] Path to the unpackaged V2 box. def v1_upgrade(dir) @logger.debug("Upgrading box in directory: #{dir}") temp_dir = Pathname.new(Dir.mktmpdir(TEMP_PREFIX, @temp_root)) @logger.debug("Temporary directory for upgrading: #{temp_dir}") # Move all the things into the temporary directory dir.children(true).each do |child| # Don't move the temp_dir next if child == temp_dir # Move every other directory into the temporary directory @logger.debug("Copying to upgrade directory: #{child}") FileUtils.mv(child, temp_dir.join(child.basename)) end # If there is no metadata.json file, make one, since this is how # we determine if the box is a V2 box. metadata_file = temp_dir.join("metadata.json") if !metadata_file.file? metadata_file.open("w") do |f| f.write(JSON.generate({ provider: "virtualbox" })) end end # Return the temporary directory temp_dir end # This locks the region given by the block with a lock on this # collection. def with_collection_lock @lock.synchronize do return yield end end # This is a helper that makes sure that our temporary directories # are cleaned up no matter what. # # @param [String] dir Path to a temporary directory # @return [Object] The result of whatever the yield is def with_temp_dir(dir=nil) dir ||= Dir.mktmpdir(TEMP_PREFIX, @temp_root) dir = Pathname.new(dir) yield dir ensure FileUtils.rm_rf(dir.to_s) end # Checks if a box with a given name exists. def exists?(box_name) all.any? { |box| box.first.eql?(box_name) } end end end vagrant-2.0.2/lib/vagrant/box_metadata.rb000066400000000000000000000073161323370221500203470ustar00rootroot00000000000000require "json" module Vagrant # BoxMetadata represents metadata about a box, including the name # it should have, a description of it, the versions it has, and # more. class BoxMetadata # The name that the box should be if it is added. # # @return [String] attr_accessor :name # The long-form human-readable description of a box. # # @return [String] attr_accessor :description # Loads the metadata associated with the box from the given # IO. # # @param [IO] io An IO object to read the metadata from. def initialize(io) begin @raw = JSON.load(io) rescue JSON::ParserError => e raise Errors::BoxMetadataMalformed, error: e.to_s end @raw ||= {} @name = @raw["name"] @description = @raw["description"] @version_map = (@raw["versions"] || []).map do |v| begin [Gem::Version.new(v["version"]), v] rescue ArgumentError raise Errors::BoxMetadataMalformedVersion, version: v["version"].to_s end end @version_map = Hash[@version_map] end # Returns data about a single version that is included in this # metadata. # # @param [String] version The version to return, this can also # be a constraint. # @return [Version] The matching version or nil if a matching # version was not found. def version(version, **opts) requirements = version.split(",").map do |v| Gem::Requirement.new(v.strip) end providers = nil providers = Array(opts[:provider]).map(&:to_sym) if opts[:provider] @version_map.keys.sort.reverse.each do |v| next if !requirements.all? { |r| r.satisfied_by?(v) } version = Version.new(@version_map[v]) next if (providers & version.providers).empty? if providers return version end nil end # Returns all the versions supported by this metadata. These # versions are sorted so the last element of the list is the # latest version. # # @return[Array] def versions @version_map.keys.sort.map(&:to_s) end # Represents a single version within the metadata. class Version # The version that this Version object represents. # # @return [String] attr_accessor :version def initialize(raw=nil) return if !raw @version = raw["version"] @provider_map = (raw["providers"] || []).map do |p| [p["name"].to_sym, p] end @provider_map = Hash[@provider_map] end # Returns a [Provider] for the given name, or nil if it isn't # supported by this version. def provider(name) p = @provider_map[name.to_sym] return nil if !p Provider.new(p) end # Returns the providers that are available for this version # of the box. # # @return [Array] def providers @provider_map.keys.map(&:to_sym) end end # Provider represents a single provider-specific box available # for a version for a box. class Provider # The name of the provider. # # @return [String] attr_accessor :name # The URL of the box. # # @return [String] attr_accessor :url # The checksum value for this box, if any. # # @return [String] attr_accessor :checksum # The type of checksum (if any) associated with this provider. # # @return [String] attr_accessor :checksum_type def initialize(raw) @name = raw["name"] @url = raw["url"] @checksum = raw["checksum"] @checksum_type = raw["checksum_type"] end end end end vagrant-2.0.2/lib/vagrant/bundler.rb000066400000000000000000000466111323370221500173530ustar00rootroot00000000000000require "monitor" require "pathname" require "set" require "tempfile" require "fileutils" require "uri" require "rubygems/package" require "rubygems/uninstaller" require "rubygems/name_tuple" require_relative "shared_helpers" require_relative "version" require_relative "util/safe_env" module Vagrant # This class manages Vagrant's interaction with Bundler. Vagrant uses # Bundler as a way to properly resolve all dependencies of Vagrant and # all Vagrant-installed plugins. class Bundler # Location of HashiCorp gem repository HASHICORP_GEMSTORE = "https://gems.hashicorp.com/".freeze # Default gem repositories DEFAULT_GEM_SOURCES = [ "https://rubygems.org/".freeze, HASHICORP_GEMSTORE ].freeze def self.instance @bundler ||= self.new end attr_reader :plugin_gem_path def initialize @plugin_gem_path = Vagrant.user_data_path.join("gems", RUBY_VERSION).freeze @logger = Log4r::Logger.new("vagrant::bundler") end # Initializes Bundler and the various gem paths so that we can begin # loading gems. This must only be called once. def init!(plugins, repair=false) # Add HashiCorp RubyGems source Gem.sources << HASHICORP_GEMSTORE # Generate dependencies for all registered plugins plugin_deps = plugins.map do |name, info| Gem::Dependency.new(name, info['gem_version'].to_s.empty? ? '> 0' : info['gem_version']) end @logger.debug("Current generated plugin dependency list: #{plugin_deps}") # Load dependencies into a request set for resolution request_set = Gem::RequestSet.new(*plugin_deps) # Never allow dependencies to be remotely satisfied during init request_set.remote = false repair_result = nil begin # Compose set for resolution composed_set = generate_vagrant_set # Resolve the request set to ensure proper activation order solution = request_set.resolve(composed_set) rescue Gem::UnsatisfiableDependencyError => failure if repair raise failure if @init_retried @logger.debug("Resolution failed but attempting to repair. Failure: #{failure}") install(plugins) @init_retried = true retry else raise end end # Activate the gems activate_solution(solution) full_vagrant_spec_list = Gem::Specification.find_all{true} + solution.map(&:full_spec) if(defined?(::Bundler)) @logger.debug("Updating Bundler with full specification list") ::Bundler.rubygems.replace_entrypoints(full_vagrant_spec_list) end Gem.post_reset do Gem::Specification.all = full_vagrant_spec_list end Gem::Specification.reset end # Removes any temporary files created by init def deinit # no-op end # Installs the list of plugins. # # @param [Hash] plugins # @return [Array] def install(plugins, local=false) internal_install(plugins, nil, local: local) end # Installs a local '*.gem' file so that Bundler can find it. # # @param [String] path Path to a local gem file. # @return [Gem::Specification] def install_local(path, opts={}) plugin_source = Gem::Source::SpecificFile.new(path) plugin_info = { plugin_source.spec.name => { "gem_version" => plugin_source.spec.version.to_s, "local_source" => plugin_source, "sources" => opts.fetch(:sources, []) } } @logger.debug("Installing local plugin - #{plugin_info}") internal_install(plugin_info, {}) plugin_source.spec end # Update updates the given plugins, or every plugin if none is given. # # @param [Hash] plugins # @param [Array] specific Specific plugin names to update. If # empty or nil, all plugins will be updated. def update(plugins, specific) specific ||= [] update = {gems: specific.empty? ? true : specific} internal_install(plugins, update) end # Clean removes any unused gems. def clean(plugins) @logger.debug("Cleaning Vagrant plugins of stale gems.") # Generate dependencies for all registered plugins plugin_deps = plugins.map do |name, info| gem_version = info['installed_gem_version'] gem_version = info['gem_version'] if gem_version.to_s.empty? gem_version = "> 0" if gem_version.to_s.empty? Gem::Dependency.new(name, gem_version) end @logger.debug("Current plugin dependency list: #{plugin_deps}") # Load dependencies into a request set for resolution request_set = Gem::RequestSet.new(*plugin_deps) # Never allow dependencies to be remotely satisfied during cleaning request_set.remote = false # Sets that we can resolve our dependencies from. Note that we only # resolve from the current set as all required deps are activated during # init. current_set = generate_vagrant_set # Collect all plugin specifications plugin_specs = Dir.glob(plugin_gem_path.join('specifications/*.gemspec').to_s).map do |spec_path| Gem::Specification.load(spec_path) end @logger.debug("Generating current plugin state solution set.") # Resolve the request set to ensure proper activation order solution = request_set.resolve(current_set) solution_specs = solution.map(&:full_spec) solution_full_names = solution_specs.map(&:full_name) # Find all specs installed to plugins directory that are not # found within the solution set plugin_specs.delete_if do |spec| solution_full_names.include?(spec.full_name) end @logger.debug("Specifications to be removed - #{plugin_specs.map(&:full_name)}") # Now delete all unused specs plugin_specs.each do |spec| @logger.debug("Uninstalling gem - #{spec.full_name}") Gem::Uninstaller.new(spec.name, version: spec.version, install_dir: plugin_gem_path, all: true, executables: true, force: true, ignore: true, ).uninstall_gem(spec) end solution.find_all do |spec| plugins.keys.include?(spec.name) end end # During the duration of the yielded block, Bundler loud output # is enabled. def verbose if block_given? initial_state = @verbose @verbose = true yield @verbose = initial_state else @verbose = true end end protected def internal_install(plugins, update, **extra) update = {} if !update.is_a?(Hash) skips = [] source_list = {} system_plugins = plugins.map do |plugin_name, plugin_info| plugin_name if plugin_info["system"] end.compact installer_set = VagrantSet.new(:both) installer_set.system_plugins = system_plugins # Generate all required plugin deps plugin_deps = plugins.map do |name, info| gem_version = info['gem_version'].to_s.empty? ? '> 0' : info['gem_version'] if update[:gems] == true || (update[:gems].respond_to?(:include?) && update[:gems].include?(name)) if Gem::Requirement.new(gem_version).exact? gem_version = "> 0" @logger.debug("Detected exact version match for `#{name}` plugin update. Reset to loose constraint #{gem_version.inspect}.") end skips << name end source_list[name] ||= [] if plugin_source = info.delete("local_source") installer_set.add_local(plugin_source.spec.name, plugin_source.spec, plugin_source) source_list[name] << plugin_source.path end Array(info["sources"]).each do |source| if !source.end_with?("/") source = source + "/" end source_list[name] << source end Gem::Dependency.new(name, gem_version) end if Vagrant.strict_dependency_enforcement @logger.debug("Enabling strict dependency enforcement") plugin_deps += vagrant_internal_specs.map do |spec| next if system_plugins.include?(spec.name) Gem::Dependency.new(spec.name, spec.version) end.compact else @logger.debug("Disabling strict dependency enforcement") end @logger.debug("Dependency list for installation:\n - " \ "#{plugin_deps.map{|d| "#{d.name} #{d.requirement}"}.join("\n - ")}") all_sources = source_list.values.flatten.uniq default_sources = DEFAULT_GEM_SOURCES & all_sources all_sources -= DEFAULT_GEM_SOURCES # Only allow defined Gem sources Gem.sources.clear @logger.debug("Enabling user defined remote RubyGems sources") all_sources.each do |src| begin next if File.file?(src) || URI.parse(src).scheme.nil? rescue URI::InvalidURIError next end @logger.debug("Adding RubyGems source #{src}") Gem.sources << src end @logger.debug("Enabling default remote RubyGems sources") default_sources.each do |src| @logger.debug("Adding source - #{src}") Gem.sources << src end validate_configured_sources! source_list.values.each{|srcs| srcs.delete_if{|src| default_sources.include?(src)}} installer_set.prefer_sources = source_list @logger.debug("Current source list for install: #{Gem.sources.to_a}") # Create the request set for the new plugins request_set = Gem::RequestSet.new(*plugin_deps) installer_set = Gem::Resolver.compose_sets( installer_set, generate_builtin_set(system_plugins), generate_plugin_set(skips) ) @logger.debug("Generating solution set for installation.") # Generate the required solution set for new plugins solution = request_set.resolve(installer_set) activate_solution(solution) @logger.debug("Installing required gems.") # Install all remote gems into plugin path. Set the installer to ignore dependencies # as we know the dependencies are satisfied and it will attempt to validate a gem's # dependencies are satisified by gems in the install directory (which will likely not # be true) result = request_set.install_into(plugin_gem_path.to_s, true, ignore_dependencies: true, prerelease: Vagrant.prerelease?, wrappers: true ) result = result.map(&:full_spec) result end # Generate the composite resolver set totally all of vagrant (builtin + plugin set) def generate_vagrant_set Gem::Resolver.compose_sets(generate_builtin_set, generate_plugin_set) end # @return [Array<[Gem::Specification, String]>] spec and directory pairs def vagrant_internal_specs list = {} directories = [Gem::Specification.default_specifications_dir] Gem::Specification.find_all{true}.each do |spec| list[spec.full_name] = spec end if(!defined?(::Bundler)) directories += Gem::Specification.dirs.find_all do |path| !path.start_with?(Gem.user_dir) end end Gem::Specification.each_spec(directories) do |spec| if !list[spec.full_name] list[spec.full_name] = spec end end list.values end # Iterates each configured RubyGem source to validate that it is properly # available. If source is unavailable an exception is raised. def validate_configured_sources! Gem.sources.each_source do |src| begin src.load_specs(:released) rescue Gem::Exception => source_error if ENV["VAGRANT_ALLOW_PLUGIN_SOURCE_ERRORS"] @logger.warn("Failed to load configured plugin source: #{src}!") @logger.warn("Error received attempting to load source (#{src}): #{source_error}") @logger.warn("Ignoring plugin source load failure due user request via env variable") else @logger.error("Failed to load configured plugin source `#{src}`: #{source_error}") raise Vagrant::Errors::PluginSourceError, source: src.uri.to_s, error_msg: source_error.message end end end end # Generate the builtin resolver set def generate_builtin_set(system_plugins=[]) builtin_set = BuiltinSet.new @logger.debug("Generating new builtin set instance.") vagrant_internal_specs.each do |spec| if !system_plugins.include?(spec.name) builtin_set.add_builtin_spec(spec) end end builtin_set end # Generate the plugin resolver set. Optionally provide specification names (short or # full) that should be ignored def generate_plugin_set(skip=[]) plugin_set = PluginSet.new @logger.debug("Generating new plugin set instance. Skip gems - #{skip}") Dir.glob(plugin_gem_path.join('specifications/*.gemspec').to_s).each do |spec_path| spec = Gem::Specification.load(spec_path) desired_spec_path = File.join(spec.gem_dir, "#{spec.name}.gemspec") # Vendor set requires the spec to be within the gem directory. Some gems will package their # spec file, and that's not what we want to load. if !File.exist?(desired_spec_path) || !FileUtils.cmp(spec.spec_file, desired_spec_path) File.write(desired_spec_path, spec.to_ruby) end next if skip.include?(spec.name) || skip.include?(spec.full_name) plugin_set.add_vendor_gem(spec.name, spec.gem_dir) end plugin_set end # Activate a given solution def activate_solution(solution) retried = false begin @logger.debug("Activating solution set: #{solution.map(&:full_name)}") solution.each do |activation_request| unless activation_request.full_spec.activated? @logger.debug("Activating gem #{activation_request.full_spec.full_name}") activation_request.full_spec.activate if(defined?(::Bundler)) @logger.debug("Marking gem #{activation_request.full_spec.full_name} loaded within Bundler.") ::Bundler.rubygems.mark_loaded activation_request.full_spec end end end rescue Gem::LoadError => e # Depending on the version of Ruby, the ordering of the solution set # will be either 0..n (molinillo) or n..0 (pre-molinillo). Instead of # attempting to determine what's in use, or if it has some how changed # again, just reverse order on failure and attempt again. if retried @logger.error("Failed to load solution set - #{e.class}: #{e}") matcher = e.message.match(/Could not find '(?[^']+)'/) if matcher && !matcher["gem_name"].empty? desired_activation_request = solution.detect do |request| request.name == matcher["gem_name"] end if desired_activation_request && !desired_activation_request.full_spec.activated? activation_request = desired_activation_request @logger.warn("Found misordered activation request for #{desired_activation_request.full_name}. Moving to solution HEAD.") solution.delete(desired_activation_request) solution.unshift(desired_activation_request) retry end end raise else @logger.debug("Failed to load solution set. Retrying with reverse order.") retried = true solution.reverse! retry end end end # This is a custom Gem::Resolver::InstallerSet. It will prefer sources which are # explicitly provided over default sources when matches are found. This is generally # the entire set used for performing full resolutions on install. class VagrantSet < Gem::Resolver::InstallerSet attr_accessor :prefer_sources attr_accessor :system_plugins def initialize(domain, defined_sources={}) @prefer_sources = defined_sources @system_plugins = [] super(domain) end # Allow InstallerSet to find matching specs, then filter # for preferred sources def find_all(req) result = super if system_plugins.include?(req.name) result.delete_if do |spec| spec.is_a?(Gem::Resolver::InstalledSpecification) end end subset = result.find_all do |idx_spec| preferred = false if prefer_sources[req.name] if idx_spec.source.respond_to?(:path) preferred = prefer_sources[req.name].include?(idx_spec.source.path.to_s) end if !preferred preferred = prefer_sources[req.name].include?(idx_spec.source.uri.to_s) end end preferred end subset.empty? ? result : subset end end # This is a custom Gem::Resolver::Set for use with vagrant "system" gems. It # allows the installed set of gems to be used for providing a solution while # enforcing strict constraints. This ensures that plugins cannot "upgrade" # gems that are builtin to vagrant itself. class BuiltinSet < Gem::Resolver::Set def initialize super @remote = false @specs = [] end def add_builtin_spec(spec) @specs.push(spec).uniq! end def find_all(req) @specs.select do |spec| allow_prerelease = spec.name == "vagrant" && Vagrant.prerelease? req.match?(spec, allow_prerelease) end.map do |spec| Gem::Resolver::InstalledSpecification.new(self, spec) end end end # This is a custom Gem::Resolver::Set for use with Vagrant plugins. It is # a modified Gem::Resolver::VendorSet that supports multiple versions of # a specific gem class PluginSet < Gem::Resolver::VendorSet ## # Adds a specification to the set with the given +name+ which has been # unpacked into the given +directory+. def add_vendor_gem(name, directory) gemspec = File.join(directory, "#{name}.gemspec") spec = Gem::Specification.load(gemspec) if !spec raise Gem::GemNotFoundException, "unable to find #{gemspec} for gem #{name}" end spec.full_gem_path = File.expand_path(directory) spec.base_dir = File.dirname(spec.base_dir) @specs[spec.name] ||= [] @specs[spec.name] << spec @directories[spec] = directory spec end ## # Returns an Array of VendorSpecification objects matching the # DependencyRequest +req+. def find_all(req) @specs.values.flatten.select do |spec| req.match?(spec) end.map do |spec| source = Gem::Source::Vendor.new(@directories[spec]) Gem::Resolver::VendorSpecification.new(self, spec, source) end end ## # Loads a spec with the given +name+. +version+, +platform+ and +source+ are # ignored. def load_spec (name, version, platform, source) version = Gem::Version.new(version) if !version.is_a?(Gem::Version) @specs.fetch(name, []).detect{|s| s.name == name && s.version == version} end end end end # Patch for Ruby 2.2 and Bundler to behave properly when uninstalling plugins if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3') if defined?(::Bundler) && !::Bundler::SpecSet.instance_methods.include?(:delete) class Gem::Specification def self.remove_spec(spec) Gem::Specification.reset end end end end vagrant-2.0.2/lib/vagrant/capability_host.rb000066400000000000000000000134231323370221500210710ustar00rootroot00000000000000module Vagrant # This module enables a class to host capabilities. Prior to being able # to use any capabilities, the `initialize_capabilities!` method must be # called. # # Capabilities allow small pieces of functionality to be plugged in using # the Vagrant plugin model. Capabilities even allow for a certain amount # of inheritence, where only a subset of capabilities may be implemented but # a parent implements the rest. # # Capabilities are used heavily in Vagrant for host/guest interactions. For # example, "mount_nfs_folder" is a guest-OS specific operation, so capabilities # defer these operations to the guest. module CapabilityHost # Initializes the capability system by detecting the proper capability # host to execute on and building the chain of capabilities to execute. # # @param [Symbol] host The host to use for the capabilities, or nil if # we should auto-detect it. # @param [Hash>] hosts Potential capability # hosts. The key is the name of the host, value[0] is a class that # implements `#detect?` and value[1] is a parent host (if any). # @param [Hash>] capabilities The capabilities # that are supported. The key is the host of the capability. Within that # is a hash where the key is the name of the capability and the value # is the class/module implementing it. def initialize_capabilities!(host, hosts, capabilities, *args) @cap_logger = Log4r::Logger.new( "vagrant::capability_host::#{self.class.to_s.downcase}") if host && !hosts[host] raise Errors::CapabilityHostExplicitNotDetected, value: host.to_s end if !host host = autodetect_capability_host(hosts, *args) if !host raise Errors::CapabilityHostNotDetected if !host end if !hosts[host] # This should never happen because the autodetect above uses the # hosts hash to look up hosts. And if an explicit host is specified, # we do another check higher up. raise "Internal error. Host not found: #{host}" end name = host host_info = hosts[name] host = host_info[0].new chain = [] chain << [name, host] # Build the proper chain of parents if there are any. # This allows us to do "inheritance" of capabilities later if host_info[1] parent_name = host_info[1] parent_info = hosts[parent_name] while parent_info chain << [parent_name, parent_info[0].new] parent_name = parent_info[1] parent_info = hosts[parent_name] end end @cap_host_chain = chain @cap_args = args @cap_caps = capabilities true end # Returns the chain of hosts that will be checked for capabilities. # # @return [Array>] def capability_host_chain @cap_host_chain end # Tests whether the given capability is possible. # # @param [Symbol] cap_name Capability name # @return [Boolean] def capability?(cap_name) !capability_module(cap_name.to_sym).nil? end # Executes the capability with the given name, optionally passing more # arguments onwards to the capability. If the capability returns a value, # it will be returned. # # @param [Symbol] cap_name Name of the capability def capability(cap_name, *args) cap_mod = capability_module(cap_name.to_sym) if !cap_mod raise Errors::CapabilityNotFound, cap: cap_name.to_s, host: @cap_host_chain[0][0].to_s end cap_method = nil begin cap_method = cap_mod.method(cap_name) rescue NameError raise Errors::CapabilityInvalid, cap: cap_name.to_s, host: @cap_host_chain[0][0].to_s end args = @cap_args + args @cap_logger.info( "Execute capability: #{cap_name} #{args.inspect} (#{@cap_host_chain[0][0]})") cap_method.call(*args) end protected def autodetect_capability_host(hosts, *args) @cap_logger.info("Autodetecting host type for #{args.inspect}") # Get the mapping of hosts with the most parents. We start searching # with the hosts with the most parents first. parent_count = {} hosts.each do |name, parts| parent_count[name] = 0 parent = parts[1] while parent parent_count[name] += 1 parent = hosts[parent] parent = parent[1] if parent end end # Now swap around the mapping so that it is a mapping of # count to the actual list of host names parent_count_to_hosts = {} parent_count.each do |name, count| parent_count_to_hosts[count] ||= [] parent_count_to_hosts[count] << name end sorted_counts = parent_count_to_hosts.keys.sort.reverse sorted_counts.each do |count| parent_count_to_hosts[count].each do |name| @cap_logger.debug("Trying: #{name}") host_info = hosts[name] host = host_info[0].new if host.detect?(*args) @cap_logger.info("Detected: #{name}!") return name end end end return nil end # Returns the registered module for a capability with the given name. # # @param [Symbol] cap_name # @return [Module] def capability_module(cap_name) @cap_logger.debug("Searching for cap: #{cap_name}") @cap_host_chain.each do |host_name, host| @cap_logger.debug("Checking in: #{host_name}") caps = @cap_caps[host_name] if caps && caps.key?(cap_name) @cap_logger.debug("Found cap: #{cap_name} in #{host_name}") return caps[cap_name] end end nil end end end vagrant-2.0.2/lib/vagrant/cli.rb000066400000000000000000000062771323370221500164730ustar00rootroot00000000000000require 'log4r' require 'optparse' module Vagrant # Manages the command line interface to Vagrant. class CLI < Vagrant.plugin("2", :command) def initialize(argv, env) super @logger = Log4r::Logger.new("vagrant::cli") @main_args, @sub_command, @sub_args = split_main_and_subcommand(argv) Util::CheckpointClient.instance.setup(env).check @logger.info("CLI: #{@main_args.inspect} #{@sub_command.inspect} #{@sub_args.inspect}") end def execute if @main_args.include?("-h") || @main_args.include?("--help") # Help is next in short-circuiting everything. Print # the help and exit. help return 0 end # If we reached this far then we must have a subcommand. If not, # then we also just print the help and exit. command_plugin = nil if @sub_command command_plugin = Vagrant.plugin("2").manager.commands[@sub_command.to_sym] end if !command_plugin || !@sub_command help return 1 end command_class = command_plugin[0].call @logger.debug("Invoking command class: #{command_class} #{@sub_args.inspect}") Util::CheckpointClient.instance.display # Initialize and execute the command class, returning the exit status. result = 0 begin result = command_class.new(@sub_args, @env).execute rescue Interrupt @env.ui.info(I18n.t("vagrant.cli_interrupt")) result = 1 end result = 0 if !result.is_a?(Integer) return result end # This prints out the help for the CLI. def help # We use the optionparser for this. Its just easier. We don't use # an optionparser above because I don't think the performance hits # of creating a whole object are worth checking only a couple flags. opts = OptionParser.new do |o| o.banner = "Usage: vagrant [options] []" o.separator "" o.on("-v", "--version", "Print the version and exit.") o.on("-h", "--help", "Print this help.") o.separator "" o.separator "Common commands:" # Add the available subcommands as separators in order to print them # out as well. commands = {} longest = 0 Vagrant.plugin("2").manager.commands.each do |key, data| # Skip non-primary commands. These only show up in extended # help output. next if !data[1][:primary] key = key.to_s klass = data[0].call commands[key] = klass.synopsis longest = key.length if key.length > longest end commands.keys.sort.each do |key| o.separator " #{key.ljust(longest+2)} #{commands[key]}" @env.ui.machine("cli-command", key.dup) end o.separator "" o.separator "For help on any individual command run `vagrant COMMAND -h`" o.separator "" o.separator "Additional subcommands are available, but are either more advanced" o.separator "or not commonly used. To see all subcommands, run the command" o.separator "`vagrant list-commands`." end @env.ui.info(opts.help, prefix: false) end end end vagrant-2.0.2/lib/vagrant/config.rb000066400000000000000000000042271323370221500171620ustar00rootroot00000000000000require "vagrant/registry" module Vagrant module Config autoload :Loader, 'vagrant/config/loader' autoload :VersionBase, 'vagrant/config/version_base' autoload :V1, 'vagrant/config/v1' autoload :V2, 'vagrant/config/v2' # This is a mutex used to guarantee that only one thread can load # procs at any given time. CONFIGURE_MUTEX = Mutex.new # This is the registry which keeps track of what configuration # versions are available, mapped by the version string used in # `Vagrant.configure` calls. VERSIONS = Registry.new VERSIONS.register("1") { V1::Loader } VERSIONS.register("2") { V2::Loader } # This is the order of versions. This is used by the loader to figure out # how to "upgrade" versions up to the desired (current) version. The # current version is always considered to be the last version in this # list. VERSIONS_ORDER = ["1", "2"] CURRENT_VERSION = VERSIONS_ORDER.last # This is the method which is called by all Vagrantfiles to configure Vagrant. # This method expects a block which accepts a single argument representing # an instance of the {Config::Top} class. # # Note that the block is not run immediately. Instead, it's proc is stored # away for execution later. def self.run(version="1", &block) # Store it for later @last_procs ||= [] @last_procs << [version.to_s, block] end # This is a method which will yield to a block and will capture all # ``Vagrant.configure`` calls, returning an array of `Proc`s. # # Wrapping this around anytime you call code which loads configurations # will force a mutex so that procs never get mixed up. This keeps # the configuration loading part of Vagrant thread-safe. def self.capture_configures CONFIGURE_MUTEX.synchronize do # Reset the last procs so that we start fresh @last_procs = [] # Yield to allow the caller to do whatever loading needed yield # Return the last procs we've seen while still in the mutex, # knowing we're safe. return @last_procs end end end end vagrant-2.0.2/lib/vagrant/config/000077500000000000000000000000001323370221500166305ustar00rootroot00000000000000vagrant-2.0.2/lib/vagrant/config/loader.rb000066400000000000000000000252051323370221500204270ustar00rootroot00000000000000require "pathname" require "log4r" module Vagrant module Config # This class is responsible for loading Vagrant configuration, # usually in the form of Vagrantfiles. # # Loading works by specifying the sources for the configuration # as well as the order the sources should be loaded. Configuration # set later always overrides those set earlier; this is how # configuration "scoping" is implemented. class Loader # Initializes a configuration loader. # # @param [Registry] versions A registry of the available versions and # their associated loaders. # @param [Array] version_order An array of the order of the versions # in the registry. This is used to determine if upgrades are # necessary. Additionally, the last version in this order is always # considered the "current" version. def initialize(versions, version_order) @logger = Log4r::Logger.new("vagrant::config::loader") @config_cache = {} @proc_cache = {} @sources = {} @versions = versions @version_order = version_order end # Set the configuration data for the given name. # # The `name` should be a symbol and must uniquely identify the data # being given. # # `data` can either be a path to a Ruby Vagrantfile or a `Proc` directly. # `data` can also be an array of such values. # # At this point, no configuration is actually loaded. Note that calling # `set` multiple times with the same name will override any previously # set values. In this way, the last set data for a given name wins. def set(name, sources) # Sources should be an array sources = [sources] if !sources.kind_of?(Array) reliably_inspected_sources = sources.reduce({}) { |accum, source| begin accum[source] = source.inspect rescue Encoding::CompatibilityError accum[source] = "" end accum } @logger.info("Set #{name.inspect} = #{reliably_inspected_sources.values}") # Gather the procs for every source, since that is what we care about. procs = [] sources.each do |source| if !@proc_cache.key?(source) # Load the procs for this source and cache them. This caching # avoids the issue where a file may have side effects when loading # and loading it multiple times causes unexpected behavior. @logger.debug("Populating proc cache for #{reliably_inspected_sources[source]}") @proc_cache[source] = procs_for_source(source, reliably_inspected_sources) end # Add on to the array of procs we're going to use procs.concat(@proc_cache[source]) end # Set this source by name. @sources[name] = procs end # This loads the configuration sources in the given order and returns # an actual configuration object that is ready to be used. # # @param [Array] order The order of configuration to load. # @return [Object] The configuration object. This is different for # each configuration version. def load(order) @logger.info("Loading configuration in order: #{order.inspect}") unknown_sources = @sources.keys - order if !unknown_sources.empty? @logger.error("Unknown config sources: #{unknown_sources.inspect}") end # Get the current version config class to use current_version = @version_order.last current_config_klass = @versions.get(current_version) # This will hold our result result = current_config_klass.init # Keep track of the warnings and errors that may come from # upgrading the Vagrantfiles warnings = [] errors = [] if !@sources[:root].nil? && @sources[:root].eql?(@sources[:home]) # Vagrants home dir is set to the same dir as its project directory # so we don't want to load and merge the same Vagrantfile config # and execute its settings/procs twice # # Note: This protection won't work if there are two separate but # identical Vagrantfiles in the home and project dir @logger.info("Duplicate Vagrantfile config objects detected in :root and :home.") @sources.delete(:home) @logger.info("Removed :home config from being loaded") end order.each do |key| next if !@sources.key?(key) @sources[key].each do |version, proc| if !@config_cache.key?(proc) @logger.debug("Loading from: #{key} (evaluating)") # Get the proper version loader for this version and load version_loader = @versions.get(version) begin version_config = version_loader.load(proc) rescue NameError => e line = "(unknown)" path = "(unknown)" if e.backtrace && e.backtrace[0] backtrace_tokens = e.backtrace[0].split(":") path = backtrace_tokens[0] backtrace_tokens.each do |part| if part =~ /\d+/ line = part.to_i break end end end raise Errors::VagrantfileNameError, path: path, line: line, message: e.message.sub(/' for .*$/, "'") end # Store the errors/warnings associated with loading this # configuration. We'll store these for later. version_warnings = [] version_errors = [] # If this version is not the current version, then we need # to upgrade to the latest version. if version != current_version @logger.debug("Upgrading config from version #{version} to #{current_version}") version_index = @version_order.index(version) current_index = @version_order.index(current_version) (version_index + 1).upto(current_index) do |index| next_version = @version_order[index] @logger.debug("Upgrading config to version #{next_version}") # Get the loader of this version and ask it to upgrade loader = @versions.get(next_version) upgrade_result = loader.upgrade(version_config) this_warnings = upgrade_result[1] this_errors = upgrade_result[2] @logger.debug("Upgraded to version #{next_version} with " + "#{this_warnings.length} warnings and " + "#{this_errors.length} errors") # Append loading this to the version warnings and errors version_warnings += this_warnings version_errors += this_errors # Store the new upgraded version version_config = upgrade_result[0] end end # Cache the loaded configuration along with any warnings # or errors so that they can be retrieved later. @config_cache[proc] = [version_config, version_warnings, version_errors] else @logger.debug("Loading from: #{key} (cache)") end # Merge the configurations cache_data = @config_cache[proc] result = current_config_klass.merge(result, cache_data[0]) # Append the total warnings/errors warnings += cache_data[1] errors += cache_data[2] end end @logger.debug("Configuration loaded successfully, finalizing and returning") [current_config_klass.finalize(result), warnings, errors] end protected # This returns an array of `Proc` objects for the given source. # The `Proc` objects returned will expect a single argument for # the configuration object and are expected to mutate this # configuration object. def procs_for_source(source, reliably_inspected_sources) # Convert all pathnames to strings so we just have their path source = source.to_s if source.is_a?(Pathname) if source.is_a?(Array) # An array must be formatted as [version, proc], so verify # that and then return it raise ArgumentError, "String source must have format [version, proc]" if source.length != 2 # Return it as an array since we're expected to return an array # of [version, proc] pairs, but an array source only has one. return [source] elsif source.is_a?(String) # Strings are considered paths, so load them return procs_for_path(source) else raise ArgumentError, "Unknown configuration source: #{reliably_inspected_sources[source]}" end end # This returns an array of `Proc` objects for the given path source. # # @param [String] path Path to the file which contains the proper # `Vagrant.configure` calls. # @return [Array] def procs_for_path(path) @logger.debug("Load procs for pathname: #{path}") return Config.capture_configures do begin Kernel.load path rescue SyntaxError => e # Report syntax errors in a nice way. raise Errors::VagrantfileSyntaxError, file: e.message rescue SystemExit # Continue raising that exception... raise rescue Vagrant::Errors::VagrantError # Continue raising known Vagrant errors since they already # contain well worded error messages and context. raise rescue Exception => e @logger.error("Vagrantfile load error: #{e.message}") @logger.error(e.backtrace.join("\n")) line = "(unknown)" if e.backtrace && e.backtrace[0] e.backtrace[0].split(":").each do |part| if part =~ /\d+/ line = part.to_i break end end end # Report the generic exception raise Errors::VagrantfileLoadError, path: path, line: line, exception_class: e.class, message: e.message end end end end end end vagrant-2.0.2/lib/vagrant/config/v1.rb000066400000000000000000000003411323370221500175010ustar00rootroot00000000000000module Vagrant module Config module V1 autoload :DummyConfig, "vagrant/config/v1/dummy_config" autoload :Loader, "vagrant/config/v1/loader" autoload :Root, "vagrant/config/v1/root" end end end vagrant-2.0.2/lib/vagrant/config/v1/000077500000000000000000000000001323370221500171565ustar00rootroot00000000000000vagrant-2.0.2/lib/vagrant/config/v1/dummy_config.rb000066400000000000000000000004701323370221500221640ustar00rootroot00000000000000module Vagrant module Config module V1 # This is a configuration object that can have anything done # to it. Anything, and it just appears to keep working. class DummyConfig def method_missing(name, *args, &block) DummyConfig.new end end end end end vagrant-2.0.2/lib/vagrant/config/v1/loader.rb000066400000000000000000000064751323370221500207650ustar00rootroot00000000000000require "vagrant/config/v1/root" module Vagrant module Config module V1 # This is the loader that handles configuration loading for V1 # configurations. class Loader < VersionBase # Returns a bare empty configuration object. # # @return [V1::Root] def self.init new_root_object end # Finalizes the configuration by making sure there is at least # one VM defined in it. def self.finalize(config) # Call the `#finalize` method on each of the configuration keys. # They're expected to modify themselves in our case. config.finalize! # Return the object config end # Loads the configuration for the given proc and returns a configuration # object. # # @param [Proc] config_proc # @return [Object] def self.load(config_proc) # Create a root configuration object root = new_root_object # Call the proc with the root config_proc.call(root) # Return the root object, which doubles as the configuration object # we actually use for accessing as well. root end # Merges two configuration objects. # # @param [V1::Root] old The older root config. # @param [V1::Root] new The newer root config. # @return [V1::Root] def self.merge(old, new) # Grab the internal states, we use these heavily throughout the process old_state = old.__internal_state new_state = new.__internal_state # The config map for the new object is the old one merged with the # new one. config_map = old_state["config_map"].merge(new_state["config_map"]) # Merge the keys. old_keys = old_state["keys"] new_keys = new_state["keys"] keys = {} old_keys.each do |key, old_value| if new_keys.key?(key) # We need to do a merge, which we expect to be available # on the config class itself. keys[key] = old_value.merge(new_keys[key]) else # We just take the old value, but dup it so that we can modify. keys[key] = old_value.dup end end new_keys.each do |key, new_value| # Add in the keys that the new class has that we haven't merged. if !keys.key?(key) keys[key] = new_value.dup end end # Return the final root object V1::Root.new(config_map, keys) end protected def self.new_root_object # Get all the registered configuration objects and use them. If # we're currently on version 1, then we load all the config objects, # otherwise we load only the upgrade safe ones, since we're # obviously being loaded for an upgrade. config_map = nil plugin_manager = Vagrant.plugin("1").manager if Config::CURRENT_VERSION == "1" config_map = plugin_manager.config else config_map = plugin_manager.config_upgrade_safe end # Create the configuration root object V1::Root.new(config_map) end end end end end vagrant-2.0.2/lib/vagrant/config/v1/root.rb000066400000000000000000000037401323370221500204720ustar00rootroot00000000000000require "set" module Vagrant module Config module V1 # This is the root configuration class. An instance of this is what # is passed into version 1 Vagrant configuration blocks. class Root # Initializes a root object that maps the given keys to specific # configuration classes. # # @param [Hash] config_map Map of key to config class. def initialize(config_map, keys=nil) @keys = keys || {} @config_map = config_map @missing_key_calls = Set.new end # We use method_missing as a way to get the configuration that is # used for Vagrant and load the proper configuration classes for # each. def method_missing(name, *args) return @keys[name] if @keys.key?(name) config_klass = @config_map[name.to_sym] if config_klass # Instantiate the class and return the instance @keys[name] = config_klass.new return @keys[name] else # Record access to a missing key as an error @missing_key_calls.add(name.to_s) return DummyConfig.new end end # Called to finalize this object just prior to it being used by # the Vagrant system. The "!" signifies that this is expected to # mutate itself. def finalize! @keys.each do |_key, instance| instance.finalize! end end # Returns the internal state of the root object. This is used # by outside classes when merging, and shouldn't be called directly. # Note the strange method name is to attempt to avoid any name # clashes with potential configuration keys. def __internal_state { "config_map" => @config_map, "keys" => @keys, "missing_key_calls" => @missing_key_calls } end end end end end vagrant-2.0.2/lib/vagrant/config/v2.rb000066400000000000000000000003411323370221500175020ustar00rootroot00000000000000module Vagrant module Config module V2 autoload :DummyConfig, "vagrant/config/v2/dummy_config" autoload :Loader, "vagrant/config/v2/loader" autoload :Root, "vagrant/config/v2/root" end end end vagrant-2.0.2/lib/vagrant/config/v2/000077500000000000000000000000001323370221500171575ustar00rootroot00000000000000vagrant-2.0.2/lib/vagrant/config/v2/dummy_config.rb000066400000000000000000000004701323370221500221650ustar00rootroot00000000000000module Vagrant module Config module V2 # This is a configuration object that can have anything done # to it. Anything, and it just appears to keep working. class DummyConfig def method_missing(name, *args, &block) DummyConfig.new end end end end end vagrant-2.0.2/lib/vagrant/config/v2/loader.rb000066400000000000000000000114331323370221500207540ustar00rootroot00000000000000require "vagrant/config/v2/root" module Vagrant module Config module V2 # This is the loader that handles configuration loading for V2 # configurations. class Loader < VersionBase # Returns a bare empty configuration object. # # @return [V2::Root] def self.init new_root_object end # Finalizes the configuration by making sure there is at least # one VM defined in it. def self.finalize(config) # Call the `#finalize` method on each of the configuration keys. # They're expected to modify themselves in our case. config.finalize! # Return the object config end # Loads the configuration for the given proc and returns a configuration # object. # # @param [Proc] config_proc # @return [Object] def self.load(config_proc) # Create a root configuration object root = new_root_object # Call the proc with the root config_proc.call(root) # Return the root object, which doubles as the configuration object # we actually use for accessing as well. root end # Merges two configuration objects. # # @param [V2::Root] old The older root config. # @param [V2::Root] new The newer root config. # @return [V2::Root] def self.merge(old, new) # Grab the internal states, we use these heavily throughout the process old_state = old.__internal_state new_state = new.__internal_state # Make sure we instantiate every key in the config so that we # merge every key. This avoids issues with the same reference # being part of the config. old_state["config_map"].each do |k, _| old.public_send(k) end new_state["config_map"].each do |k, _| new.public_send(k) end # The config map for the new object is the old one merged with the # new one. config_map = old_state["config_map"].merge(new_state["config_map"]) # Merge the keys. old_keys = old_state["keys"] new_keys = new_state["keys"] keys = {} old_keys.each do |key, old_value| if new_keys.key?(key) # We need to do a merge, which we expect to be available # on the config class itself. keys[key] = old_value.merge(new_keys[key]) else # We just take the old value, but dup it so that we can modify. keys[key] = old_value.dup end end new_keys.each do |key, new_value| # Add in the keys that the new class has that we haven't merged. if !keys.key?(key) keys[key] = new_value.dup end end # Merge the missing keys new_missing_key_calls = old_state["missing_key_calls"] + new_state["missing_key_calls"] # Return the final root object V2::Root.new(config_map).tap do |result| result.__set_internal_state({ "config_map" => config_map, "keys" => keys, "missing_key_calls" => new_missing_key_calls }) end end # Upgrade a V1 configuration to a V2 configuration. We do this by # creating a V2 configuration, and calling "upgrade" on each of the # V1 configurations, expecting them to set the right settings on the # new root. # # @param [V1::Root] old # @return [Array] A 3-tuple result. def self.upgrade(old) # Get a new root root = new_root_object # Store the warnings/errors warnings = [] errors = [] # Go through the old keys and upgrade them if they can be old.__internal_state["keys"].each do |_, old_value| if old_value.respond_to?(:upgrade) result = old_value.upgrade(root) # Sanity check to guard against random return values if result.is_a?(Array) warnings += result[0] errors += result[1] end end end old.__internal_state["missing_key_calls"].to_a.sort.each do |key| warnings << I18n.t("vagrant.config.loader.bad_v1_key", key: key) end [root, warnings, errors] end protected def self.new_root_object # Get all the registered plugins for V2 config_map = Vagrant.plugin("2").manager.config # Create the configuration root object V2::Root.new(config_map) end end end end end vagrant-2.0.2/lib/vagrant/config/v2/root.rb000066400000000000000000000076721323370221500205030ustar00rootroot00000000000000require "set" require "vagrant/config/v2/util" module Vagrant module Config module V2 # This is the root configuration class. An instance of this is what # is passed into version 2 Vagrant configuration blocks. class Root # Initializes a root object that maps the given keys to specific # configuration classes. # # @param [Hash] config_map Map of key to config class. def initialize(config_map, keys=nil) @keys = keys || {} @config_map = config_map @missing_key_calls = Set.new @logger = Log4r::Logger.new("vagrant::config") end # We use method_missing as a way to get the configuration that is # used for Vagrant and load the proper configuration classes for # each. def method_missing(name, *args) return @keys[name] if @keys.key?(name) config_klass = @config_map[name.to_sym] if config_klass # Instantiate the class and return the instance @keys[name] = config_klass.new return @keys[name] else @logger.debug("missing key request name=#{name} loc=#{caller.first}") # Record access to a missing key as an error @missing_key_calls.add(name.to_s) return DummyConfig.new end end # Called to finalize this object just prior to it being used by # the Vagrant system. The "!" signifies that this is expected to # mutate itself. def finalize! @config_map.each do |key, klass| if !@keys.key?(key) @keys[key] = klass.new end end @keys.each do |_key, instance| instance.finalize! instance._finalize! end end # This validates the configuration and returns a hash of error # messages by section. If there are no errors, an empty hash # is returned. # # @param [Environment] env # @return [Hash] def validate(machine) # Go through each of the configuration keys and validate errors = {} @keys.each do |_key, instance| if instance.respond_to?(:validate) # Validate this single item, and if we have errors then # we merge them into our total errors list. result = instance.validate(machine) if result && !result.empty? errors = Util.merge_errors(errors, result) end end end # Go through and delete empty keys errors.keys.each do |key| errors.delete(key) if errors[key].empty? end # If we have missing keys, record those as errors if !@missing_key_calls.empty? errors["Vagrant"] = @missing_key_calls.to_a.sort.map do |key| I18n.t("vagrant.config.root.bad_key", key: key) end end errors end # Returns the internal state of the root object. This is used # by outside classes when merging, and shouldn't be called directly. # Note the strange method name is to attempt to avoid any name # clashes with potential configuration keys. def __internal_state { "config_map" => @config_map, "keys" => @keys, "missing_key_calls" => @missing_key_calls } end # This sets the internal state. This is used by the core to do some # merging logic and shouldn't be used by the general public. def __set_internal_state(state) @config_map = state["config_map"] if state.key?("config_map") @keys = state["keys"] if state.key?("keys") @missing_key_calls = state["missing_key_calls"] if state.key?("missing_key_calls") end end end end end vagrant-2.0.2/lib/vagrant/config/v2/util.rb000066400000000000000000000007551323370221500204700ustar00rootroot00000000000000module Vagrant module Config module V2 class Util # This merges two error hashes from validate methods. # # @param [Hash] first # @param [Hash] second # @return [Hash] Merged result def self.merge_errors(first, second) first.dup.tap do |result| second.each do |key, value| result[key] ||= [] result[key] += value end end end end end end end vagrant-2.0.2/lib/vagrant/config/version_base.rb000066400000000000000000000065351323370221500216450ustar00rootroot00000000000000module Vagrant module Config # This is the base class for any configuration versions, and includes # the stub methods that configuaration versions must implement. Vagrant # supports configuration versioning so that backwards compatibility can be # maintained for past Vagrantfiles while newer configurations are added. # Vagrant only introduces new configuration versions for major versions # of Vagrant. class VersionBase # Returns an empty configuration object. This can be any kind of object, # since it is treated as an opaque value on the other side, used only # for things like calling into {merge}. # # @return [Object] def self.init raise NotImplementedError end # This is called just before configuration loading is complete of # a potentially completely-merged value to perform final touch-ups # to the configuration, if required. # # This is an optional method to implement. The default implementation # will simply return the same object. # # This will ONLY be called if this is the version that is being # used. In the case that an `upgrade` is called, this will never # be called. # # @param [Object] obj Final configuration object. # @param [Object] Finalized configuration object. def self.finalize(obj) obj end # Loads the configuration for the given proc and returns a configuration # object. The return value is treated as an opaque object, so it can be # anything you'd like. The return value is the object that is passed # into methods like {merge}, so it should be something you expect. # # @param [Proc] proc The proc that is to be configured. # @return [Object] def self.load(proc) raise NotImplementedError end # Merges two configuration objects, returning the merged object. # The values of `old` and `new` are the opaque objects returned by # {load} or {init}. # # Once again, the return object is treated as an opaque value by # the Vagrant configuration loader, so it can be anything you'd like. # # @param [Object] old Old configuration object. # @param [Object] new New configuration object. # @return [Object] The merged configuration object. def self.merge(old, new) raise NotImplementedError end # This is called if a previous version of configuration needs to be # upgraded to this version. Each version of configuration should know # how to upgrade the version immediately prior to it. This should be # a best effort upgrade that makes many assumptions. The goal is for # this to work in almost every case, but perhaps with some warnings. # The return value for this is a 3-tuple: `[object, warnings, errors]`, # where `object` is the upgraded configuration object, `warnings` is # an array of warning messages, and `errors` is an array of error # messages. # # @param [Object] old The version of the configuration object just # prior to this one. # @return [Array] The 3-tuple result. Please see the above documentation # for more information on the exact structure of this object. def self.upgrade(old) raise NotImplementedError end end end end vagrant-2.0.2/lib/vagrant/environment.rb000066400000000000000000001053651323370221500202660ustar00rootroot00000000000000require 'fileutils' require 'json' require 'pathname' require 'set' require 'thread' require 'log4r' require 'vagrant/util/file_mode' require 'vagrant/util/platform' require "vagrant/util/silence_warnings" require "vagrant/vagrantfile" require "vagrant/version" module Vagrant # A "Vagrant environment" represents a configuration of how Vagrant # should behave: data directories, working directory, UI output, # etc. In day-to-day usage, every `vagrant` invocation typically # leads to a single Vagrant environment. class Environment # This is the current version that this version of Vagrant is # compatible with in the home directory. # # @return [String] CURRENT_SETUP_VERSION = "1.5" DEFAULT_LOCAL_DATA = ".vagrant" # The `cwd` that this environment represents attr_reader :cwd # The persistent data directory where global data can be stored. It # is up to the creator of the data in this directory to properly # remove it when it is no longer needed. # # @return [Pathname] attr_reader :data_dir # The valid name for a Vagrantfile for this environment. attr_reader :vagrantfile_name # The {UI} object to communicate with the outside world. attr_reader :ui # This is the UI class to use when creating new UIs. attr_reader :ui_class # The directory to the "home" folder that Vagrant will use to store # global state. attr_reader :home_path # The directory to the directory where local, environment-specific # data is stored. attr_reader :local_data_path # The directory where temporary files for Vagrant go. attr_reader :tmp_path # The directory where boxes are stored. attr_reader :boxes_path # The path where the plugins are stored (gems) attr_reader :gems_path # The path to the default private key attr_reader :default_private_key_path # Initializes a new environment with the given options. The options # is a hash where the main available key is `cwd`, which defines where # the environment represents. There are other options available but # they shouldn't be used in general. If `cwd` is nil, then it defaults # to the `Dir.pwd` (which is the cwd of the executing process). def initialize(opts=nil) opts = { cwd: nil, home_path: nil, local_data_path: nil, ui_class: nil, vagrantfile_name: nil, }.merge(opts || {}) # Set the default working directory to look for the vagrantfile opts[:cwd] ||= ENV["VAGRANT_CWD"] if ENV.key?("VAGRANT_CWD") opts[:cwd] ||= Dir.pwd opts[:cwd] = Pathname.new(opts[:cwd]) if !opts[:cwd].directory? raise Errors::EnvironmentNonExistentCWD, cwd: opts[:cwd].to_s end opts[:cwd] = opts[:cwd].expand_path # Set the default ui class opts[:ui_class] ||= UI::Silent # Set the Vagrantfile name up. We append "Vagrantfile" and "vagrantfile" so that # those continue to work as well, but anything custom will take precedence. opts[:vagrantfile_name] ||= ENV["VAGRANT_VAGRANTFILE"] if \ ENV.key?("VAGRANT_VAGRANTFILE") opts[:vagrantfile_name] = [opts[:vagrantfile_name]] if \ opts[:vagrantfile_name] && !opts[:vagrantfile_name].is_a?(Array) # Set instance variables for all the configuration parameters. @cwd = opts[:cwd] @home_path = opts[:home_path] @vagrantfile_name = opts[:vagrantfile_name] @ui = opts[:ui_class].new @ui_class = opts[:ui_class] # This is the batch lock, that enforces that only one {BatchAction} # runs at a time from {#batch}. @batch_lock = Mutex.new @locks = {} @logger = Log4r::Logger.new("vagrant::environment") @logger.info("Environment initialized (#{self})") @logger.info(" - cwd: #{cwd}") # Setup the home directory @home_path ||= Vagrant.user_data_path @home_path = Util::Platform.fs_real_path(@home_path) @boxes_path = @home_path.join("boxes") @data_dir = @home_path.join("data") @gems_path = Vagrant::Bundler.instance.plugin_gem_path @tmp_path = @home_path.join("tmp") @machine_index_dir = @data_dir.join("machine-index") # Prepare the directories setup_home_path # Setup the local data directory. If a configuration path is given, # it is expanded relative to the root path. Otherwise, we use the # default (which is also expanded relative to the root path). if !root_path.nil? if !ENV["VAGRANT_DOTFILE_PATH"].to_s.empty? && !opts[:child] opts[:local_data_path] ||= Pathname.new(File.expand_path(ENV["VAGRANT_DOTFILE_PATH"], root_path)) else opts[:local_data_path] ||= root_path.join(DEFAULT_LOCAL_DATA) end end if opts[:local_data_path] @local_data_path = Pathname.new(File.expand_path(opts[:local_data_path], @cwd)) end @logger.debug("Effective local data path: #{@local_data_path}") # If we have a root path, load the ".vagrantplugins" file. if root_path plugins_file = root_path.join(".vagrantplugins") if plugins_file.file? @logger.info("Loading plugins file: #{plugins_file}") load plugins_file end end setup_local_data_path # Setup the default private key @default_private_key_path = @home_path.join("insecure_private_key") copy_insecure_private_key # Call the hooks that does not require configurations to be loaded # by using a "clean" action runner hook(:environment_plugins_loaded, runner: Action::Runner.new(env: self)) # Call the environment load hooks hook(:environment_load, runner: Action::Runner.new(env: self)) end # Return a human-friendly string for pretty printed or inspected # instances. # # @return [String] def inspect "#<#{self.class}: #{@cwd}>".encode('external') end # Action runner for executing actions in the context of this environment. # # @return [Action::Runner] def action_runner @action_runner ||= Action::Runner.new do { action_runner: action_runner, box_collection: boxes, hook: method(:hook), host: host, machine_index: machine_index, gems_path: gems_path, home_path: home_path, root_path: root_path, tmp_path: tmp_path, ui: @ui } end end # Returns a list of machines that this environment is currently # managing that physically have been created. # # An "active" machine is a machine that Vagrant manages that has # been created. The machine itself may be in any state such as running, # suspended, etc. but if a machine is "active" then it exists. # # Note that the machines in this array may no longer be present in # the Vagrantfile of this environment. In this case the machine can # be considered an "orphan." Determining which machines are orphan # and which aren't is not currently a supported feature, but will # be in a future version. # # @return [Array] def active_machines # We have no active machines if we have no data path return [] if !@local_data_path machine_folder = @local_data_path.join("machines") # If the machine folder is not a directory then we just return # an empty array since no active machines exist. return [] if !machine_folder.directory? # Traverse the machines folder accumulate a result result = [] machine_folder.children(true).each do |name_folder| # If this isn't a directory then it isn't a machine next if !name_folder.directory? name = name_folder.basename.to_s.to_sym name_folder.children(true).each do |provider_folder| # If this isn't a directory then it isn't a provider next if !provider_folder.directory? # If this machine doesn't have an ID, then ignore next if !provider_folder.join("id").file? provider = provider_folder.basename.to_s.to_sym result << [name, provider] end end # Return the results result end # This creates a new batch action, yielding it, and then running it # once the block is called. # # This handles the case where batch actions are disabled by the # VAGRANT_NO_PARALLEL environmental variable. def batch(parallel=true) parallel = false if ENV["VAGRANT_NO_PARALLEL"] @batch_lock.synchronize do BatchAction.new(parallel).tap do |b| # Yield it so that the caller can setup actions yield b # And run it! b.run end end end # Makes a call to the CLI with the given arguments as if they # came from the real command line (sometimes they do!). An example: # # env.cli("package", "--vagrantfile", "Vagrantfile") # def cli(*args) CLI.new(args.flatten, self).execute end # This returns the provider name for the default provider for this # environment. # # @return [Symbol] Name of the default provider. def default_provider(**opts) opts[:exclude] = Set.new(opts[:exclude]) if opts[:exclude] opts[:force_default] = true if !opts.key?(:force_default) opts[:check_usable] = true if !opts.key?(:check_usable) # Implement the algorithm from # https://www.vagrantup.com/docs/providers/basic_usage.html#default-provider # with additional steps 2.5 and 3.5 from # https://bugzilla.redhat.com/show_bug.cgi?id=1444492 # to allow system-configured provider priorities. # # 1. The --provider flag on a vagrant up is chosen above all else, if it is # present. # # (Step 1 is done by the caller; this method is only called if --provider # wasn't given.) # # 2. If the VAGRANT_DEFAULT_PROVIDER environmental variable is set, it # takes next priority and will be the provider chosen. default = ENV["VAGRANT_DEFAULT_PROVIDER"].to_s if default.empty? default = nil else default = default.to_sym @logger.debug("Default provider: `#{default}`") end # If we're forcing the default, just short-circuit and return # that (the default behavior) if default && opts[:force_default] @logger.debug("Using forced default provider: `#{default}`") return default end # Determine the config to use to look for provider definitions. By # default it is the global but if we're targeting a specific machine, # then look there. root_config = vagrantfile.config if opts[:machine] machine_info = vagrantfile.machine_config(opts[:machine], nil, nil) root_config = machine_info[:config] end # Get the list of providers within our configuration, in order. config = root_config.vm.__providers # Get the list of usable providers with their internally-declared # priorities. usable = [] Vagrant.plugin("2").manager.providers.each do |key, data| impl = data[0] popts = data[1] # Skip excluded providers next if opts[:exclude] && opts[:exclude].include?(key) # Skip providers that can't be defaulted, unless they're in our # config, in which case someone made our decision for us. if !config.include?(key) next if popts.key?(:defaultable) && !popts[:defaultable] end # Skip providers that aren't usable. next if opts[:check_usable] && !impl.usable?(false) # Each provider sets its own priority, defaulting to 5 so we can trust # it's always set. usable << [popts[:priority], key] end @logger.debug("Initial usable provider list: #{usable}") # Sort the usable providers by priority. Higher numbers are higher # priority, otherwise alpha sort. usable = usable.sort {|a, b| a[0] == b[0] ? a[1] <=> b[1] : b[0] <=> a[0]} .map {|prio, key| key} @logger.debug("Priority sorted usable provider list: #{usable}") # If we're not forcing the default, but it's usable and hasn't been # otherwise excluded, return it now. if usable.include?(default) @logger.debug("Using default provider `#{default}` as it was found in usable list.") return default end # 2.5. Vagrant will go through all of the config.vm.provider calls in the # Vagrantfile and try each in order. It will choose the first # provider that is usable and listed in VAGRANT_PREFERRED_PROVIDERS. preferred = ENV.fetch('VAGRANT_PREFERRED_PROVIDERS', '') .split(',') .map {|s| s.strip} .select {|s| !s.empty?} .map {|s| s.to_sym} @logger.debug("Preferred provider list: #{preferred}") config.each do |key| if usable.include?(key) && preferred.include?(key) @logger.debug("Using preferred provider `#{key}` detected in configuration and usable.") return key end end # 3. Vagrant will go through all of the config.vm.provider calls in the # Vagrantfile and try each in order. It will choose the first provider # that is usable. For example, if you configure Hyper-V, it will never # be chosen on Mac this way. It must be both configured and usable. config.each do |key| if usable.include?(key) @logger.debug("Using provider `#{key}` detected in configuration and usable.") return key end end # 3.5. Vagrant will go through VAGRANT_PREFERRED_PROVIDERS and find the # first plugin that reports it is usable. preferred.each do |key| if usable.include?(key) @logger.debug("Using preferred provider `#{key}` found in usable list.") return key end end # 4. Vagrant will go through all installed provider plugins (including the # ones that come with Vagrant), and find the first plugin that reports # it is usable. There is a priority system here: systems that are known # better have a higher priority than systems that are worse. For # example, if you have the VMware provider installed, it will always # take priority over VirtualBox. if !usable.empty? @logger.debug("Using provider `#{usable[0]}` as it is the highest priority in the usable list.") return usable[0] end # 5. If Vagrant still has not found any usable providers, it will error. # No providers available is a critical error for Vagrant. raise Errors::NoDefaultProvider end # Returns whether or not we know how to install the provider with # the given name. # # @return [Boolean] def can_install_provider?(name) host.capability?(provider_install_key(name)) end # Installs the provider with the given name. # # This will raise an exception if we don't know how to install the # provider with the given name. You should guard this call with # `can_install_provider?` for added safety. # # An exception will be raised if there are any failures installing # the provider. def install_provider(name) host.capability(provider_install_key(name)) end # Returns the collection of boxes for the environment. # # @return [BoxCollection] def boxes @_boxes ||= BoxCollection.new( boxes_path, hook: method(:hook), temp_dir_root: tmp_path) end # Returns the {Config::Loader} that can be used to load Vagrantflies # given the settings of this environment. # # @return [Config::Loader] def config_loader return @config_loader if @config_loader home_vagrantfile = nil root_vagrantfile = nil home_vagrantfile = find_vagrantfile(home_path) if home_path if root_path root_vagrantfile = find_vagrantfile(root_path, @vagrantfile_name) end @config_loader = Config::Loader.new( Config::VERSIONS, Config::VERSIONS_ORDER) @config_loader.set(:home, home_vagrantfile) if home_vagrantfile @config_loader.set(:root, root_vagrantfile) if root_vagrantfile @config_loader end # Loads another environment for the given Vagrantfile, sharing as much # useful state from this Environment as possible (such as UI and paths). # Any initialization options can be overidden using the opts hash. # # @param [String] vagrantfile Path to a Vagrantfile # @return [Environment] def environment(vagrantfile, **opts) path = File.expand_path(vagrantfile, root_path) file = File.basename(path) path = File.dirname(path) Util::SilenceWarnings.silence! do Environment.new({ child: true, cwd: path, home_path: home_path, ui_class: ui_class, vagrantfile_name: file, }.merge(opts)) end end # This defines a hook point where plugin action hooks that are registered # against the given name will be run in the context of this environment. # # @param [Symbol] name Name of the hook. # @param [Action::Runner] action_runner A custom action runner for running hooks. def hook(name, opts=nil) @logger.info("Running hook: #{name}") opts ||= {} opts[:callable] ||= Action::Builder.new opts[:runner] ||= action_runner opts[:action_name] = name opts[:env] = self opts.delete(:runner).run(opts.delete(:callable), opts) end # Returns the host object associated with this environment. # # @return [Class] def host return @host if defined?(@host) # Determine the host class to use. ":detect" is an old Vagrant config # that shouldn't be valid anymore, but we respect it here by assuming # its old behavior. No need to deprecate this because I thin it is # fairly harmless. host_klass = vagrantfile.config.vagrant.host host_klass = nil if host_klass == :detect begin @host = Host.new( host_klass, Vagrant.plugin("2").manager.hosts, Vagrant.plugin("2").manager.host_capabilities, self) rescue Errors::CapabilityHostNotDetected # If the auto-detect failed, then we create a brand new host # with no capabilities and use that. This should almost never happen # since Vagrant works on most host OS's now, so this is a "slow path" klass = Class.new(Vagrant.plugin("2", :host)) do def detect?(env); true; end end hosts = { generic: [klass, nil] } host_caps = {} @host = Host.new(:generic, hosts, host_caps, self) rescue Errors::CapabilityHostExplicitNotDetected => e raise Errors::HostExplicitNotDetected, e.extra_data end end # This acquires a process-level lock with the given name. # # The lock file is held within the data directory of this environment, # so make sure that all environments that are locking are sharing # the same data directory. # # This will raise Errors::EnvironmentLockedError if the lock can't # be obtained. # # @param [String] name Name of the lock, since multiple locks can # be held at one time. def lock(name="global", **opts) f = nil # If we don't have a block, then locking is useless, so ignore it return if !block_given? # This allows multiple locks in the same process to be nested return yield if @locks[name] || opts[:noop] # The path to this lock lock_path = data_dir.join("lock.#{name}.lock") @logger.debug("Attempting to acquire process-lock: #{name}") lock("dotlock", noop: name == "dotlock", retry: true) do f = File.open(lock_path, "w+") end # The file locking fails only if it returns "false." If it # succeeds it returns a 0, so we must explicitly check for # the proper error case. while f.flock(File::LOCK_EX | File::LOCK_NB) === false @logger.warn("Process-lock in use: #{name}") if !opts[:retry] raise Errors::EnvironmentLockedError, name: name end sleep 0.2 end @logger.info("Acquired process lock: #{name}") result = nil begin # Mark that we have a lock @locks[name] = true result = yield ensure # We need to make sure that no matter what this is always # reset to false so we don't think we have a lock when we # actually don't. @locks.delete(name) @logger.info("Released process lock: #{name}") end # Clean up the lock file, this requires another lock if name != "dotlock" lock("dotlock", retry: true) do f.close begin File.delete(lock_path) rescue @logger.error( "Failed to delete lock file #{lock_path} - some other thread " + "might be trying to acquire it. ignoring this error") end end end # Return the result return result ensure begin f.close if f rescue IOError end end # This executes the push with the given name, raising any exceptions that # occur. # # Precondition: the push is not nil and exists. def push(name) @logger.info("Getting push: #{name}") name = name.to_sym pushes = self.vagrantfile.config.push.__compiled_pushes if !pushes.key?(name) raise Vagrant::Errors::PushStrategyNotDefined, name: name, pushes: pushes.keys end strategy, config = pushes[name] push_registry = Vagrant.plugin("2").manager.pushes klass, _ = push_registry.get(strategy) if klass.nil? raise Vagrant::Errors::PushStrategyNotLoaded, name: strategy, pushes: push_registry.keys end klass.new(self, config).push end # The list of pushes defined in this Vagrantfile. # # @return [Array] def pushes self.vagrantfile.config.push.__compiled_pushes.keys end # This returns a machine with the proper provider for this environment. # The machine named by `name` must be in this environment. # # @param [Symbol] name Name of the machine (as configured in the # Vagrantfile). # @param [Symbol] provider The provider that this machine should be # backed by. # @param [Boolean] refresh If true, then if there is a cached version # it is reloaded. # @return [Machine] def machine(name, provider, refresh=false) @logger.info("Getting machine: #{name} (#{provider})") # Compose the cache key of the name and provider, and return from # the cache if we have that. cache_key = [name, provider] @machines ||= {} if refresh @logger.info("Refreshing machine (busting cache): #{name} (#{provider})") @machines.delete(cache_key) end if @machines.key?(cache_key) @logger.info("Returning cached machine: #{name} (#{provider})") return @machines[cache_key] end @logger.info("Uncached load of machine.") # Determine the machine data directory and pass it to the machine. machine_data_path = @local_data_path.join( "machines/#{name}/#{provider}") # Create the machine and cache it for future calls. This will also # return the machine from this method. @machines[cache_key] = vagrantfile.machine( name, provider, boxes, machine_data_path, self) end # The {MachineIndex} to store information about the machines. # # @return [MachineIndex] def machine_index @machine_index ||= MachineIndex.new(@machine_index_dir) end # This returns a list of the configured machines for this environment. # Each of the names returned by this method is valid to be used with # the {#machine} method. # # @return [Array] Configured machine names. def machine_names vagrantfile.machine_names end # This returns the name of the machine that is the "primary." In the # case of a single-machine environment, this is just the single machine # name. In the case of a multi-machine environment, then this can # potentially be nil if no primary machine is specified. # # @return [Symbol] def primary_machine_name vagrantfile.primary_machine_name end # The root path is the path where the top-most (loaded last) # Vagrantfile resides. It can be considered the project root for # this environment. # # @return [String] def root_path return @root_path if defined?(@root_path) root_finder = lambda do |path| # Note: To remain compatible with Ruby 1.8, we have to use # a `find` here instead of an `each`. vf = find_vagrantfile(path, @vagrantfile_name) return path if vf return nil if path.root? || !File.exist?(path) root_finder.call(path.parent) end @root_path = root_finder.call(cwd) end # Unload the environment, running completion hooks. The environment # should not be used after this (but CAN be, technically). It is # recommended to always immediately set the variable to `nil` after # running this so you can't accidentally run any more methods. Example: # # env.unload # env = nil # def unload hook(:environment_unload) end # Represents the default Vagrantfile, or the Vagrantfile that is # in the working directory or a parent of the working directory # of this environment. # # The existence of this function is primarily a convenience. There # is nothing stopping you from instantiating your own {Vagrantfile} # and loading machines in any way you see fit. Typical behavior of # Vagrant, however, loads this Vagrantfile. # # This Vagrantfile is comprised of two major sources: the Vagrantfile # in the user's home directory as well as the "root" Vagrantfile or # the Vagrantfile in the working directory (or parent). # # @return [Vagrantfile] def vagrantfile @vagrantfile ||= Vagrantfile.new(config_loader, [:home, :root]) end #--------------------------------------------------------------- # Load Methods #--------------------------------------------------------------- # This sets the `@home_path` variable properly. # # @return [Pathname] def setup_home_path @logger.info("Home path: #{@home_path}") # Setup the list of child directories that need to be created if they # don't already exist. dirs = [ @home_path, @home_path.join("rgloader"), @boxes_path, @data_dir, @gems_path, @tmp_path, @machine_index_dir, ] # Go through each required directory, creating it if it doesn't exist dirs.each do |dir| next if File.directory?(dir) begin @logger.info("Creating: #{dir}") FileUtils.mkdir_p(dir) rescue Errno::EACCES raise Errors::HomeDirectoryNotAccessible, home_path: @home_path.to_s end end # Attempt to write into the home directory to verify we can begin # Append a random suffix to avoid race conditions if Vagrant # is running in parallel with other Vagrant processes. suffix = (0...32).map { (65 + rand(26)).chr }.join path = @home_path.join("perm_test_#{suffix}") path.open("w") do |f| f.write("hello") end path.unlink rescue Errno::EACCES raise Errors::HomeDirectoryNotAccessible, home_path: @home_path.to_s end # Create the version file that we use to track the structure of # the home directory. If we have an old version, we need to explicitly # upgrade it. Otherwise, we just mark that its the current version. version_file = @home_path.join("setup_version") if version_file.file? version = version_file.read.chomp if version > CURRENT_SETUP_VERSION raise Errors::HomeDirectoryLaterVersion end case version when CURRENT_SETUP_VERSION # We're already good, at the latest version. when "1.1" # We need to update our directory structure upgrade_home_path_v1_1 # Delete the version file so we put our latest version in version_file.delete else raise Errors::HomeDirectoryUnknownVersion, path: @home_path.to_s, version: version end end if !version_file.file? @logger.debug( "Creating home directory version file: #{CURRENT_SETUP_VERSION}") version_file.open("w") do |f| f.write(CURRENT_SETUP_VERSION) end end # Create the rgloader/loader file so we can use encoded files. loader_file = @home_path.join("rgloader", "loader.rb") if !loader_file.file? source_loader = Vagrant.source_root.join("templates/rgloader.rb") FileUtils.cp(source_loader.to_s, loader_file.to_s) end end # This creates the local data directory and show an error if it # couldn't properly be created. def setup_local_data_path(force=false) if @local_data_path.nil? @logger.warn("No local data path is set. Local data cannot be stored.") return end @logger.info("Local data path: #{@local_data_path}") # If the local data path is a file, then we are probably seeing an # old (V1) "dotfile." In this case, we upgrade it. The upgrade process # will remove the old data file if it is successful. if @local_data_path.file? upgrade_v1_dotfile(@local_data_path) end # If we don't have a root path, we don't setup anything return if !force && root_path.nil? begin @logger.debug("Creating: #{@local_data_path}") FileUtils.mkdir_p(@local_data_path) rescue Errno::EACCES raise Errors::LocalDataDirectoryNotAccessible, local_data_path: @local_data_path.to_s end end protected # This method copies the private key into the home directory if it # doesn't already exist. # # This must be done because `ssh` requires that the key is chmod # 0600, but if Vagrant is installed as a separate user, then the # effective uid won't be able to read the key. So the key is copied # to the home directory and chmod 0600. def copy_insecure_private_key if !@default_private_key_path.exist? @logger.info("Copying private key to home directory") source = File.expand_path("keys/vagrant", Vagrant.source_root) destination = @default_private_key_path begin FileUtils.cp(source, destination) rescue Errno::EACCES raise Errors::CopyPrivateKeyFailed, source: source, destination: destination end end if !Util::Platform.windows? # On Windows, permissions don't matter as much, so don't worry # about doing chmod. if Util::FileMode.from_octal(@default_private_key_path.stat.mode) != "600" @logger.info("Changing permissions on private key to 0600") @default_private_key_path.chmod(0600) end end end # Finds the Vagrantfile in the given directory. # # @param [Pathname] path Path to search in. # @return [Pathname] def find_vagrantfile(search_path, filenames=nil) filenames ||= ["Vagrantfile", "vagrantfile"] filenames.each do |vagrantfile| current_path = search_path.join(vagrantfile) return current_path if current_path.file? end nil end # Returns the key used for the host capability for provider installs # of the given name. def provider_install_key(name) "provider_install_#{name}".to_sym end # This upgrades a home directory that was in the v1.1 format to the # v1.5 format. It will raise exceptions if anything fails. def upgrade_home_path_v1_1 if !ENV["VAGRANT_UPGRADE_SILENT_1_5"] @ui.ask(I18n.t("vagrant.upgrading_home_path_v1_5")) end collection = BoxCollection.new( @home_path.join("boxes"), temp_dir_root: tmp_path) collection.upgrade_v1_1_v1_5 end # This upgrades a Vagrant 1.0.x "dotfile" to the new V2 format. # # This is a destructive process. Once the upgrade is complete, the # old dotfile is removed, and the environment becomes incompatible for # Vagrant 1.0 environments. # # @param [Pathname] path The path to the dotfile def upgrade_v1_dotfile(path) @logger.info("Upgrading V1 dotfile to V2 directory structure...") # First, verify the file isn't empty. If it is an empty file, we # just delete it and go on with life. contents = path.read.strip if contents.strip == "" @logger.info("V1 dotfile was empty. Removing and moving on.") path.delete return end # Otherwise, verify there is valid JSON in here since a Vagrant # environment would always ensure valid JSON. This is a sanity check # to make sure we don't nuke a dotfile that is not ours... @logger.debug("Attempting to parse JSON of V1 file") json_data = nil begin json_data = JSON.parse(contents) @logger.debug("JSON parsed successfully. Things are okay.") rescue JSON::ParserError # The file could've been tampered with since Vagrant 1.0.x is # supposed to ensure that the contents are valid JSON. Show an error. raise Errors::DotfileUpgradeJSONError, state_file: path.to_s end # Alright, let's upgrade this guy to the new structure. Start by # backing up the old dotfile. backup_file = path.dirname.join(".vagrant.v1.#{Time.now.to_i}") @logger.info("Renaming old dotfile to: #{backup_file}") path.rename(backup_file) # Now, we create the actual local data directory. This should succeed # this time since we renamed the old conflicting V1. setup_local_data_path(true) if json_data["active"] @logger.debug("Upgrading to V2 style for each active VM") json_data["active"].each do |name, id| @logger.info("Upgrading dotfile: #{name} (#{id})") # Create the machine configuration directory directory = @local_data_path.join("machines/#{name}/virtualbox") FileUtils.mkdir_p(directory) # Write the ID file directory.join("id").open("w+") do |f| f.write(id) end end end # Upgrade complete! Let the user know @ui.info(I18n.t("vagrant.general.upgraded_v1_dotfile", backup_path: backup_file.to_s)) end end end vagrant-2.0.2/lib/vagrant/errors.rb000066400000000000000000000573621323370221500172410ustar00rootroot00000000000000# This file contains all of the internal errors in Vagrant's core # commands, actions, etc. module Vagrant # This module contains all of the internal errors in Vagrant's core. # These errors are _expected_ errors and as such don't typically represent # bugs in Vagrant itself. These are meant as a way to detect errors and # display them in a user-friendly way. # # # Defining a new Error # # To define a new error, inherit from {VagrantError}, which lets Vagrant # know that this is an expected error, and also gives you some helpers for # providing exit codes and error messages. An example is shown below, then # it is explained: # # class MyError < Vagrant::Errors::VagrantError # error_key "my_error" # end # # This creates an error with an I18n error key of "my_error." {VagrantError} # uses I18n to look up error messages, in the "vagrant.errors" namespace. So # in the above, the error message would be the translation of "vagrant.errors.my_error" # # If you don't want to use I18n, you can override the {#initialize} method and # set your own error message. # # # Raising an Error # # To raise an error, it is nothing special, just raise it like any normal # exception: # # raise MyError.new # # Eventually this exception will bubble out to the `vagrant` binary which # will show a nice error message. And if it is raised in the middle of a # middleware sequence, then {Action::Warden} will catch it and begin the # recovery process prior to exiting. module Errors # Main superclass of any errors in Vagrant. This provides some # convenience methods for setting the status code and error key. # The status code is used by the `vagrant` executable as the # error code, and the error key is used as a default message from # I18n. class VagrantError < StandardError # This is extra data passed into the message for translation. attr_accessor :extra_data def self.error_key(key=nil, namespace=nil) define_method(:error_key) { key } error_namespace(namespace) if namespace end def self.error_message(message) define_method(:error_message) { message } end def self.error_namespace(namespace) define_method(:error_namespace) { namespace } end def initialize(*args) key = args.shift if args.first.is_a?(Symbol) message = args.shift if args.first.is_a?(Hash) message ||= {} @extra_data = message.dup message[:_key] ||= error_key message[:_namespace] ||= error_namespace message[:_key] = key if key if message[:_key] message = translate_error(message) else message = error_message end super(message) end # The error message for this error. This is used if no error_key # is specified for a translatable error message. def error_message; "No error message"; end # The default error namespace which is used for the error key. # This can be overridden here or by calling the "error_namespace" # class method. def error_namespace; "vagrant.errors"; end # The key for the error message. This should be set using the # {error_key} method but can be overridden here if needed. def error_key; nil; end # This is the exit code that should be used when exiting from # this exception. # # @return [Integer] def status_code; 1; end protected def translate_error(opts) return nil if !opts[:_key] I18n.t("#{opts[:_namespace]}.#{opts[:_key]}", opts) end end class ActiveMachineWithDifferentProvider < VagrantError error_key(:active_machine_with_different_provider) end class BatchMultiError < VagrantError error_key(:batch_multi_error) end class BoxAddDirectVersion < VagrantError error_key(:box_add_direct_version) end class BoxAddMetadataMultiURL < VagrantError error_key(:box_add_metadata_multi_url) end class BoxAddNameMismatch < VagrantError error_key(:box_add_name_mismatch) end class BoxAddNameRequired < VagrantError error_key(:box_add_name_required) end class BoxAddNoMatchingProvider < VagrantError error_key(:box_add_no_matching_provider) end class BoxAddNoMatchingVersion < VagrantError error_key(:box_add_no_matching_version) end class BoxAddShortNotFound < VagrantError error_key(:box_add_short_not_found) end class BoxAlreadyExists < VagrantError error_key(:box_add_exists) end class BoxChecksumInvalidType < VagrantError error_key(:box_checksum_invalid_type) end class BoxChecksumMismatch < VagrantError error_key(:box_checksum_mismatch) end class BoxConfigChangingBox < VagrantError error_key(:box_config_changing_box) end class BoxMetadataCorrupted < VagrantError error_key(:box_metadata_corrupted) end class BoxMetadataDownloadError < VagrantError error_key(:box_metadata_download_error) end class BoxMetadataFileNotFound < VagrantError error_key(:box_metadata_file_not_found) end class BoxMetadataMalformed < VagrantError error_key(:box_metadata_malformed) end class BoxMetadataMalformedVersion < VagrantError error_key(:box_metadata_malformed_version) end class BoxNotFound < VagrantError error_key(:box_not_found) end class BoxNotFoundWithProvider < VagrantError error_key(:box_not_found_with_provider) end class BoxNotFoundWithProviderAndVersion < VagrantError error_key(:box_not_found_with_provider_and_version) end class BoxProviderDoesntMatch < VagrantError error_key(:box_provider_doesnt_match) end class BoxRemoveNotFound < VagrantError error_key(:box_remove_not_found) end class BoxRemoveProviderNotFound < VagrantError error_key(:box_remove_provider_not_found) end class BoxRemoveVersionNotFound < VagrantError error_key(:box_remove_version_not_found) end class BoxRemoveMultiProvider < VagrantError error_key(:box_remove_multi_provider) end class BoxRemoveMultiVersion < VagrantError error_key(:box_remove_multi_version) end class BoxServerNotSet < VagrantError error_key(:box_server_not_set) end class BoxUnpackageFailure < VagrantError error_key(:untar_failure, "vagrant.actions.box.unpackage") end class BoxUpdateMultiProvider < VagrantError error_key(:box_update_multi_provider) end class BoxUpdateNoMetadata < VagrantError error_key(:box_update_no_metadata) end class BoxVerificationFailed < VagrantError error_key(:failed, "vagrant.actions.box.verify") end class BundlerDisabled < VagrantError error_key(:bundler_disabled) end class BundlerError < VagrantError error_key(:bundler_error) end class CantReadMACAddresses < VagrantError error_key(:cant_read_mac_addresses) end class CapabilityHostExplicitNotDetected < VagrantError error_key(:capability_host_explicit_not_detected) end class CapabilityHostNotDetected < VagrantError error_key(:capability_host_not_detected) end class CapabilityInvalid < VagrantError error_key(:capability_invalid) end class CapabilityNotFound < VagrantError error_key(:capability_not_found) end class CFEngineBootstrapFailed < VagrantError error_key(:cfengine_bootstrap_failed) end class CFEngineCantAutodetectIP < VagrantError error_key(:cfengine_cant_autodetect_ip) end class CFEngineInstallFailed < VagrantError error_key(:cfengine_install_failed) end class CFEngineNotInstalled < VagrantError error_key(:cfengine_not_installed) end class CLIInvalidUsage < VagrantError error_key(:cli_invalid_usage) end class CLIInvalidOptions < VagrantError error_key(:cli_invalid_options) end class CloneNotFound < VagrantError error_key(:clone_not_found) end class CloneMachineNotFound < VagrantError error_key(:clone_machine_not_found) end class CommandDeprecated < VagrantError error_key(:command_deprecated) end class CommandUnavailable < VagrantError error_key(:command_unavailable) end class CommandUnavailableWindows < CommandUnavailable error_key(:command_unavailable_windows) end class CommunicatorNotFound < VagrantError error_key(:communicator_not_found) end class ConfigInvalid < VagrantError error_key(:config_invalid) end class ConfigUpgradeErrors < VagrantError error_key(:config_upgrade_errors) end class CopyPrivateKeyFailed < VagrantError error_key(:copy_private_key_failed) end class CorruptMachineIndex < VagrantError error_key(:corrupt_machine_index) end class DarwinMountFailed < VagrantError error_key(:darwin_mount_failed) end class DestroyRequiresForce < VagrantError error_key(:destroy_requires_force) end class DotfileUpgradeJSONError < VagrantError error_key(:dotfile_upgrade_json_error) end class DownloaderError < VagrantError error_key(:downloader_error) end class DownloaderInterrupted < DownloaderError error_key(:downloader_interrupted) end class DownloaderChecksumError < VagrantError error_key(:downloader_checksum_error) end class EnvInval < VagrantError error_key(:env_inval) end class EnvironmentNonExistentCWD < VagrantError error_key(:environment_non_existent_cwd) end class EnvironmentLockedError < VagrantError error_key(:environment_locked) end class HomeDirectoryLaterVersion < VagrantError error_key(:home_dir_later_version) end class HomeDirectoryNotAccessible < VagrantError error_key(:home_dir_not_accessible) end class HomeDirectoryUnknownVersion < VagrantError error_key(:home_dir_unknown_version) end class ForwardPortAdapterNotFound < VagrantError error_key(:forward_port_adapter_not_found) end class ForwardPortAutolistEmpty < VagrantError error_key(:auto_empty, "vagrant.actions.vm.forward_ports") end class ForwardPortCollision < VagrantError error_key(:collision_error, "vagrant.actions.vm.forward_ports") end class GuestCapabilityInvalid < VagrantError error_key(:guest_capability_invalid) end class GuestCapabilityNotFound < VagrantError error_key(:guest_capability_not_found) end class GuestExplicitNotDetected < VagrantError error_key(:guest_explicit_not_detected) end class GuestNotDetected < VagrantError error_key(:guest_not_detected) end class HostExplicitNotDetected < VagrantError error_key(:host_explicit_not_detected) end class LinuxMountFailed < VagrantError error_key(:linux_mount_failed) end class LinuxRDPClientNotFound < VagrantError error_key(:linux_rdp_client_not_found) end class LocalDataDirectoryNotAccessible < VagrantError error_key(:local_data_dir_not_accessible) end class MachineActionLockedError < VagrantError error_key(:machine_action_locked) end class MachineGuestNotReady < VagrantError error_key(:machine_guest_not_ready) end class MachineLocked < VagrantError error_key(:machine_locked) end class MachineNotFound < VagrantError error_key(:machine_not_found) end class MachineStateInvalid < VagrantError error_key(:machine_state_invalid) end class MultiVMTargetRequired < VagrantError error_key(:multi_vm_target_required) end class NetSSHException < VagrantError error_key(:net_ssh_exception) end class NetworkCollision < VagrantError error_key(:collides, "vagrant.actions.vm.host_only_network") end class NetworkAddressInvalid < VagrantError error_key(:network_address_invalid) end class NetworkDHCPAlreadyAttached < VagrantError error_key(:dhcp_already_attached, "vagrant.actions.vm.network") end class NetworkNotFound < VagrantError error_key(:not_found, "vagrant.actions.vm.host_only_network") end class NetworkTypeNotSupported < VagrantError error_key(:network_type_not_supported) end class NetworkManagerNotInstalled < VagrantError error_key(:network_manager_not_installed) end class NFSBadExports < VagrantError error_key(:nfs_bad_exports) end class NFSDupePerms < VagrantError error_key(:nfs_dupe_permissions) end class NFSExportsFailed < VagrantError error_key(:nfs_exports_failed) end class NFSCantReadExports < VagrantError error_key(:nfs_cant_read_exports) end class NFSMountFailed < VagrantError error_key(:nfs_mount_failed) end class NFSNoGuestIP < VagrantError error_key(:nfs_no_guest_ip) end class NFSNoHostIP < VagrantError error_key(:nfs_no_host_ip) end class NFSNoHostonlyNetwork < VagrantError error_key(:nfs_no_hostonly_network) end class NFSNoValidIds < VagrantError error_key(:nfs_no_valid_ids) end class NFSNotSupported < VagrantError error_key(:nfs_not_supported) end class NFSClientNotInstalledInGuest < VagrantError error_key(:nfs_client_not_installed_in_guest) end class NoDefaultProvider < VagrantError error_key(:no_default_provider) end class NoDefaultSyncedFolderImpl < VagrantError error_key(:no_default_synced_folder_impl) end class NoEnvironmentError < VagrantError error_key(:no_env) end class PackageIncludeMissing < VagrantError error_key(:include_file_missing, "vagrant.actions.general.package") end class PackageIncludeSymlink < VagrantError error_key(:package_include_symlink) end class PackageOutputDirectory < VagrantError error_key(:output_is_directory, "vagrant.actions.general.package") end class PackageOutputExists < VagrantError error_key(:output_exists, "vagrant.actions.general.package") end class PackageRequiresDirectory < VagrantError error_key(:requires_directory, "vagrant.actions.general.package") end class PowerShellNotFound < VagrantError error_key(:powershell_not_found) end class PowerShellInvalidVersion < VagrantError error_key(:powershell_invalid_version) end class PowerShellError < VagrantError error_key(:powershell_error, "vagrant_ps.errors.powershell_error") end class ProviderCantInstall < VagrantError error_key(:provider_cant_install) end class ProviderChecksumMismatch < VagrantError error_key(:provider_checksum_mismatch) end class ProviderInstallFailed < VagrantError error_key(:provider_install_failed) end class ProviderNotFound < VagrantError error_key(:provider_not_found) end class ProviderNotUsable < VagrantError error_key(:provider_not_usable) end class ProvisionerFlagInvalid < VagrantError error_key(:provisioner_flag_invalid) end class ProvisionerWinRMUnsupported < VagrantError error_key(:provisioner_winrm_unsupported) end class PluginGemNotFound < VagrantError error_key(:plugin_gem_not_found) end class PluginInstallLicenseNotFound < VagrantError error_key(:plugin_install_license_not_found) end class PluginInstallSpace < VagrantError error_key(:plugin_install_space) end class PluginInstallVersionConflict < VagrantError error_key(:plugin_install_version_conflict) end class PluginLoadError < VagrantError error_key(:plugin_load_error) end class PluginNotInstalled < VagrantError error_key(:plugin_not_installed) end class PluginStateFileParseError < VagrantError error_key(:plugin_state_file_not_parsable) end class PluginUninstallSystem < VagrantError error_key(:plugin_uninstall_system) end class PluginInitError < VagrantError error_key(:plugin_init_error) end class PluginSourceError < VagrantError error_key(:plugin_source_error) end class PushesNotDefined < VagrantError error_key(:pushes_not_defined) end class PushStrategyNotDefined < VagrantError error_key(:push_strategy_not_defined) end class PushStrategyNotLoaded < VagrantError error_key(:push_strategy_not_loaded) end class PushStrategyNotProvided < VagrantError error_key(:push_strategy_not_provided) end class RSyncError < VagrantError error_key(:rsync_error) end class RSyncNotFound < VagrantError error_key(:rsync_not_found) end class RSyncNotInstalledInGuest < VagrantError error_key(:rsync_not_installed_in_guest) end class RSyncGuestInstallError < VagrantError error_key(:rsync_guest_install_error) end class SCPPermissionDenied < VagrantError error_key(:scp_permission_denied) end class SCPUnavailable < VagrantError error_key(:scp_unavailable) end class SharedFolderCreateFailed < VagrantError error_key(:shared_folder_create_failed) end class ShellExpandFailed < VagrantError error_key(:shell_expand_failed) end class SnapshotConflictFailed < VagrantError error_key(:snapshot_force) end class SnapshotNotFound < VagrantError error_key(:snapshot_not_found) end class SnapshotNotSupported < VagrantError error_key(:snapshot_not_supported) end class SSHAuthenticationFailed < VagrantError error_key(:ssh_authentication_failed) end class SSHChannelOpenFail < VagrantError error_key(:ssh_channel_open_fail) end class SSHConnectEACCES < VagrantError error_key(:ssh_connect_eacces) end class SSHConnectionRefused < VagrantError error_key(:ssh_connection_refused) end class SSHConnectionAborted < VagrantError error_key(:ssh_connection_aborted) end class SSHConnectionReset < VagrantError error_key(:ssh_connection_reset) end class SSHConnectionTimeout < VagrantError error_key(:ssh_connection_timeout) end class SSHDisconnected < VagrantError error_key(:ssh_disconnected) end class SSHHostDown < VagrantError error_key(:ssh_host_down) end class SSHInvalidShell< VagrantError error_key(:ssh_invalid_shell) end class SSHInsertKeyUnsupported < VagrantError error_key(:ssh_insert_key_unsupported) end class SSHIsPuttyLink < VagrantError error_key(:ssh_is_putty_link) end class SSHKeyBadOwner < VagrantError error_key(:ssh_key_bad_owner) end class SSHKeyBadPermissions < VagrantError error_key(:ssh_key_bad_permissions) end class SSHKeyTypeNotSupported < VagrantError error_key(:ssh_key_type_not_supported) end class SSHNoRoute < VagrantError error_key(:ssh_no_route) end class SSHNotReady < VagrantError error_key(:ssh_not_ready) end class SSHRunRequiresKeys < VagrantError error_key(:ssh_run_requires_keys) end class SSHUnavailable < VagrantError error_key(:ssh_unavailable) end class SSHUnavailableWindows < VagrantError error_key(:ssh_unavailable_windows) end class SyncedFolderUnusable < VagrantError error_key(:synced_folder_unusable) end class UIExpectsTTY < VagrantError error_key(:ui_expects_tty) end class UnimplementedProviderAction < VagrantError error_key(:unimplemented_provider_action) end class VagrantInterrupt < VagrantError error_key(:interrupted) end class VagrantfileExistsError < VagrantError error_key(:vagrantfile_exists) end class VagrantfileLoadError < VagrantError error_key(:vagrantfile_load_error) end class VagrantfileNameError < VagrantError error_key(:vagrantfile_name_error) end class VagrantfileSyntaxError < VagrantError error_key(:vagrantfile_syntax_error) end class VagrantfileTemplateNotFoundError < VagrantError error_key(:vagrantfile_template_not_found_error) end class VagrantfileWriteError < VagrantError error_key(:vagrantfile_write_error) end class VagrantVersionBad < VagrantError error_key(:vagrant_version_bad) end class VBoxManageError < VagrantError error_key(:vboxmanage_error) end class VBoxManageLaunchError < VagrantError error_key(:vboxmanage_launch_error) end class VBoxManageNotFoundError < VagrantError error_key(:vboxmanage_not_found_error) end class VBoxManageNotFoundWSLError < VagrantError error_key(:vboxmanage_not_found_wsl_error) end class VirtualBoxBrokenVersion040214 < VagrantError error_key(:virtualbox_broken_version_040214) end class VirtualBoxGuestPropertyNotFound < VagrantError error_key(:virtualbox_guest_property_not_found) end class VirtualBoxInvalidVersion < VagrantError error_key(:virtualbox_invalid_version) end class VirtualBoxNoRoomForHighLevelNetwork < VagrantError error_key(:virtualbox_no_room_for_high_level_network) end class VirtualBoxNotDetected < VagrantError error_key(:virtualbox_not_detected) end class VirtualBoxKernelModuleNotLoaded < VagrantError error_key(:virtualbox_kernel_module_not_loaded) end class VirtualBoxInstallIncomplete < VagrantError error_key(:virtualbox_install_incomplete) end class VirtualBoxNoName < VagrantError error_key(:virtualbox_no_name) end class VirtualBoxMountFailed < VagrantError error_key(:virtualbox_mount_failed) end class VirtualBoxMountNotSupportedBSD < VagrantError error_key(:virtualbox_mount_not_supported_bsd) end class VirtualBoxNameExists < VagrantError error_key(:virtualbox_name_exists) end class VirtualBoxUserMismatch < VagrantError error_key(:virtualbox_user_mismatch) end class VirtualBoxVersionEmpty < VagrantError error_key(:virtualbox_version_empty) end class VMBaseMacNotSpecified < VagrantError error_key(:no_base_mac, "vagrant.actions.vm.match_mac") end class VMBootBadState < VagrantError error_key(:boot_bad_state) end class VMBootTimeout < VagrantError error_key(:boot_timeout) end class VMCloneFailure < VagrantError error_key(:failure, "vagrant.actions.vm.clone") end class VMCreateMasterFailure < VagrantError error_key(:failure, "vagrant.actions.vm.clone.create_master") end class VMCustomizationFailed < VagrantError error_key(:failure, "vagrant.actions.vm.customize") end class VMImportFailure < VagrantError error_key(:failure, "vagrant.actions.vm.import") end class VMInaccessible < VagrantError error_key(:vm_inaccessible) end class VMNameExists < VagrantError error_key(:vm_name_exists) end class VMNoMatchError < VagrantError error_key(:vm_no_match) end class VMNotCreatedError < VagrantError error_key(:vm_creation_required) end class VMNotFoundError < VagrantError error_key(:vm_not_found) end class VMNotRunningError < VagrantError error_key(:vm_not_running) end class VMPowerOffToPackage < VagrantError error_key(:power_off, "vagrant.actions.vm.export") end class WSLVagrantVersionMismatch < VagrantError error_key(:wsl_vagrant_version_mismatch) end class WSLVagrantAccessError < VagrantError error_key(:wsl_vagrant_access_error) end class WSLVirtualBoxWindowsAccessError < VagrantError error_key(:wsl_virtualbox_windows_access) end end end vagrant-2.0.2/lib/vagrant/guest.rb000066400000000000000000000043101323370221500170350ustar00rootroot00000000000000require "log4r" require "vagrant/capability_host" module Vagrant # This class handles guest-OS specific interactions with a machine. # It is primarily responsible for detecting the proper guest OS # implementation and then delegating capabilities. # # Vagrant has many tasks which require specific guest OS knowledge. # These are implemented using a guest/capability system. Various plugins # register as "guests" which determine the underlying OS of the system. # Then, "guest capabilities" register themselves for a specific OS (one # or more), and these capabilities are called. # # Example capabilities might be "mount_virtualbox_shared_folder" or # "configure_networks". # # This system allows for maximum flexibility and pluginability for doing # guest OS specific operations. class Guest include CapabilityHost def initialize(machine, guests, capabilities) @capabilities = capabilities @guests = guests @machine = machine end # This will detect the proper guest OS for the machine and set up # the class to actually execute capabilities. def detect! guest_name = @machine.config.vm.guest initialize_capabilities!(guest_name, @guests, @capabilities, @machine) rescue Errors::CapabilityHostExplicitNotDetected => e raise Errors::GuestExplicitNotDetected, value: e.extra_data[:value] rescue Errors::CapabilityHostNotDetected raise Errors::GuestNotDetected end # See {CapabilityHost#capability} def capability(*args) super rescue Errors::CapabilityNotFound => e raise Errors::GuestCapabilityNotFound, cap: e.extra_data[:cap], guest: name rescue Errors::CapabilityInvalid => e raise Errors::GuestCapabilityInvalid, cap: e.extra_data[:cap], guest: name end # Returns the specified or detected guest type name. # # @return [Symbol] def name capability_host_chain[0][0] end # This returns whether the guest is ready to work. If this returns # `false`, then {#detect!} should be called in order to detect the # guest OS. # # @return [Boolean] def ready? !!capability_host_chain end end end vagrant-2.0.2/lib/vagrant/host.rb000066400000000000000000000007001323370221500166620ustar00rootroot00000000000000require "vagrant/capability_host" module Vagrant # This class handles host-OS specific interations. It is responsible for # detecting the proper host OS implementation and delegating capabilities # to plugins. # # See {Guest} for more information on capabilities. class Host include CapabilityHost def initialize(host, hosts, capabilities, env) initialize_capabilities!(host, hosts, capabilities, env) end end end vagrant-2.0.2/lib/vagrant/machine.rb000066400000000000000000000472611323370221500173260ustar00rootroot00000000000000require_relative "util/ssh" require "digest/md5" require "thread" require "log4r" module Vagrant # This represents a machine that Vagrant manages. This provides a singular # API for querying the state and making state changes to the machine, which # is backed by any sort of provider (VirtualBox, VMware, etc.). class Machine # The box that is backing this machine. # # @return [Box] attr_accessor :box # Configuration for the machine. # # @return [Object] attr_accessor :config # Directory where machine-specific data can be stored. # # @return [Pathname] attr_reader :data_dir # The environment that this machine is a part of. # # @return [Environment] attr_reader :env # ID of the machine. This ID comes from the provider and is not # guaranteed to be of any particular format except that it is # a string. # # @return [String] attr_reader :id # Name of the machine. This is assigned by the Vagrantfile. # # @return [Symbol] attr_reader :name # The provider backing this machine. # # @return [Object] attr_reader :provider # The provider-specific configuration for this machine. # # @return [Object] attr_accessor :provider_config # The name of the provider. # # @return [Symbol] attr_reader :provider_name # The options given to the provider when registering the plugin. # # @return [Hash] attr_reader :provider_options # The UI for outputting in the scope of this machine. # # @return [UI] attr_reader :ui # The Vagrantfile that this machine is attached to. # # @return [Vagrantfile] attr_reader :vagrantfile # Initialize a new machine. # # @param [String] name Name of the virtual machine. # @param [Class] provider The provider backing this machine. This is # currently expected to be a V1 `provider` plugin. # @param [Object] provider_config The provider-specific configuration for # this machine. # @param [Hash] provider_options The provider-specific options from the # plugin definition. # @param [Object] config The configuration for this machine. # @param [Pathname] data_dir The directory where machine-specific data # can be stored. This directory is ensured to exist. # @param [Box] box The box that is backing this virtual machine. # @param [Environment] env The environment that this machine is a # part of. def initialize(name, provider_name, provider_cls, provider_config, provider_options, config, data_dir, box, env, vagrantfile, base=false) @logger = Log4r::Logger.new("vagrant::machine") @logger.info("Initializing machine: #{name}") @logger.info(" - Provider: #{provider_cls}") @logger.info(" - Box: #{box}") @logger.info(" - Data dir: #{data_dir}") @box = box @config = config @data_dir = data_dir @env = env @vagrantfile = vagrantfile @guest = Guest.new( self, Vagrant.plugin("2").manager.guests, Vagrant.plugin("2").manager.guest_capabilities) @name = name @provider_config = provider_config @provider_name = provider_name @provider_options = provider_options @ui = Vagrant::UI::Prefixed.new(@env.ui, @name) @ui_mutex = Mutex.new @state_mutex = Mutex.new # Read the ID, which is usually in local storage @id = nil # XXX: This is temporary. This will be removed very soon. if base @id = name # For base setups, we don't want to insert the key @config.ssh.insert_key = false else reload end # Keep track of where our UUID should be placed @index_uuid_file = nil @index_uuid_file = @data_dir.join("index_uuid") if @data_dir # Initializes the provider last so that it has access to all the # state we setup on this machine. @provider = provider_cls.new(self) @provider._initialize(@provider_name, self) # If we're using WinRM, we eager load the plugin because of # GH-3390 if @config.vm.communicator == :winrm @logger.debug("Eager loading WinRM communicator to avoid GH-3390") communicate end # If the ID is the special not created ID, then set our ID to # nil so that we destroy all our data. if state.id == MachineState::NOT_CREATED_ID self.id = nil end # Output a bunch of information about this machine in # machine-readable format in case someone is listening. @ui.machine("metadata", "provider", provider_name) end # This calls an action on the provider. The provider may or may not # actually implement the action. # # @param [Symbol] name Name of the action to run. # @param [Hash] extra_env This data will be passed into the action runner # as extra data set on the environment hash for the middleware # runner. def action(name, opts=nil) @logger.info("Calling action: #{name} on provider #{@provider}") opts ||= {} # Determine whether we lock or not lock = true lock = opts.delete(:lock) if opts.key?(:lock) # Extra env keys are the remaining opts extra_env = opts.dup check_cwd # Warns the UI if the machine was last used on a different dir # Create a deterministic ID for this machine vf = nil vf = @env.vagrantfile_name[0] if @env.vagrantfile_name id = Digest::MD5.hexdigest( "#{@env.root_path}#{vf}#{@env.local_data_path}#{@name}") # We only lock if we're not executing an SSH action. In the future # we will want to do more fine-grained unlocking in actions themselves # but for a 1.6.2 release this will work. locker = Proc.new { |*args, &block| block.call } locker = @env.method(:lock) if lock && !name.to_s.start_with?("ssh") # Lock this machine for the duration of this action locker.call("machine-action-#{id}") do # Get the callable from the provider. callable = @provider.action(name) # If this action doesn't exist on the provider, then an exception # must be raised. if callable.nil? raise Errors::UnimplementedProviderAction, action: name, provider: @provider.to_s end # Call the action ui.machine("action", name.to_s, "start") action_result = action_raw(name, callable, extra_env) ui.machine("action", name.to_s, "end") action_result end rescue Errors::EnvironmentLockedError raise Errors::MachineActionLockedError, action: name, name: @name end # This calls a raw callable in the proper context of the machine using # the middleware stack. # # @param [Symbol] name Name of the action # @param [Proc] callable # @param [Hash] extra_env Extra env for the action env. # @return [Hash] The resulting env def action_raw(name, callable, extra_env=nil) # Run the action with the action runner on the environment env = { action_name: "machine_action_#{name}".to_sym, machine: self, machine_action: name, ui: @ui, }.merge(extra_env || {}) @env.action_runner.run(callable, env) end # Returns a communication object for executing commands on the remote # machine. Note that the _exact_ semantics of this are up to the # communication provider itself. Despite this, the semantics are expected # to be consistent across operating systems. For example, all linux-based # systems should have similar communication (usually a shell). All # Windows systems should have similar communication as well. Therefore, # prior to communicating with the machine, users of this method are # expected to check the guest OS to determine their behavior. # # This method will _always_ return some valid communication object. # The `ready?` API can be used on the object to check if communication # is actually ready. # # @return [Object] def communicate if !@communicator requested = @config.vm.communicator requested ||= :ssh klass = Vagrant.plugin("2").manager.communicators[requested] raise Errors::CommunicatorNotFound, comm: requested.to_s if !klass @communicator = klass.new(self) end @communicator end # Returns a guest implementation for this machine. The guest implementation # knows how to do guest-OS specific tasks, such as configuring networks, # mounting folders, etc. # # @return [Guest] def guest raise Errors::MachineGuestNotReady if !communicate.ready? @guest.detect! if !@guest.ready? @guest end # This sets the unique ID associated with this machine. This will # persist this ID so that in the future Vagrant will be able to find # this machine again. The unique ID must be absolutely unique to the # virtual machine, and can be used by providers for finding the # actual machine associated with this instance. # # **WARNING:** Only providers should ever use this method. # # @param [String] value The ID. def id=(value) @logger.info("New machine ID: #{value.inspect}") id_file = nil if @data_dir # The file that will store the id if we have one. This allows the # ID to persist across Vagrant runs. Also, store the UUID for the # machine index. id_file = @data_dir.join("id") end if value if id_file # Write the "id" file with the id given. id_file.open("w+") do |f| f.write(value) end end if uid_file # Write the user id that created this machine uid_file.open("w+") do |f| f.write(Process.uid.to_s) end end # If we don't have a UUID, then create one if index_uuid.nil? # Create the index entry and save it entry = MachineIndex::Entry.new entry.local_data_path = @env.local_data_path entry.name = @name.to_s entry.provider = @provider_name.to_s entry.state = "preparing" entry.vagrantfile_path = @env.root_path entry.vagrantfile_name = @env.vagrantfile_name if @box entry.extra_data["box"] = { "name" => @box.name, "provider" => @box.provider.to_s, "version" => @box.version.to_s, } end entry = @env.machine_index.set(entry) @env.machine_index.release(entry) # Store our UUID so we can access it later if @index_uuid_file @index_uuid_file.open("w+") do |f| f.write(entry.id) end end end else # Delete the file, since the machine is now destroyed id_file.delete if id_file && id_file.file? uid_file.delete if uid_file && uid_file.file? # If we have a UUID associated with the index, remove it uuid = index_uuid if uuid entry = @env.machine_index.get(uuid) @env.machine_index.delete(entry) if entry end if @data_dir # Delete the entire data directory contents since all state # associated with the VM is now gone. @data_dir.children.each do |child| begin child.rmtree rescue Errno::EACCES @logger.info("EACCESS deleting file: #{child}") end end end end # Store the ID locally @id = value.nil? ? nil : value.to_s # Notify the provider that the ID changed in case it needs to do # any accounting from it. @provider.machine_id_changed end # Returns the UUID associated with this machine in the machine # index. We only have a UUID if an ID has been set. # # @return [String] UUID or nil if we don't have one yet. def index_uuid return nil if !@index_uuid_file return @index_uuid_file.read.chomp if @index_uuid_file.file? return nil end # This returns a clean inspect value so that printing the value via # a pretty print (`p`) results in a readable value. # # @return [String] def inspect "#<#{self.class}: #{@name} (#{@provider.class})>" end # This reloads the ID of the underlying machine. def reload old_id = @id @id = nil if @data_dir # 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? end if @id != old_id && @provider # It changed, notify the provider @provider.machine_id_changed end @id end # This returns the SSH info for accessing this machine. This SSH info # is queried from the underlying provider. This method returns `nil` if # the machine is not ready for SSH communication. # # The structure of the resulting hash is guaranteed to contain the # following structure, although it may return other keys as well # not documented here: # # { # host: "1.2.3.4", # port: "22", # username: "mitchellh", # private_key_path: "/path/to/my/key" # } # # Note that Vagrant makes no guarantee that this info works or is # correct. This is simply the data that the provider gives us or that # is configured via a Vagrantfile. It is still possible after this # point when attempting to connect via SSH to get authentication # errors. # # @return [Hash] SSH information. def ssh_info # First, ask the provider for their information. If the provider # returns nil, then the machine is simply not ready for SSH, and # we return nil as well. info = @provider.ssh_info return nil if info.nil? # Delete out the nil entries. info.dup.each do |key, value| info.delete(key) if value.nil? end # We set the defaults info[:host] ||= @config.ssh.default.host info[:port] ||= @config.ssh.default.port info[:private_key_path] ||= @config.ssh.default.private_key_path info[:keys_only] ||= @config.ssh.default.keys_only info[:verify_host_key] ||= @config.ssh.default.verify_host_key info[:username] ||= @config.ssh.default.username info[:compression] ||= @config.ssh.default.compression info[:dsa_authentication] ||= @config.ssh.default.dsa_authentication info[:extra_args] ||= @config.ssh.default.extra_args # We set overrides if they are set. These take precedence over # provider-returned data. info[:host] = @config.ssh.host if @config.ssh.host info[:port] = @config.ssh.port if @config.ssh.port info[:keys_only] = @config.ssh.keys_only info[:verify_host_key] = @config.ssh.verify_host_key info[:compression] = @config.ssh.compression info[:dsa_authentication] = @config.ssh.dsa_authentication info[:username] = @config.ssh.username if @config.ssh.username info[:password] = @config.ssh.password if @config.ssh.password info[:extra_args] = @config.ssh.extra_args if @config.ssh.extra_args # We also set some fields that are purely controlled by Varant info[:forward_agent] = @config.ssh.forward_agent info[:forward_x11] = @config.ssh.forward_x11 info[:forward_env] = @config.ssh.forward_env info[:ssh_command] = @config.ssh.ssh_command if @config.ssh.ssh_command # Add in provided proxy command config info[:proxy_command] = @config.ssh.proxy_command if @config.ssh.proxy_command # Set the private key path. If a specific private key is given in # the Vagrantfile we set that. Otherwise, we use the default (insecure) # private key, but only if the provider didn't give us one. if !info[:private_key_path] && !info[:password] if @config.ssh.private_key_path info[:private_key_path] = @config.ssh.private_key_path elsif info[:keys_only] info[:private_key_path] = @env.default_private_key_path end end # If we have a private key in our data dir, then use that if @data_dir && !@config.ssh.private_key_path data_private_key = @data_dir.join("private_key") if data_private_key.file? info[:private_key_path] = [data_private_key.to_s] end end # Setup the keys info[:private_key_path] ||= [] info[:private_key_path] = Array(info[:private_key_path]) # Expand the private key path relative to the root path info[:private_key_path].map! do |path| File.expand_path(path, @env.root_path) end # Check that the private key permissions are valid info[:private_key_path].each do |path| key_path = Pathname.new(path) if key_path.exist? Vagrant::Util::SSH.check_key_permissions(key_path) end end # Return the final compiled SSH info data info end # Returns the state of this machine. The state is queried from the # backing provider, so it can be any arbitrary symbol. # # @return [MachineState] def state result = @provider.state raise Errors::MachineStateInvalid if !result.is_a?(MachineState) # Update our state cache if we have a UUID and an entry in the # master index. uuid = index_uuid if uuid # active_machines provides access to query this info on each machine # from a different thread, ensure multiple machines do not access # the locked entry simultaneously as this triggers a locked machine # exception. @state_mutex.synchronize do entry = @env.machine_index.get(uuid) if entry entry.state = result.short_description @env.machine_index.set(entry) @env.machine_index.release(entry) end end end result end # Returns the user ID that created this machine. This is specific to # the host machine that this was created on. # # @return [String] def uid path = uid_file return nil if !path return nil if !path.file? return uid_file.read.chomp end # Temporarily changes the machine UI. This is useful if you want # to execute an {#action} with a different UI. def with_ui(ui) @ui_mutex.synchronize do begin old_ui = @ui @ui = ui yield ensure @ui = old_ui end end end protected # Returns the path to the file that stores the UID. def uid_file return nil if !@data_dir @data_dir.join("creator_uid") end # Checks the current directory for a given machine # and displays a warning if that machine has moved # from its previous location on disk. If the machine # has moved, it prints a warning to the user. def check_cwd desired_encoding = @env.root_path.to_s.encoding vagrant_cwd_filepath = @data_dir.join('vagrant_cwd') vagrant_cwd = if File.exist?(vagrant_cwd_filepath) File.read(vagrant_cwd_filepath, external_encoding: desired_encoding ).chomp end if vagrant_cwd != @env.root_path.to_s if vagrant_cwd ui.warn(I18n.t( 'vagrant.moved_cwd', old_wd: "#{vagrant_cwd}", current_wd: "#{@env.root_path.to_s}")) end File.write(vagrant_cwd_filepath, @env.root_path.to_s, external_encoding: desired_encoding ) end end end end vagrant-2.0.2/lib/vagrant/machine_index.rb000066400000000000000000000324731323370221500205140ustar00rootroot00000000000000require "json" require "pathname" require "securerandom" require "thread" require "vagrant/util/silence_warnings" module Vagrant # MachineIndex is able to manage the index of created Vagrant environments # in a central location. # # The MachineIndex stores a mapping of UUIDs to basic information about # a machine. The UUIDs are stored with the Vagrant environment and are # looked up in the machine index. # # The MachineIndex stores information such as the name of a machine, # the directory it was last seen at, its last known state, etc. Using # this information, we can load the entire {Machine} object for a machine, # or we can just display metadata if needed. # # The internal format of the data file is currently JSON in the following # structure: # # { # "version": 1, # "machines": { # "uuid": { # "name": "foo", # "provider": "vmware_fusion", # "data_path": "/path/to/data/dir", # "vagrantfile_path": "/path/to/Vagrantfile", # "state": "running", # "updated_at": "2014-03-02 11:11:44 +0100" # } # } # } # class MachineIndex include Enumerable # Initializes a MachineIndex at the given file location. # # @param [Pathname] data_dir Path to the directory where data for the # index can be stored. This folder should exist and must be writable. def initialize(data_dir) @data_dir = data_dir @index_file = data_dir.join("index") @lock = Monitor.new @machines = {} @machine_locks = {} with_index_lock do unlocked_reload end end # Deletes a machine by UUID. # # The machine being deleted with this UUID must either be locked # by this index or must be unlocked. # # @param [Entry] entry The entry to delete. # @return [Boolean] true if delete is successful def delete(entry) return true if !entry.id @lock.synchronize do with_index_lock do return true if !@machines[entry.id] # If we don't have the lock, then we need to acquire it. if !@machine_locks[entry.id] raise "Unlocked delete on machine: #{entry.id}" end # Reload so we have the latest data, then delete and save unlocked_reload @machines.delete(entry.id) unlocked_save # Release access on this machine unlocked_release(entry.id) end end true end # Iterate over every machine in the index. The yielded {Entry} objects # will NOT be locked, so you'll have to call {#get} manually to acquire # the lock on them. def each(reload=false) if reload @lock.synchronize do with_index_lock do unlocked_reload end end end @machines.each do |uuid, data| yield Entry.new(uuid, data.merge("id" => uuid)) end end # Accesses a machine by UUID and returns a {MachineIndex::Entry} # # The entry returned is locked and can't be read again or updated by # this process or any other. To unlock the machine, call {#release} # with the entry. # # You can only {#set} an entry (update) when the lock is held. # # @param [String] uuid UUID for the machine to access. # @return [MachineIndex::Entry] def get(uuid) entry = nil @lock.synchronize do with_index_lock do # Reload the data unlocked_reload data = find_by_prefix(uuid) return nil if !data uuid = data["id"] entry = Entry.new(uuid, data) # Lock this machine lock_file = lock_machine(uuid) if !lock_file raise Errors::MachineLocked, name: entry.name, provider: entry.provider end @machine_locks[uuid] = lock_file end end entry end # Tests if the index has the given UUID. # # @param [String] uuid # @return [Boolean] def include?(uuid) @lock.synchronize do with_index_lock do unlocked_reload return !!find_by_prefix(uuid) end end end # Releases an entry, unlocking it. # # This is an idempotent operation. It is safe to call this even if you're # unsure if an entry is locked or not. # # After calling this, the previous entry should no longer be used. # # @param [Entry] entry def release(entry) @lock.synchronize do unlocked_release(entry.id) end end # Creates/updates an entry object and returns the resulting entry. # # If the entry was new (no UUID), then the UUID will be set on the # resulting entry and can be used. Additionally, the a lock will # be created for the resulting entry, so you must {#release} it # if you want others to be able to access it. # # If the entry isn't new (has a UUID). then this process must hold # that entry's lock or else this set will fail. # # @param [Entry] entry # @return [Entry] def set(entry) # Get the struct and update the updated_at attribute struct = entry.to_json_struct # Set an ID if there isn't one already set id = entry.id @lock.synchronize do with_index_lock do # Reload so we have the latest machine data. This allows other # processes to update their own machines without conflicting # with our own. unlocked_reload # If we don't have a machine ID, try to look one up if !id self.each do |other| if entry.name == other.name && entry.provider == other.provider && entry.vagrantfile_path.to_s == other.vagrantfile_path.to_s id = other.id break end end # If we still don't have an ID, generate a random one id = SecureRandom.uuid.gsub("-", "") if !id # Get a lock on this machine lock_file = lock_machine(id) if !lock_file raise "Failed to lock new machine: #{entry.name}" end @machine_locks[id] = lock_file end if !@machine_locks[id] raise "Unlocked write on machine: #{id}" end # Set our machine and save @machines[id] = struct unlocked_save end end Entry.new(id, struct) end protected # Finds a machine where the UUID is prefixed by the given string. # # @return [Hash] def find_by_prefix(prefix) @machines.each do |uuid, data| return data.merge("id" => uuid) if uuid.start_with?(prefix) end nil end # Locks a machine exclusively to us, returning the file handle # that holds the lock. # # If the lock cannot be acquired, then nil is returned. # # This should be called within an index lock. # # @return [File] def lock_machine(uuid) lock_path = @data_dir.join("#{uuid}.lock") lock_file = lock_path.open("w+") if lock_file.flock(File::LOCK_EX | File::LOCK_NB) === false lock_file.close lock_file = nil end lock_file end # Releases a local lock on a machine. This does not acquire any locks # so make sure to lock around it. # # @param [String] id def unlocked_release(id) lock_file = @machine_locks[id] if lock_file lock_file.close begin File.delete(lock_file.path) rescue Errno::EACCES # Another process is probably opened it, no problem. end @machine_locks.delete(id) end end # This will reload the data without locking the index. It is assumed # the caller with lock the index outside of this call. # # @param [File] f def unlocked_reload return if !@index_file.file? data = nil begin data = JSON.load(@index_file.read) rescue JSON::ParserError raise Errors::CorruptMachineIndex, path: @index_file.to_s end if data if !data["version"] || data["version"].to_i != 1 raise Errors::CorruptMachineIndex, path: @index_file.to_s end @machines = data["machines"] || {} end end # Saves the index. def unlocked_save @index_file.open("w") do |f| f.write(JSON.dump({ "version" => 1, "machines" => @machines, })) end end # This will hold a lock to the index so it can be read or updated. def with_index_lock lock_path = "#{@index_file}.lock" File.open(lock_path, "w+") do |f| f.flock(File::LOCK_EX) yield end end # An entry in the MachineIndex. class Entry # The unique ID for this entry. This is _not_ the ID for the # machine itself (which is provider-specific and in the data directory). # # @return [String] attr_reader :id # The path for the "local data" directory for the environment. # # @return [Pathname] attr_accessor :local_data_path # The name of the machine. # # @return [String] attr_accessor :name # The name of the provider. # # @return [String] attr_accessor :provider # The last known state of this machine. # # @return [String] attr_accessor :state # The valid Vagrantfile filenames for this environment. # # @return [Array] attr_accessor :vagrantfile_name # The path to the Vagrantfile that manages this machine. # # @return [Pathname] attr_accessor :vagrantfile_path # The last time this entry was updated. # # @return [DateTime] attr_reader :updated_at # Extra data to store with the index entry. This can be anything # and is treated like a general global state bag. # # @return [Hash] attr_accessor :extra_data # Initializes an entry. # # The parameter given should be nil if this is being created # publicly. def initialize(id=nil, raw=nil) @extra_data = {} # Do nothing if we aren't given a raw value. Otherwise, parse it. return if !raw @id = id @local_data_path = raw["local_data_path"] @name = raw["name"] @provider = raw["provider"] @state = raw["state"] @vagrantfile_name = raw["vagrantfile_name"] @vagrantfile_path = raw["vagrantfile_path"] # TODO(mitchellh): parse into a proper datetime @updated_at = raw["updated_at"] @extra_data = raw["extra_data"] || {} # Be careful with the paths @local_data_path = nil if @local_data_path == "" @vagrantfile_path = nil if @vagrantfile_path == "" # Convert to proper types @local_data_path = Pathname.new(@local_data_path) if @local_data_path @vagrantfile_path = Pathname.new(@vagrantfile_path) if @vagrantfile_path end # Returns boolean true if this entry appears to be valid. # The critera for being valid: # # * Vagrantfile directory exists # * Vagrant environment contains a machine with this # name and provider. # # This method is _slow_. It should be used with care. # # @param [Pathname] home_path The home path for the Vagrant # environment. # @return [Boolean] def valid?(home_path) return false if !vagrantfile_path return false if !vagrantfile_path.directory? # Create an environment so we can determine the active # machines... found = false env = vagrant_env(home_path) env.active_machines.each do |name, provider| if name.to_s == self.name.to_s && provider.to_s == self.provider.to_s found = true break end end # If an active machine of the same name/provider was not # found, it is already false. return false if !found # Get the machine machine = nil begin machine = env.machine(self.name.to_sym, self.provider.to_sym) rescue Errors::MachineNotFound return false end # Refresh the machine state return false if machine.state.id == MachineState::NOT_CREATED_ID true end # Creates a {Vagrant::Environment} for this entry. # # @return [Vagrant::Environment] def vagrant_env(home_path, **opts) Vagrant::Util::SilenceWarnings.silence! do Environment.new({ cwd: @vagrantfile_path, home_path: home_path, local_data_path: @local_data_path, vagrantfile_name: @vagrantfile_name, }.merge(opts)) end end # Converts to the structure used by the JSON def to_json_struct { "local_data_path" => @local_data_path.to_s, "name" => @name, "provider" => @provider, "state" => @state, "vagrantfile_name" => @vagrantfile_name, "vagrantfile_path" => @vagrantfile_path.to_s, "updated_at" => @updated_at, "extra_data" => @extra_data, } end end end end vagrant-2.0.2/lib/vagrant/machine_state.rb000066400000000000000000000032461323370221500205210ustar00rootroot00000000000000module Vagrant # This represents the state of a given machine. This is a very basic # class that simply stores a short and long description of the state # of a machine. # # The state also stores a state "id" which can be used as a unique # identifier for a state. This should be a symbol. This allows internal # code to compare state such as ":not_created" instead of using # string comparison. # # The short description should be a single word description of the # state of the machine such as "running" or "not created". # # The long description can span multiple lines describing what the # state actually means. class MachineState # This is a special ID that can be set for the state ID that # tells Vagrant that the machine is not created. If this is the # case, then Vagrant will set the ID to nil which will automatically # clean out the machine data directory. NOT_CREATED_ID = :not_created # Unique ID for this state. # # @return [Symbol] attr_reader :id # Short description for this state. # # @return [String] attr_reader :short_description # Long description for this state. # # @return [String] attr_reader :long_description # Creates a new instance to represent the state of a machine. # # @param [Symbol] id Unique identifier for this state. # @param [String] short Short (preferably one-word) description of # the state. # @param [String] long Long description (can span multiple lines) # of the state. def initialize(id, short, long) @id = id @short_description = short @long_description = long end end end vagrant-2.0.2/lib/vagrant/plugin.rb000066400000000000000000000003521323370221500172060ustar00rootroot00000000000000module Vagrant module Plugin autoload :V1, "vagrant/plugin/v1" autoload :V2, "vagrant/plugin/v2" autoload :Manager, "vagrant/plugin/manager" autoload :StateFile, "vagrant/plugin/state_file" end end vagrant-2.0.2/lib/vagrant/plugin/000077500000000000000000000000001323370221500166615ustar00rootroot00000000000000vagrant-2.0.2/lib/vagrant/plugin/manager.rb000066400000000000000000000141731323370221500206260ustar00rootroot00000000000000require "pathname" require "set" require_relative "../bundler" require_relative "../shared_helpers" require_relative "state_file" module Vagrant module Plugin # The Manager helps with installing, listing, and initializing plugins. class Manager # Returns the path to the [StateFile] for user plugins. # # @return [Pathname] def self.user_plugins_file Vagrant.user_data_path.join("plugins.json") end # Returns the path to the [StateFile] for system plugins. def self.system_plugins_file dir = Vagrant.installer_embedded_dir return nil if !dir Pathname.new(dir).join("plugins.json") end def self.instance @instance ||= self.new(user_plugins_file) end # @param [Pathname] user_file def initialize(user_file) @user_file = StateFile.new(user_file) system_path = self.class.system_plugins_file @system_file = nil @system_file = StateFile.new(system_path) if system_path && system_path.file? end # Installs another plugin into our gem directory. # # @param [String] name Name of the plugin (gem) # @return [Gem::Specification] def install_plugin(name, **opts) local = false if name =~ /\.gem$/ # If this is a gem file, then we install that gem locally. local_spec = Vagrant::Bundler.instance.install_local(name, opts) name = local_spec.name opts[:version] = local_spec.version.to_s end plugins = installed_plugins plugins[name] = { "require" => opts[:require], "gem_version" => opts[:version], "sources" => opts[:sources], } if local_spec.nil? result = nil install_lambda = lambda do Vagrant::Bundler.instance.install(plugins, local).each do |spec| next if spec.name != name next if result && result.version >= spec.version result = spec end end if opts[:verbose] Vagrant::Bundler.instance.verbose(&install_lambda) else install_lambda.call end else result = local_spec end # Add the plugin to the state file @user_file.add_plugin( result.name, version: opts[:version], require: opts[:require], sources: opts[:sources], installed_gem_version: result.version.to_s ) # After install clean plugin gems to remove any cruft. This is useful # for removing outdated dependencies or other versions of an installed # plugin if the plugin is upgraded/downgraded Vagrant::Bundler.instance.clean(installed_plugins) result rescue Gem::GemNotFoundException raise Errors::PluginGemNotFound, name: name rescue Gem::Exception => e raise Errors::BundlerError, message: e.to_s end # Uninstalls the plugin with the given name. # # @param [String] name def uninstall_plugin(name) if @system_file if !@user_file.has_plugin?(name) && @system_file.has_plugin?(name) raise Errors::PluginUninstallSystem, name: name end end @user_file.remove_plugin(name) # Clean the environment, removing any old plugins Vagrant::Bundler.instance.clean(installed_plugins) rescue Gem::Exception => e raise Errors::BundlerError, message: e.to_s end # Updates all or a specific set of plugins. def update_plugins(specific) result = Vagrant::Bundler.instance.update(installed_plugins, specific) installed_plugins.each do |name, info| matching_spec = result.detect{|s| s.name == name} info = Hash[ info.map do |key, value| [key.to_sym, value] end ] if matching_spec @user_file.add_plugin(name, **info.merge( version: "> 0", installed_gem_version: matching_spec.version.to_s )) end end Vagrant::Bundler.instance.clean(installed_plugins) result rescue Gem::Exception => e raise Errors::BundlerError, message: e.to_s end # This returns the list of plugins that should be enabled. # # @return [Hash] def installed_plugins system = {} if @system_file @system_file.installed_plugins.each do |k, v| system[k] = v.merge("system" => true) end end plugin_list = Util::DeepMerge.deep_merge(system, @user_file.installed_plugins) # Sort plugins by name Hash[ plugin_list.map{|plugin_name, plugin_info| [plugin_name, plugin_info] }.sort_by(&:first) ] end # This returns the list of plugins that are installed as # Gem::Specifications. # # @return [Array] def installed_specs installed_plugin_info = installed_plugins installed = Set.new(installed_plugin_info.keys) installed_versions = Hash[ installed_plugin_info.map{|plugin_name, plugin_info| gem_version = plugin_info["gem_version"].to_s gem_version = "> 0" if gem_version.empty? [plugin_name, Gem::Requirement.new(gem_version)] } ] # Go through the plugins installed in this environment and # get the latest version of each. installed_map = {} Gem::Specification.find_all.each do |spec| # Ignore specs that aren't in our installed list next if !installed.include?(spec.name) next if installed_versions[spec.name] && !installed_versions[spec.name].satisfied_by?(spec.version) # If we already have a newer version in our list of installed, # then ignore it next if installed_map.key?(spec.name) && installed_map[spec.name].version >= spec.version installed_map[spec.name] = spec end installed_map.values end end end end vagrant-2.0.2/lib/vagrant/plugin/state_file.rb000066400000000000000000000064751323370221500213410ustar00rootroot00000000000000require "json" require "fileutils" require "tempfile" module Vagrant module Plugin # This is a helper to deal with the plugin state file that Vagrant # uses to track what plugins are installed and activated and such. class StateFile def initialize(path) @path = path @data = {} if @path.exist? begin @data = JSON.parse(@path.read) rescue JSON::ParserError => e raise Vagrant::Errors::PluginStateFileParseError, path: path, message: e.message end upgrade_v0! if !@data["version"] end @data["version"] ||= "1" @data["installed"] ||= {} end # Add a plugin that is installed to the state file. # # @param [String] name The name of the plugin def add_plugin(name, **opts) @data["installed"][name] = { "ruby_version" => RUBY_VERSION, "vagrant_version" => Vagrant::VERSION, "gem_version" => opts[:version] || "", "require" => opts[:require] || "", "sources" => opts[:sources] || [], "installed_gem_version" => opts[:installed_gem_version] } save! end # Adds a RubyGems index source to look up gems. # # @param [String] url URL of the source. def add_source(url) @data["sources"] ||= [] @data["sources"] << url if !@data["sources"].include?(url) save! end # This returns a hash of installed plugins according to the state # file. Note that this may _not_ directly match over to actually # installed gems. # # @return [Hash] def installed_plugins @data["installed"] end # Returns true/false if the plugin is present in this state file. # # @return [Boolean] def has_plugin?(name) @data["installed"].key?(name) end # Remove a plugin that is installed from the state file. # # @param [String] name The name of the plugin. def remove_plugin(name) @data["installed"].delete(name) save! end # Remove a source for RubyGems. # # @param [String] url URL of the source def remove_source(url) @data["sources"] ||= [] @data["sources"].delete(url) save! end # Returns the list of RubyGems sources that will be searched for # plugins. # # @return [Array] def sources @data["sources"] || [] end # This saves the state back into the state file. def save! Tempfile.open(@path.basename.to_s, @path.dirname.to_s) do |f| f.binmode f.write(JSON.dump(@data)) f.fsync f.chmod(0644) f.close FileUtils.mv(f.path, @path) end end protected # This upgrades the internal data representation from V0 (the initial # version) to V1. def upgrade_v0! @data["version"] = "1" new_installed = {} (@data["installed"] || []).each do |plugin| new_installed[plugin] = { "ruby_version" => "0", "vagrant_version" => "0", } end @data["installed"] = new_installed save! end end end end vagrant-2.0.2/lib/vagrant/plugin/v1.rb000066400000000000000000000011321323370221500175310ustar00rootroot00000000000000require "log4r" require "vagrant/plugin/v1/errors" module Vagrant module Plugin module V1 autoload :Command, "vagrant/plugin/v1/command" autoload :Communicator, "vagrant/plugin/v1/communicator" autoload :Config, "vagrant/plugin/v1/config" autoload :Guest, "vagrant/plugin/v1/guest" autoload :Host, "vagrant/plugin/v1/host" autoload :Manager, "vagrant/plugin/v1/manager" autoload :Plugin, "vagrant/plugin/v1/plugin" autoload :Provider, "vagrant/plugin/v1/provider" autoload :Provisioner, "vagrant/plugin/v1/provisioner" end end end vagrant-2.0.2/lib/vagrant/plugin/v1/000077500000000000000000000000001323370221500172075ustar00rootroot00000000000000vagrant-2.0.2/lib/vagrant/plugin/v1/command.rb000066400000000000000000000132471323370221500211610ustar00rootroot00000000000000require 'log4r' require "vagrant/util/safe_puts" module Vagrant module Plugin module V1 # This is the base class for a CLI command. class Command include Util::SafePuts def initialize(argv, env) @argv = argv @env = env @logger = Log4r::Logger.new("vagrant::command::#{self.class.to_s.downcase}") end # This is what is called on the class to actually execute it. Any # subclasses should implement this method and do any option parsing # and validation here. def execute end protected # Parses the options given an OptionParser instance. # # This is a convenience method that properly handles duping the # originally argv array so that it is not destroyed. # # This method will also automatically detect "-h" and "--help" # and print help. And if any invalid options are detected, the help # will be printed, as well. # # If this method returns `nil`, then you should assume that help # was printed and parsing failed. def parse_options(opts=nil) # Creating a shallow copy of the arguments so the OptionParser # doesn't destroy the originals. argv = @argv.dup # Default opts to a blank optionparser if none is given opts ||= OptionParser.new # Add the help option, which must be on every command. opts.on_tail("-h", "--help", "Print this help") do safe_puts(opts.help) return nil end opts.parse!(argv) return argv rescue OptionParser::InvalidOption raise Errors::CLIInvalidOptions, help: opts.help.chomp end # Yields a VM for each target VM for the command. # # This is a convenience method for easily implementing methods that # take a target VM (in the case of multi-VM) or every VM if no # specific VM name is specified. # # @param [String] name The name of the VM. Nil if every VM. # @param [Boolean] single_target If true, then an exception will be # raised if more than one target is found. def with_target_vms(names=nil, options=nil) # Using VMs requires a Vagrant environment to be properly setup raise Errors::NoEnvironmentError if !@env.root_path # Setup the options hash options ||= {} # Require that names be an array names ||= [] names = [names] if !names.is_a?(Array) # First determine the proper array of VMs. vms = [] if names.length > 0 names.each do |name| if pattern = name[/^\/(.+?)\/$/, 1] # This is a regular expression name, so we convert to a regular # expression and allow that sort of matching. regex = Regexp.new(pattern) @env.vms.each do |name, vm| vms << vm if name =~ regex end raise Errors::VMNoMatchError if vms.empty? else # String name, just look for a specific VM vms << @env.vms[name.to_sym] raise Errors::VMNotFoundError, name: name if !vms[0] end end else vms = @env.vms_ordered end # Make sure we're only working with one VM if single target if options[:single_target] && vms.length != 1 vm = @env.primary_vm raise Errors::MultiVMTargetRequired if !vm vms = [vm] end # If we asked for reversed ordering, then reverse it vms.reverse! if options[:reverse] # Go through each VM and yield it! vms.each do |old_vm| # We get a new VM from the environment here to avoid potentially # stale VMs (if there was a config reload on the environment # or something). vm = @env.vms[old_vm.name] yield vm end end # This method will split the argv given into three parts: the # flags to this command, the subcommand, and the flags to the # subcommand. For example: # # -v status -h -v # # The above would yield 3 parts: # # ["-v"] # "status" # ["-h", "-v"] # # These parts are useful because the first is a list of arguments # given to the current command, the second is a subcommand, and the # third are the commands given to the subcommand. # # @return [Array] The three parts. def split_main_and_subcommand(argv) # Initialize return variables main_args = nil sub_command = nil sub_args = [] # We split the arguments into two: One set containing any # flags before a word, and then the rest. The rest are what # get actually sent on to the subcommand. argv.each_index do |i| if !argv[i].start_with?("-") # We found the beginning of the sub command. Split the # args up. main_args = argv[0, i] sub_command = argv[i] sub_args = argv[i + 1, argv.length - i + 1] # Break so we don't find the next non flag and shift our # main args. break end end # Handle the case that argv was empty or didn't contain any subcommand main_args = argv.dup if main_args.nil? return [main_args, sub_command, sub_args] end end end end end vagrant-2.0.2/lib/vagrant/plugin/v1/communicator.rb000066400000000000000000000074431323370221500222440ustar00rootroot00000000000000module Vagrant module Plugin module V1 # Base class for a communicator in Vagrant. A communicator is # responsible for communicating with a machine in some way. There # are various stages of Vagrant that require things such as uploading # files to the machine, executing shell commands, etc. Implementors # of this class are expected to provide this functionality in some # way. # # Note that a communicator must provide **all** of the methods # in this base class. There is currently no way for one communicator # to provide say a more efficient way of uploading a file, but not # provide shell execution. This sort of thing will come in a future # version. class Communicator # This returns true/false depending on if the given machine # can be communicated with using this communicator. If this returns # `true`, then this class will be used as the primary communication # method for the machine. # # @return [Boolean] def self.match?(machine) false end # Initializes the communicator with the machine that we will be # communicating with. This base method does nothing (it doesn't # even store the machine in an instance variable for you), so you're # expected to override this and do something with the machine if # you care about it. # # @param [Machine] machine The machine this instance is expected to # communicate with. def initialize(machine) end # Checks if the target machine is ready for communication. If this # returns true, then all the other methods for communicating with # the machine are expected to be functional. # # @return [Boolean] def ready? false end # Download a file from the remote machine to the local machine. # # @param [String] from Path of the file on the remote machine. # @param [String] to Path of where to save the file locally. def download(from, to) end # Upload a file to the remote machine. # # @param [String] from Path of the file locally to upload. # @param [String] to Path of where to save the file on the remote # machine. def upload(from, to) end # Execute a command on the remote machine. The exact semantics # of this method are up to the implementor, but in general the # users of this class will expect this to be a shell. # # This method gives you no way to write data back to the remote # machine, so only execute commands that don't expect input. # # @param [String] command Command to execute. # @yield [type, data] Realtime output of the command being executed. # @yieldparam [String] type Type of the output. This can be # `:stdout`, `:stderr`, etc. The exact types are up to the # implementor. # @yieldparam [String] data Data for the given output. # @return [Integer] Exit code of the command. def execute(command, opts=nil) end # Executes a command on the remote machine with administrative # privileges. See {#execute} for documentation, as the API is the # same. # # @see #execute def sudo(command, opts=nil) end # Executes a command and returns true if the command succeeded, # and false otherwise. By default, this executes as a normal user, # and it is up to the communicator implementation if they expose an # option for running tests as an administrator. # # @see #execute def test(command, opts=nil) end end end end end vagrant-2.0.2/lib/vagrant/plugin/v1/config.rb000066400000000000000000000106011323370221500207770ustar00rootroot00000000000000module Vagrant module Plugin module V1 # This is the base class for a configuration key defined for # V1. Any configuration key plugins for V1 should inherit from this # class. class Config # This constant represents an unset value. This is useful so it is # possible to know the difference between a configuration value that # was never set, and a value that is nil (explicitly). Best practice # is to initialize all variables to this value, then the {#merge} # method below will "just work" in many cases. UNSET_VALUE = Object.new # This is called as a last-minute hook that allows the configuration # object to finalize itself before it will be put into use. This is # a useful place to do some defaults in the case the user didn't # configure something or so on. # # An example of where this sort of thing is used or has been used: # the "vm" configuration key uses this to make sure that at least # one sub-VM has been defined: the default VM. # # The configuration object is expected to mutate itself. def finalize! # Default implementation is to do nothing. end # Merge another configuration object into this one. This assumes that # the other object is the same class as this one. This should not # mutate this object, but instead should return a new, merged object. # # The default implementation will simply iterate over the instance # variables and merge them together, with this object overriding # any conflicting instance variables of the older object. Instance # variables starting with "__" (double underscores) will be ignored. # This lets you set some sort of instance-specific state on your # configuration keys without them being merged together later. # # @param [Object] other The other configuration object to merge from, # this must be the same type of object as this one. # @return [Object] The merged object. def merge(other) result = self.class.new # Set all of our instance variables on the new class [self, other].each do |obj| obj.instance_variables.each do |key| # Ignore keys that start with a double underscore. This allows # configuration classes to still hold around internal state # that isn't propagated. if !key.to_s.start_with?("@__") result.instance_variable_set(key, obj.instance_variable_get(key)) end end end result end # Allows setting options from a hash. By default this simply calls # the `#{key}=` method on the config class with the value, which is # the expected behavior most of the time. # # This is expected to mutate itself. # # @param [Hash] options A hash of options to set on this configuration # key. def set_options(options) options.each do |key, value| send("#{key}=", value) end end # Converts this configuration object to JSON. def to_json(*a) instance_variables_hash.to_json(*a) end # Returns the instance variables as a hash of key-value pairs. def instance_variables_hash instance_variables.inject({}) do |acc, iv| acc[iv.to_s[1..-1]] = instance_variable_get(iv) acc end end # This is called to upgrade this V1 config to V2. The parameter given # is the full V2 configuration object, so you can do anything to it # that you want. # # No return value is expected, modifications should be made directly # to the new V2 object. # # @param [V2::Root] new def upgrade(new) end # Called after the configuration is finalized and loaded to validate # this object. # # @param [Environment] env Vagrant::Environment object of the # environment that this configuration has been loaded into. This # gives you convenient access to things like the the root path # and so on. # @param [ErrorRecorder] errors def validate(env, errors) end end end end end vagrant-2.0.2/lib/vagrant/plugin/v1/errors.rb000066400000000000000000000006211323370221500210470ustar00rootroot00000000000000# This file contains all the errors that the V1 plugin interface # may throw. module Vagrant module Plugin module V1 # Exceptions that can be thrown within the plugin interface all # inherit from this parent exception. class Error < StandardError; end # This is thrown when a command name given is invalid. class InvalidCommandName < Error; end end end end vagrant-2.0.2/lib/vagrant/plugin/v1/guest.rb000066400000000000000000000067221323370221500206720ustar00rootroot00000000000000module Vagrant module Plugin module V1 # The base class for a guest. A guest represents an installed system # within a machine that Vagrant manages. There are some portions of # Vagrant which are OS-specific such as mountaing shared folders and # halting the machine, and this abstraction allows the implementation # for these to be separate from the core of Vagrant. class Guest class BaseError < Errors::VagrantError error_namespace("vagrant.guest.base") end include Vagrant::Util # The VM which this system is tied to. attr_reader :vm # Initializes the system. Any subclasses MUST make sure this # method is called on the parent. Therefore, if a subclass overrides # `initialize`, then you must call `super`. def initialize(vm) @vm = vm end # This method is automatically called when the system is available (when # Vagrant can successfully SSH into the machine) to give the system a chance # to determine the distro and return a distro-specific system. # # If this method returns nil, then this instance is assumed to be # the most specific guest implementation. def distro_dispatch end # Halt the machine. This method should gracefully shut down the # operating system. This method will cause `vagrant halt` and associated # commands to _block_, meaning that if the machine doesn't halt # in a reasonable amount of time, this method should just return. # # If when this method returns, the machine's state isn't "powered_off," # Vagrant will proceed to forcefully shut the machine down. def halt raise BaseError, _key: :unsupported_halt end # Mounts a shared folder. # # This method should create, mount, and properly set permissions # on the shared folder. This method should also properly # adhere to any configuration values such as `shared_folder_uid` # on `config.vm`. # # @param [String] name The name of the shared folder. # @param [String] guestpath The path on the machine which the user # wants the folder mounted. # @param [Hash] options Additional options for the shared folder # which can be honored. def mount_shared_folder(name, guestpath, options) raise BaseError, _key: :unsupported_shared_folder end # Mounts a shared folder via NFS. This assumes that the exports # via the host are already done. def mount_nfs(ip, folders) raise BaseError, _key: :unsupported_nfs end # Configures the given list of networks on the virtual machine. # # The networks parameter will be an array of hashes where the hashes # represent the configuration of a network interface. The structure # of the hash will be roughly the following: # # { # type: :static, # ip: "192.168.33.10", # netmask: "255.255.255.0", # interface: 1 # } # def configure_networks(networks) raise BaseError, _key: :unsupported_configure_networks end # Called to change the hostname of the virtual machine. def change_host_name(name) raise BaseError, _key: :unsupported_host_name end end end end end vagrant-2.0.2/lib/vagrant/plugin/v1/host.rb000066400000000000000000000041121323370221500205070ustar00rootroot00000000000000module Vagrant module Plugin module V1 # Base class for a host in Vagrant. A host class contains functionality # that is specific to a specific OS that is running Vagrant. This # abstraction is done becauase there is some host-specific logic that # Vagrant must do in some cases. class Host # This returns true/false depending on if the current running system # matches the host class. # # @return [Boolean] def self.match? nil end # The precedence of the host when checking for matches. This is to # allow certain host such as generic OS's ("Linux", "BSD", etc.) # to be specified last. # # The hosts with the higher numbers will be checked first. # # If you're implementing a basic host, you can probably ignore this. def self.precedence 5 end # Initializes a new host class. # # The only required parameter is a UI object so that the host # objects have some way to communicate with the outside world. # # @param [UI] ui UI for the hosts to output to. def initialize(ui) @ui = ui end # Returns true of false denoting whether or not this host supports # NFS shared folder setup. This method ideally should verify that # NFS is installed. # # @return [Boolean] def nfs? false end # Exports the given hash of folders via NFS. # # @param [String] id A unique ID that is guaranteed to be unique to # match these sets of folders. # @param [String] ip IP of the guest machine. # @param [Hash] folders Shared folders to sync. def nfs_export(id, ip, folders) end # Prunes any NFS exports made by Vagrant which aren't in the set # of valid ids given. # # @param [Array] valid_ids Valid IDs that should not be # pruned. def nfs_prune(valid_ids) end end end end end vagrant-2.0.2/lib/vagrant/plugin/v1/manager.rb000066400000000000000000000063601323370221500211530ustar00rootroot00000000000000require "log4r" module Vagrant module Plugin module V1 # This class maintains a list of all the registered plugins as well # as provides methods that allow querying all registered components of # those plugins as a single unit. class Manager attr_reader :registered def initialize @logger = Log4r::Logger.new("vagrant::plugin::v1::manager") @registered = [] end # This returns all the registered communicators. # # @return [Hash] def communicators result = {} @registered.each do |plugin| result.merge!(plugin.communicator.to_hash) end result end # This returns all the registered configuration classes. # # @return [Hash] def config result = {} @registered.each do |plugin| plugin.config.each do |key, klass| result[key] = klass end end result end # This returns all the registered configuration classes that were # marked as "upgrade safe." # # @return [Hash] def config_upgrade_safe result = {} @registered.each do |plugin| configs = plugin.data[:config_upgrade_safe] if configs configs.each do |key| result[key] = plugin.config.get(key) end end end result end # This returns all the registered guests. # # @return [Hash] def guests result = {} @registered.each do |plugin| result.merge!(plugin.guest.to_hash) end result end # This returns all registered host classes. # # @return [Hash] def hosts hosts = {} @registered.each do |plugin| hosts.merge!(plugin.host.to_hash) end hosts end # This returns all registered providers. # # @return [Hash] def providers providers = {} @registered.each do |plugin| providers.merge!(plugin.provider.to_hash) end providers end # This registers a plugin. This should _NEVER_ be called by the public # and should only be called from within Vagrant. Vagrant will # automatically register V1 plugins when a name is set on the # plugin. def register(plugin) if !@registered.include?(plugin) @logger.info("Registered plugin: #{plugin.name}") @registered << plugin end end # This clears out all the registered plugins. This is only used by # unit tests and should not be called directly. def reset! @registered.clear end # This unregisters a plugin so that its components will no longer # be used. Note that this should only be used for testing purposes. def unregister(plugin) if @registered.include?(plugin) @logger.info("Unregistered: #{plugin.name}") @registered.delete(plugin) end end end end end end vagrant-2.0.2/lib/vagrant/plugin/v1/plugin.rb000066400000000000000000000202021323370221500210260ustar00rootroot00000000000000require "set" require "log4r" module Vagrant module Plugin module V1 # This is the superclass for all V1 plugins. class Plugin # Special marker that can be used for action hooks that matches # all action sequences. ALL_ACTIONS = :__all_actions__ # The logger for this class. LOGGER = Log4r::Logger.new("vagrant::plugin::v1::plugin") # Set the root class up to be ourself, so that we can reference this # from within methods which are probably in subclasses. ROOT_CLASS = self # This returns the manager for all V1 plugins. # # @return [V1::Manager] def self.manager @manager ||= Manager.new end # Set the name of the plugin. The moment that this is called, the # plugin will be registered and available. Before this is called, a # plugin does not exist. The name must be unique among all installed # plugins. # # @param [String] name Name of the plugin. # @return [String] The name of the plugin. def self.name(name=UNSET_VALUE) # Get or set the value first, so we have a name for logging when # we register. result = get_or_set(:name, name) # The plugin should be registered if we're setting a real name on it Plugin.manager.register(self) if name != UNSET_VALUE # Return the result result end # Sets a human-friendly descrition of the plugin. # # @param [String] value Description of the plugin. # @return [String] Description of the plugin. def self.description(value=UNSET_VALUE) get_or_set(:description, value) end # Registers a callback to be called when a specific action sequence # is run. This allows plugin authors to hook into things like VM # bootup, VM provisioning, etc. # # @param [Symbol] name Name of the action. # @return [Array] List of the hooks for the given action. def self.action_hook(name, &block) # Get the list of hooks for the given hook name data[:action_hooks] ||= {} hooks = data[:action_hooks][name.to_sym] ||= [] # Return the list if we don't have a block return hooks if !block_given? # Otherwise add the block to the list of hooks for this action. hooks << block end # Defines additional command line commands available by key. The key # becomes the subcommand, so if you register a command "foo" then # "vagrant foo" becomes available. # # @param [String] name Subcommand key. def self.command(name=UNSET_VALUE, &block) data[:command] ||= Registry.new if name != UNSET_VALUE # Validate the name of the command if name.to_s !~ /^[-a-z0-9]+$/i raise InvalidCommandName, "Commands can only contain letters, numbers, and hyphens" end # Register a new command class only if a name was given. data[:command].register(name.to_sym, &block) end # Return the registry data[:command] end # Defines additional communicators to be available. Communicators # should be returned by a block passed to this method. This is done # to ensure that the class is lazy loaded, so if your class inherits # from or uses any Vagrant internals specific to Vagrant 1.0, then # the plugin can still be defined without breaking anything in future # versions of Vagrant. # # @param [String] name Communicator name. def self.communicator(name=UNSET_VALUE, &block) data[:communicator] ||= Registry.new # Register a new communicator class only if a name was given. data[:communicator].register(name.to_sym, &block) if name != UNSET_VALUE # Return the registry data[:communicator] end # Defines additional configuration keys to be available in the # Vagrantfile. The configuration class should be returned by a # block passed to this method. This is done to ensure that the class # is lazy loaded, so if your class inherits from any classes that # are specific to Vagrant 1.0, then the plugin can still be defined # without breaking anything in future versions of Vagrant. # # @param [String] name Configuration key. # @param [Boolean] upgrade_safe If this is true, then this configuration # key is safe to load during an upgrade, meaning that it depends # on NO Vagrant internal classes. Do _not_ set this to true unless # you really know what you're doing, since you can cause Vagrant # to crash (although Vagrant will output a user-friendly error # message if this were to happen). def self.config(name=UNSET_VALUE, upgrade_safe=false, &block) data[:config] ||= Registry.new # Register a new config class only if a name was given. if name != UNSET_VALUE data[:config].register(name.to_sym, &block) # If we were told this is an upgrade safe configuration class # then we add it to the set. if upgrade_safe data[:config_upgrade_safe] ||= Set.new data[:config_upgrade_safe].add(name.to_sym) end end # Return the registry data[:config] end # Defines an additionally available guest implementation with # the given key. # # @param [String] name Name of the guest. def self.guest(name=UNSET_VALUE, &block) data[:guests] ||= Registry.new # Register a new guest class only if a name was given data[:guests].register(name.to_sym, &block) if name != UNSET_VALUE # Return the registry data[:guests] end # Defines an additionally available host implementation with # the given key. # # @param [String] name Name of the host. def self.host(name=UNSET_VALUE, &block) data[:hosts] ||= Registry.new # Register a new host class only if a name was given data[:hosts].register(name.to_sym, &block) if name != UNSET_VALUE # Return the registry data[:hosts] end # Registers additional providers to be available. # # @param [Symbol] name Name of the provider. def self.provider(name=UNSET_VALUE, &block) data[:providers] ||= Registry.new # Register a new provider class only if a name was given data[:providers].register(name.to_sym, &block) if name != UNSET_VALUE # Return the registry data[:providers] end # Registers additional provisioners to be available. # # @param [String] name Name of the provisioner. def self.provisioner(name=UNSET_VALUE, &block) data[:provisioners] ||= Registry.new # Register a new provisioner class only if a name was given data[:provisioners].register(name.to_sym, &block) if name != UNSET_VALUE # Return the registry data[:provisioners] end # Returns the internal data associated with this plugin. This # should NOT be called by the general public. # # @return [Hash] def self.data @data ||= {} end protected # Sentinel value denoting that a value has not been set. UNSET_VALUE = Object.new # Helper method that will set a value if a value is given, or otherwise # return the already set value. # # @param [Symbol] key Key for the data # @param [Object] value Value to store. # @return [Object] Stored value. def self.get_or_set(key, value=UNSET_VALUE) # If no value is to be set, then return the value we have already set return data[key] if value.eql?(UNSET_VALUE) # Otherwise set the value data[key] = value end end end end end vagrant-2.0.2/lib/vagrant/plugin/v1/provider.rb000066400000000000000000000045451323370221500213760ustar00rootroot00000000000000module Vagrant module Plugin module V1 # This is the base class for a provider for the V1 API. A provider # is responsible for creating compute resources to match the needs # of a Vagrant-configured system. class Provider # Initialize the provider to represent the given machine. # # @param [Vagrant::Machine] machine The machine that this provider # is responsible for. def initialize(machine) end # This should return an action callable for the given name. # # @param [Symbol] name Name of the action. # @return [Object] A callable action sequence object, whether it # is a proc, object, etc. def action(name) nil end # This method is called if the underying machine ID changes. Providers # can use this method to load in new data for the actual backing # machine or to realize that the machine is now gone (the ID can # become `nil`). No parameters are given, since the underlying machine # is simply the machine instance given to this object. And no # return value is necessary. def machine_id_changed end # This should return a hash of information that explains how to # SSH into the machine. If the machine is not at a point where # SSH is even possible, then `nil` should be returned. # # The general structure of this returned hash should be the # following: # # { # host: "1.2.3.4", # port: "22", # username: "mitchellh", # private_key_path: "/path/to/my/key" # } # # **Note:** Vagrant only supports private key based authentication, # mainly for the reason that there is no easy way to exec into an # `ssh` prompt with a password, whereas we can pass a private key # via commandline. # # @return [Hash] SSH information. For the structure of this hash # read the accompanying documentation for this method. def ssh_info nil end # This should return the state of the machine within this provider. # The state can be any symbol. # # @return [Symbol] def state nil end end end end end vagrant-2.0.2/lib/vagrant/plugin/v1/provisioner.rb000066400000000000000000000032641323370221500221200ustar00rootroot00000000000000module Vagrant module Plugin module V1 # This is the base class for a provisioner for the V1 API. A provisioner # is primarily responsible for installing software on a Vagrant guest. class Provisioner # The environment which provisioner is running in. This is the # action environment, not a Vagrant::Environment. attr_reader :env # The configuration for this provisioner. This will be an instance of # the `Config` class which is part of the provisioner. attr_reader :config def initialize(env, config) @env = env @config = config end # This method is expected to return a class that is used for # configuring the provisioner. This return value is expected to be # a subclass of {Config}. # # @return [Config] def self.config_class end # This is the method called to "prepare" the provisioner. This is called # before any actions are run by the action runner (see {Vagrant::Actions::Runner}). # This can be used to setup shared folders, forward ports, etc. Whatever is # necessary on a "meta" level. # # No return value is expected. def prepare end # This is the method called to provision the system. This method # is expected to do whatever necessary to provision the system (create files, # SSH, etc.) def provision! end # This is the method called to when the system is being destroyed # and allows the provisioners to engage in any cleanup tasks necessary. def cleanup end end end end end vagrant-2.0.2/lib/vagrant/plugin/v2.rb000066400000000000000000000015771323370221500175470ustar00rootroot00000000000000require "log4r" # We don't autoload components because if we're loading anything in the # V2 namespace anyways, then we're going to need the Components class. require "vagrant/plugin/v2/components" require "vagrant/plugin/v2/errors" module Vagrant module Plugin module V2 autoload :Command, "vagrant/plugin/v2/command" autoload :Communicator, "vagrant/plugin/v2/communicator" autoload :Config, "vagrant/plugin/v2/config" autoload :Guest, "vagrant/plugin/v2/guest" autoload :Host, "vagrant/plugin/v2/host" autoload :Manager, "vagrant/plugin/v2/manager" autoload :Plugin, "vagrant/plugin/v2/plugin" autoload :Provider, "vagrant/plugin/v2/provider" autoload :Push, "vagrant/plugin/v2/push" autoload :Provisioner, "vagrant/plugin/v2/provisioner" autoload :SyncedFolder, "vagrant/plugin/v2/synced_folder" end end end vagrant-2.0.2/lib/vagrant/plugin/v2/000077500000000000000000000000001323370221500172105ustar00rootroot00000000000000vagrant-2.0.2/lib/vagrant/plugin/v2/command.rb000066400000000000000000000267111323370221500211620ustar00rootroot00000000000000require 'log4r' require "vagrant/util/safe_puts" module Vagrant module Plugin module V2 # This is the base class for a CLI command. class Command include Util::SafePuts # This should return a brief (60 characters or less) synopsis of what # this command does. It will be used in the output of the help. # # @return [String] def self.synopsis "" end def initialize(argv, env) @argv = argv @env = env @logger = Log4r::Logger.new("vagrant::command::#{self.class.to_s.downcase}") end # This is what is called on the class to actually execute it. Any # subclasses should implement this method and do any option parsing # and validation here. def execute end protected # Parses the options given an OptionParser instance. # # This is a convenience method that properly handles duping the # originally argv array so that it is not destroyed. # # This method will also automatically detect "-h" and "--help" # and print help. And if any invalid options are detected, the help # will be printed, as well. # # If this method returns `nil`, then you should assume that help # was printed and parsing failed. def parse_options(opts=nil) # make sure optparse doesn't use POSIXLY_CORRECT parsing ENV["POSIXLY_CORRECT"] = nil # Creating a shallow copy of the arguments so the OptionParser # doesn't destroy the originals. argv = @argv.dup # Default opts to a blank optionparser if none is given opts ||= OptionParser.new # Add the help option, which must be on every command. opts.on_tail("-h", "--help", "Print this help") do safe_puts(opts.help) return nil end opts.parse!(argv) return argv rescue OptionParser::InvalidOption, OptionParser::MissingArgument raise Errors::CLIInvalidOptions, help: opts.help.chomp end # Yields a VM for each target VM for the command. # # This is a convenience method for easily implementing methods that # take a target VM (in the case of multi-VM) or every VM if no # specific VM name is specified. # # @param [String] name The name of the VM. Nil if every VM. # @param [Hash] options Additional tweakable settings. # @option options [Symbol] :provider The provider to back the # machines with. All machines will be backed with this # provider. If none is given, a sensible default is chosen. # @option options [Boolean] :reverse If true, the resulting order # of machines is reversed. # @option options [Boolean] :single_target If true, then an # exception will be raised if more than one target is found. def with_target_vms(names=nil, options=nil) @logger.debug("Getting target VMs for command. Arguments:") @logger.debug(" -- names: #{names.inspect}") @logger.debug(" -- options: #{options.inspect}") # Setup the options hash options ||= {} # Require that names be an array names ||= [] names = [names] if !names.is_a?(Array) # Determine if we require a local Vagrant environment. There are # two cases that we require a local environment: # # * We're asking for ANY/EVERY VM (no names given). # # * We're asking for specific VMs, at least once of which # is NOT in the local machine index. # requires_local_env = false requires_local_env = true if names.empty? requires_local_env ||= names.any? { |n| !@env.machine_index.include?(n) } raise Errors::NoEnvironmentError if requires_local_env && !@env.root_path # Cache the active machines outside the loop active_machines = @env.active_machines # This is a helper that gets a single machine with the proper # provider. The "proper provider" in this case depends on what was # given: # # * If a provider was explicitly specified, then use that provider. # But if an active machine exists with a DIFFERENT provider, # then throw an error (for now), since we don't yet support # bringing up machines with different providers. # # * If no provider was specified, then use the active machine's # provider if it exists, otherwise use the default provider. # get_machine = lambda do |name| # Check for an active machine with the same name provider_to_use = options[:provider] provider_to_use = provider_to_use.to_sym if provider_to_use # If we have this machine in our index, load that. entry = @env.machine_index.get(name.to_s) if entry @env.machine_index.release(entry) # Create an environment for this location and yield the # machine in that environment. We silence warnings here because # Vagrantfiles often have constants, so people would otherwise # constantly (heh) get "already initialized constant" warnings. begin env = entry.vagrant_env( @env.home_path, ui_class: @env.ui_class) rescue Vagrant::Errors::EnvironmentNonExistentCWD # This means that this environment working directory # no longer exists, so delete this entry. entry = @env.machine_index.get(name.to_s) @env.machine_index.delete(entry) if entry raise end next env.machine(entry.name.to_sym, entry.provider.to_sym) end active_machines.each do |active_name, active_provider| if name == active_name # We found an active machine with the same name if provider_to_use && provider_to_use != active_provider # We found an active machine with a provider that doesn't # match the requested provider. Show an error. raise Errors::ActiveMachineWithDifferentProvider, name: active_name.to_s, active_provider: active_provider.to_s, requested_provider: provider_to_use.to_s else # Use this provider and exit out of the loop. One of the # invariants [for now] is that there shouldn't be machines # with multiple providers. @logger.info("Active machine found with name #{active_name}. " + "Using provider: #{active_provider}") provider_to_use = active_provider break end end end # Use the default provider if nothing else provider_to_use ||= @env.default_provider(machine: name) # Get the right machine with the right provider @env.machine(name, provider_to_use) end # First determine the proper array of VMs. machines = [] if names.length > 0 names.each do |name| if pattern = name[/^\/(.+?)\/$/, 1] @logger.debug("Finding machines that match regex: #{pattern}") # This is a regular expression name, so we convert to a regular # expression and allow that sort of matching. regex = Regexp.new(pattern) @env.machine_names.each do |machine_name| if machine_name =~ regex machines << get_machine.call(machine_name) end end raise Errors::VMNoMatchError if machines.empty? else # String name, just look for a specific VM @logger.debug("Finding machine that match name: #{name}") machines << get_machine.call(name.to_sym) raise Errors::VMNotFoundError, name: name if !machines[0] end end else # No name was given, so we return every VM in the order # configured. @logger.debug("Loading all machines...") machines = @env.machine_names.map do |machine_name| get_machine.call(machine_name) end end # Make sure we're only working with one VM if single target if options[:single_target] && machines.length != 1 @logger.debug("Using primary machine since single target") primary_name = @env.primary_machine_name raise Errors::MultiVMTargetRequired if !primary_name machines = [get_machine.call(primary_name)] end # If we asked for reversed ordering, then reverse it machines.reverse! if options[:reverse] # Go through each VM and yield it! color_order = [:default] color_index = 0 machines.each do |machine| # Set the machine color machine.ui.opts[:color] = color_order[color_index % color_order.length] color_index += 1 @logger.info("With machine: #{machine.name} (#{machine.provider.inspect})") yield machine # Call the state method so that we update our index state. Don't # worry about exceptions here, since we just care about updating # the cache. begin # Called for side effects machine.state rescue Errors::VagrantError end end end # This method will split the argv given into three parts: the # flags to this command, the subcommand, and the flags to the # subcommand. For example: # # -v status -h -v # # The above would yield 3 parts: # # ["-v"] # "status" # ["-h", "-v"] # # These parts are useful because the first is a list of arguments # given to the current command, the second is a subcommand, and the # third are the commands given to the subcommand. # # @return [Array] The three parts. def split_main_and_subcommand(argv) # Initialize return variables main_args = nil sub_command = nil sub_args = [] # We split the arguments into two: One set containing any # flags before a word, and then the rest. The rest are what # get actually sent on to the subcommand. argv.each_index do |i| if !argv[i].start_with?("-") # We found the beginning of the sub command. Split the # args up. main_args = argv[0, i] sub_command = argv[i] sub_args = argv[i + 1, argv.length - i + 1] # Break so we don't find the next non flag and shift our # main args. break end end # Handle the case that argv was empty or didn't contain any subcommand main_args = argv.dup if main_args.nil? return [main_args, sub_command, sub_args] end end end end end vagrant-2.0.2/lib/vagrant/plugin/v2/communicator.rb000066400000000000000000000110461323370221500222370ustar00rootroot00000000000000require "timeout" module Vagrant module Plugin module V2 # Base class for a communicator in Vagrant. A communicator is # responsible for communicating with a machine in some way. There # are various stages of Vagrant that require things such as uploading # files to the machine, executing shell commands, etc. Implementors # of this class are expected to provide this functionality in some # way. # # Note that a communicator must provide **all** of the methods # in this base class. There is currently no way for one communicator # to provide say a more efficient way of uploading a file, but not # provide shell execution. This sort of thing will come in a future # version. class Communicator # This returns true/false depending on if the given machine # can be communicated with using this communicator. If this returns # `true`, then this class will be used as the primary communication # method for the machine. # # @return [Boolean] def self.match?(machine) true end # Initializes the communicator with the machine that we will be # communicating with. This base method does nothing (it doesn't # even store the machine in an instance variable for you), so you're # expected to override this and do something with the machine if # you care about it. # # @param [Machine] machine The machine this instance is expected to # communicate with. def initialize(machine) end # Checks if the target machine is ready for communication. If this # returns true, then all the other methods for communicating with # the machine are expected to be functional. # # @return [Boolean] def ready? false end # wait_for_ready waits until the communicator is ready, blocking # until then. It will wait up to the given duration or raise an # exception if something goes wrong. # # @param [Integer] duration Timeout in seconds. # @return [Boolean] Will return true on successful connection # or false on timeout. def wait_for_ready(duration) # By default, we implement a naive solution. begin Timeout.timeout(duration) do while true return true if ready? sleep 0.5 end end rescue Timeout::Error # We timed out, we failed. end return false end # Download a file from the remote machine to the local machine. # # @param [String] from Path of the file on the remote machine. # @param [String] to Path of where to save the file locally. def download(from, to) end # Upload a file to the remote machine. # # @param [String] from Path of the file locally to upload. # @param [String] to Path of where to save the file on the remote # machine. def upload(from, to) end # Execute a command on the remote machine. The exact semantics # of this method are up to the implementor, but in general the # users of this class will expect this to be a shell. # # This method gives you no way to write data back to the remote # machine, so only execute commands that don't expect input. # # @param [String] command Command to execute. # @yield [type, data] Realtime output of the command being executed. # @yieldparam [String] type Type of the output. This can be # `:stdout`, `:stderr`, etc. The exact types are up to the # implementor. # @yieldparam [String] data Data for the given output. # @return [Integer] Exit code of the command. def execute(command, opts=nil) end # Executes a command on the remote machine with administrative # privileges. See {#execute} for documentation, as the API is the # same. # # @see #execute def sudo(command, opts=nil) end # Executes a command and returns true if the command succeeded, # and false otherwise. By default, this executes as a normal user, # and it is up to the communicator implementation if they expose an # option for running tests as an administrator. # # @see #execute def test(command, opts=nil) end end end end end vagrant-2.0.2/lib/vagrant/plugin/v2/components.rb000066400000000000000000000055221323370221500217260ustar00rootroot00000000000000module Vagrant module Plugin module V2 # This is the container class for the components of a single plugin. # This allows us to separate the plugin class which defines the # components, and the actual container of those components. This # removes a bit of state overhead from the plugin class itself. class Components # This contains all the action hooks. # # @return [Hash] attr_reader :action_hooks # This contains all the command plugins by name, and returns # the command class and options. The command class is wrapped # in a Proc so that it can be lazy loaded. # # @return [Registry>] attr_reader :commands # This contains all the configuration plugins by scope. # # @return [Hash] attr_reader :configs # This contains all the guests and their parents. # # @return [Registry>] attr_reader :guests # This contains all the registered guest capabilities. # # @return [Hash] attr_reader :guest_capabilities # This contains all the hosts and their parents. # # @return [Registry>] attr_reader :hosts # This contains all the registered host capabilities. # # @return [Hash] attr_reader :host_capabilities # This contains all the provider plugins by name, and returns # the provider class and options. # # @return [Hash] attr_reader :providers # This contains all the registered provider capabilities. # # @return [Hash] attr_reader :provider_capabilities # This contains all the push implementations by name. # # @return [Registry>] attr_reader :pushes # This contains all the synced folder implementations by name. # # @return [Registry>] attr_reader :synced_folders def initialize # The action hooks hash defaults to [] @action_hooks = Hash.new { |h, k| h[k] = [] } @commands = Registry.new @configs = Hash.new { |h, k| h[k] = Registry.new } @guests = Registry.new @guest_capabilities = Hash.new { |h, k| h[k] = Registry.new } @hosts = Registry.new @host_capabilities = Hash.new { |h, k| h[k] = Registry.new } @providers = Registry.new @provider_capabilities = Hash.new { |h, k| h[k] = Registry.new } @pushes = Registry.new @synced_folders = Registry.new end end end end end vagrant-2.0.2/lib/vagrant/plugin/v2/config.rb000066400000000000000000000125711323370221500210100ustar00rootroot00000000000000require "set" module Vagrant module Plugin module V2 # This is the base class for a configuration key defined for # V2. Any configuration key plugins for V2 should inherit from this # class. class Config # This constant represents an unset value. This is useful so it is # possible to know the difference between a configuration value that # was never set, and a value that is nil (explicitly). Best practice # is to initialize all variables to this value, then the {#merge} # method below will "just work" in many cases. UNSET_VALUE = Object.new # This is called as a last-minute hook that allows the configuration # object to finalize itself before it will be put into use. This is # a useful place to do some defaults in the case the user didn't # configure something or so on. # # An example of where this sort of thing is used or has been used: # the "vm" configuration key uses this to make sure that at least # one sub-VM has been defined: the default VM. # # The configuration object is expected to mutate itself. def finalize! # Default implementation is to do nothing. end # Merge another configuration object into this one. This assumes that # the other object is the same class as this one. This should not # mutate this object, but instead should return a new, merged object. # # The default implementation will simply iterate over the instance # variables and merge them together, with this object overriding # any conflicting instance variables of the older object. Instance # variables starting with "__" (double underscores) will be ignored. # This lets you set some sort of instance-specific state on your # configuration keys without them being merged together later. # # @param [Object] other The other configuration object to merge from, # this must be the same type of object as this one. # @return [Object] The merged object. def merge(other) result = self.class.new # Set all of our instance variables on the new class [self, other].each do |obj| obj.instance_variables.each do |key| # Ignore keys that start with a double underscore. This allows # configuration classes to still hold around internal state # that isn't propagated. if !key.to_s.start_with?("@__") # Don't set the value if it is the unset value, either. value = obj.instance_variable_get(key) result.instance_variable_set(key, value) if value != UNSET_VALUE end end end # Persist through the set of invalid methods this_invalid = @__invalid_methods || Set.new other_invalid = other.instance_variable_get(:"@__invalid_methods") || Set.new result.instance_variable_set(:"@__invalid_methods", this_invalid + other_invalid) result end # Capture all bad configuration calls and save them for an error # message later during validation. def method_missing(name, *args, &block) return super if @__finalized name = name.to_s name = name[0...-1] if name.end_with?("=") @__invalid_methods ||= Set.new @__invalid_methods.add(name) # Return the dummy object so that anything else works ::Vagrant::Config::V2::DummyConfig.new end # Allows setting options from a hash. By default this simply calls # the `#{key}=` method on the config class with the value, which is # the expected behavior most of the time. # # This is expected to mutate itself. # # @param [Hash] options A hash of options to set on this configuration # key. def set_options(options) options.each do |key, value| send("#{key}=", value) end end # Converts this configuration object to JSON. def to_json(*a) instance_variables_hash.to_json(*a) end # A default to_s implementation. def to_s self.class.to_s end # Returns the instance variables as a hash of key-value pairs. def instance_variables_hash instance_variables.inject({}) do |acc, iv| acc[iv.to_s[1..-1]] = instance_variable_get(iv) acc end end # Called after the configuration is finalized and loaded to validate # this object. # # @param [Machine] machine Access to the machine that is being # validated. # @return [Hash] def validate(machine) return { self.to_s => _detected_errors } end # This returns any automatically detected errors. # # @return [Array] def _detected_errors return [] if !@__invalid_methods || @__invalid_methods.empty? return [I18n.t("vagrant.config.common.bad_field", fields: @__invalid_methods.to_a.sort.join(", "))] end # An internal finalize call that no subclass should override. def _finalize! @__finalized = true end end end end end vagrant-2.0.2/lib/vagrant/plugin/v2/errors.rb000066400000000000000000000006211323370221500210500ustar00rootroot00000000000000# This file contains all the errors that the V2 plugin interface # may throw. module Vagrant module Plugin module V2 # Exceptions that can be thrown within the plugin interface all # inherit from this parent exception. class Error < StandardError; end # This is thrown when a command name given is invalid. class InvalidCommandName < Error; end end end end vagrant-2.0.2/lib/vagrant/plugin/v2/guest.rb000066400000000000000000000014161323370221500206660ustar00rootroot00000000000000module Vagrant module Plugin module V2 # A base class for a guest OS. A guest OS is responsible for detecting # that the guest operating system running within the machine. The guest # can then be extended with various "guest capabilities" which are their # own form of plugin. # # The guest class itself is only responsible for detecting itself, # and may provide helpers for the capabilties. class Guest # This method is called when the machine is booted and has communication # capabilities in order to detect whether this guest operating system # is running within the machine. # # @return [Boolean] def detect?(machine) false end end end end end vagrant-2.0.2/lib/vagrant/plugin/v2/host.rb000066400000000000000000000010551323370221500205130ustar00rootroot00000000000000module Vagrant module Plugin module V2 # Base class for a host in Vagrant. A host class contains functionality # that is specific to a specific OS that is running Vagrant. This # abstraction is done becauase there is some host-specific logic that # Vagrant must do in some cases. class Host # This returns true/false depending on if the current running system # matches the host class. # # @return [Boolean] def detect?(env) false end end end end end vagrant-2.0.2/lib/vagrant/plugin/v2/manager.rb000066400000000000000000000147461323370221500211630ustar00rootroot00000000000000require "log4r" module Vagrant module Plugin module V2 # This class maintains a list of all the registered plugins as well # as provides methods that allow querying all registered components of # those plugins as a single unit. class Manager attr_reader :registered def initialize @logger = Log4r::Logger.new("vagrant::plugin::v2::manager") @registered = [] end # This returns all the action hooks. # # @return [Array] def action_hooks(hook_name) result = [] @registered.each do |plugin| result += plugin.components.action_hooks[Plugin::ALL_ACTIONS] result += plugin.components.action_hooks[hook_name] end result end # This returns all the registered commands. # # @return [Registry>] def commands Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.components.commands) end end end # This returns all the registered communicators. # # @return [Hash] def communicators Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.communicator) end end end # This returns all the registered configuration classes. # # @return [Hash] def config Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.components.configs[:top]) end end end # This returns all the registered guests. # # @return [Hash] def guests Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.components.guests) end end end # This returns all the registered guest capabilities. # # @return [Hash] def guest_capabilities results = Hash.new { |h, k| h[k] = Registry.new } @registered.each do |plugin| plugin.components.guest_capabilities.each do |guest, caps| results[guest].merge!(caps) end end results end # This returns all the registered guests. # # @return [Hash] def hosts Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.components.hosts) end end end # This returns all the registered host capabilities. # # @return [Hash] def host_capabilities results = Hash.new { |h, k| h[k] = Registry.new } @registered.each do |plugin| plugin.components.host_capabilities.each do |host, caps| results[host].merge!(caps) end end results end # This returns all registered providers. # # @return [Hash] def providers Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.components.providers) end end end # This returns all the registered provider capabilities. # # @return [Hash] def provider_capabilities results = Hash.new { |h, k| h[k] = Registry.new } @registered.each do |plugin| plugin.components.provider_capabilities.each do |provider, caps| results[provider].merge!(caps) end end results end # This returns all the config classes for the various providers. # # @return [Hash] def provider_configs Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.components.configs[:provider]) end end end # This returns all the config classes for the various provisioners. # # @return [Registry] def provisioner_configs Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.components.configs[:provisioner]) end end end # This returns all registered provisioners. # # @return [Hash] def provisioners Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.provisioner) end end end # This returns all registered pushes. # # @return [Registry] def pushes Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.components.pushes) end end end # This returns all the config classes for the various pushes. # # @return [Registry] def push_configs Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.components.configs[:push]) end end end # This returns all synced folder implementations. # # @return [Registry] def synced_folders Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.components.synced_folders) end end end # This registers a plugin. This should _NEVER_ be called by the public # and should only be called from within Vagrant. Vagrant will # automatically register V2 plugins when a name is set on the # plugin. def register(plugin) if !@registered.include?(plugin) @logger.info("Registered plugin: #{plugin.name}") @registered << plugin end end # This clears out all the registered plugins. This is only used by # unit tests and should not be called directly. def reset! @registered.clear end # This unregisters a plugin so that its components will no longer # be used. Note that this should only be used for testing purposes. def unregister(plugin) if @registered.include?(plugin) @logger.info("Unregistered: #{plugin.name}") @registered.delete(plugin) end end end end end end vagrant-2.0.2/lib/vagrant/plugin/v2/plugin.rb000066400000000000000000000234651323370221500210450ustar00rootroot00000000000000require "set" require "log4r" require "vagrant/plugin/v2/components" module Vagrant module Plugin module V2 # This is the superclass for all V2 plugins. class Plugin # Special marker that can be used for action hooks that matches # all action sequences. ALL_ACTIONS = :__all_actions__ # The logger for this class. LOGGER = Log4r::Logger.new("vagrant::plugin::v2::plugin") # Set the root class up to be ourself, so that we can reference this # from within methods which are probably in subclasses. ROOT_CLASS = self # This returns the manager for all V2 plugins. # # @return [V2::Manager] def self.manager @manager ||= Manager.new end # Returns the {Components} for this plugin. # # @return [Components] def self.components @components ||= Components.new end # Set the name of the plugin. The moment that this is called, the # plugin will be registered and available. Before this is called, a # plugin does not exist. The name must be unique among all installed # plugins. # # @param [String] name Name of the plugin. # @return [String] The name of the plugin. def self.name(name=UNSET_VALUE) # Get or set the value first, so we have a name for logging when # we register. result = get_or_set(:name, name) # The plugin should be registered if we're setting a real name on it Plugin.manager.register(self) if name != UNSET_VALUE # Return the result result end # Sets a human-friendly descrition of the plugin. # # @param [String] value Description of the plugin. # @return [String] Description of the plugin. def self.description(value=UNSET_VALUE) get_or_set(:description, value) end # Registers a callback to be called when a specific action sequence # is run. This allows plugin authors to hook into things like VM # bootup, VM provisioning, etc. # # @param [String] name Name of the action. # @param [Symbol] hook_name The location to hook. If this isn't # set, every middleware action is hooked. # @return [Array] List of the hooks for the given action. def self.action_hook(name, hook_name=nil, &block) # The name is currently not used but we want it for the future. hook_name ||= ALL_ACTIONS components.action_hooks[hook_name.to_sym] << block end # Defines additional command line commands available by key. The key # becomes the subcommand, so if you register a command "foo" then # "vagrant foo" becomes available. # # @param [String] name Subcommand key. def self.command(name, **opts, &block) # Validate the name of the command if name.to_s !~ /^[-a-z0-9]+$/i raise InvalidCommandName, "Commands can only contain letters, numbers, and hyphens" end # By default, the command is primary opts[:primary] = true if !opts.key?(:primary) # Register the command components.commands.register(name.to_sym) do [block, opts] end nil end # Defines additional communicators to be available. Communicators # should be returned by a block passed to this method. This is done # to ensure that the class is lazy loaded, so if your class inherits # from or uses any Vagrant internals specific to Vagrant 1.0, then # the plugin can still be defined without breaking anything in future # versions of Vagrant. # # @param [String] name Communicator name. def self.communicator(name=UNSET_VALUE, &block) data[:communicator] ||= Registry.new # Register a new communicator class only if a name was given. data[:communicator].register(name.to_sym, &block) if name != UNSET_VALUE # Return the registry data[:communicator] end # Defines additional configuration keys to be available in the # Vagrantfile. The configuration class should be returned by a # block passed to this method. This is done to ensure that the class # is lazy loaded, so if your class inherits from any classes that # are specific to Vagrant 1.0, then the plugin can still be defined # without breaking anything in future versions of Vagrant. # # @param [String] name Configuration key. def self.config(name, scope=nil, &block) scope ||= :top components.configs[scope].register(name.to_sym, &block) nil end # Defines an additionally available guest implementation with # the given key. # # @param [String] name Name of the guest. # @param [String] parent Name of the parent guest (if any) def self.guest(name, parent=nil, &block) components.guests.register(name.to_sym) do parent = parent.to_sym if parent [block.call, parent] end nil end # Defines a capability for the given guest. The block should return # a class/module that has a method with the capability name, ready # to be executed. This means that if it is an instance method, # the block should return an instance of the class. # # @param [String] guest The name of the guest # @param [String] cap The name of the capability def self.guest_capability(guest, cap, &block) components.guest_capabilities[guest.to_sym].register(cap.to_sym, &block) nil end # Defines an additionally available host implementation with # the given key. # # @param [String] name Name of the host. # @param [String] parent Name of the parent host (if any) def self.host(name, parent=nil, &block) components.hosts.register(name.to_sym) do parent = parent.to_sym if parent [block.call, parent] end nil end # Defines a capability for the given host. The block should return # a class/module that has a method with the capability name, ready # to be executed. This means that if it is an instance method, # the block should return an instance of the class. # # @param [String] host The name of the host # @param [String] cap The name of the capability def self.host_capability(host, cap, &block) components.host_capabilities[host.to_sym].register(cap.to_sym, &block) nil end # Registers additional providers to be available. # # @param [Symbol] name Name of the provider. def self.provider(name=UNSET_VALUE, options=nil, &block) options ||= {} options[:priority] ||= 5 components.providers.register(name.to_sym) do [block.call, options] end nil end # Defines a capability for the given provider. The block should return # a class/module that has a method with the capability name, ready # to be executed. This means that if it is an instance method, # the block should return an instance of the class. # # @param [String] provider The name of the provider # @param [String] cap The name of the capability def self.provider_capability(provider, cap, &block) components.provider_capabilities[provider.to_sym].register(cap.to_sym, &block) nil end # Registers additional provisioners to be available. # # @param [String] name Name of the provisioner. def self.provisioner(name=UNSET_VALUE, &block) data[:provisioners] ||= Registry.new # Register a new provisioner class only if a name was given data[:provisioners].register(name.to_sym, &block) if name != UNSET_VALUE # Return the registry data[:provisioners] end # Registers additional pushes to be available. # # @param [String] name Name of the push. # @param [Hash] options List of options for the push. def self.push(name, options=nil, &block) components.pushes.register(name.to_sym) do [block.call, options] end nil end # Registers additional synced folder implementations. # # @param [String] name Name of the implementation. # @param [Integer] priority The priority of the implementation, # higher (big) numbers are tried before lower (small) numbers. def self.synced_folder(name, priority=10, &block) components.synced_folders.register(name.to_sym) do [block.call, priority] end nil end # Returns the internal data associated with this plugin. This # should NOT be called by the general public. # # @return [Hash] def self.data @data ||= {} end protected # Sentinel value denoting that a value has not been set. UNSET_VALUE = Object.new # Helper method that will set a value if a value is given, or otherwise # return the already set value. # # @param [Symbol] key Key for the data # @param [Object] value Value to store. # @return [Object] Stored value. def self.get_or_set(key, value=UNSET_VALUE) # If no value is to be set, then return the value we have already set return data[key] if value.eql?(UNSET_VALUE) # Otherwise set the value data[key] = value end end end end end vagrant-2.0.2/lib/vagrant/plugin/v2/provider.rb000066400000000000000000000103401323370221500213650ustar00rootroot00000000000000require "vagrant/capability_host" module Vagrant module Plugin module V2 # This is the base class for a provider for the V2 API. A provider # is responsible for creating compute resources to match the needs # of a Vagrant-configured system. class Provider include CapabilityHost # This is called early, before a machine is instantiated, to check # if this provider is usable. This should return true or false. # # If raise_error is true, then instead of returning false, this # should raise an error with a helpful message about why this # provider cannot be used. # # @param [Boolean] raise_error If true, raise exception if not usable. # @return [Boolean] def self.usable?(raise_error=false) # Return true by default for backwards compat since this was # introduced long after providers were being written. true end # This is called early, before a machine is instantiated, to check # if this provider is installed. This should return true or false. # # If the provider is not installed and Vagrant determines it is # able to install this provider, then it will do so. Installation # is done by calling Environment.install_provider. # # If Environment.can_install_provider? returns false, then an error # will be shown to the user. def self.installed? # By default return true for backwards compat so all providers # continue to work. true end # Initialize the provider to represent the given machine. # # @param [Vagrant::Machine] machine The machine that this provider # is responsible for. def initialize(machine) end # This should return an action callable for the given name. # # @param [Symbol] name Name of the action. # @return [Object] A callable action sequence object, whether it # is a proc, object, etc. def action(name) nil end # This method is called if the underying machine ID changes. Providers # can use this method to load in new data for the actual backing # machine or to realize that the machine is now gone (the ID can # become `nil`). No parameters are given, since the underlying machine # is simply the machine instance given to this object. And no # return value is necessary. def machine_id_changed end # This should return a hash of information that explains how to # SSH into the machine. If the machine is not at a point where # SSH is even possible, then `nil` should be returned. # # The general structure of this returned hash should be the # following: # # { # host: "1.2.3.4", # port: "22", # username: "mitchellh", # private_key_path: "/path/to/my/key" # } # # **Note:** Vagrant only supports private key based authentication, # mainly for the reason that there is no easy way to exec into an # `ssh` prompt with a password, whereas we can pass a private key # via commandline. # # @return [Hash] SSH information. For the structure of this hash # read the accompanying documentation for this method. def ssh_info nil end # This should return the state of the machine within this provider. # The state must be an instance of {MachineState}. Please read the # documentation of that class for more information. # # @return [MachineState] def state nil end # This is an internal initialize function that should never be # overridden. It is used to initialize some common internal state # that is used in a provider. def _initialize(name, machine) initialize_capabilities!( name.to_sym, { name.to_sym => [Class.new, nil] }, Vagrant.plugin("2").manager.provider_capabilities, machine, ) end end end end end vagrant-2.0.2/lib/vagrant/plugin/v2/provisioner.rb000066400000000000000000000036661323370221500221270ustar00rootroot00000000000000module Vagrant module Plugin module V2 # This is the base class for a provisioner for the V2 API. A provisioner # is primarily responsible for installing software on a Vagrant guest. class Provisioner attr_reader :machine attr_reader :config # Initializes the provisioner with the machine that it will be # provisioning along with the provisioner configuration (if there # is any). # # The provisioner should _not_ do anything at this point except # initialize internal state. # # @param [Machine] machine The machine that this will be provisioning. # @param [Object] config Provisioner configuration, if one was set. def initialize(machine, config) @machine = machine @config = config end # Called with the root configuration of the machine so the provisioner # can add some configuration on top of the machine. # # During this step, and this step only, the provisioner should modify # the root machine configuration to add any additional features it # may need. Examples include sharing folders, networking, and so on. # This step is guaranteed to be called before any of those steps are # done so the provisioner may do that. # # No return value is expected. def configure(root_config) end # This is the method called when the actual provisioning should be # done. The communicator is guaranteed to be ready at this point, # and any shared folders or networks are already setup. # # No return value is expected. def provision end # This is the method called when destroying a machine that allows # for any state related to the machine created by the provisioner # to be cleaned up. def cleanup end end end end end vagrant-2.0.2/lib/vagrant/plugin/v2/push.rb000066400000000000000000000011311323370221500205100ustar00rootroot00000000000000module Vagrant module Plugin module V2 class Push attr_reader :env attr_reader :config # Initializes the pusher with the given environment the push # configuration. # # @param [Environment] env # @param [Object] config Push configuration def initialize(env, config) @env = env @config = config end # This is the method called when the actual pushing should be # done. # # No return value is expected. def push end end end end end vagrant-2.0.2/lib/vagrant/plugin/v2/synced_folder.rb000066400000000000000000000036341323370221500223630ustar00rootroot00000000000000module Vagrant module Plugin module V2 # This is the base class for a synced folder implementation. class SyncedFolder # This is called early when the synced folder is set to determine # if this implementation can be used for this machine. This should # return true or false. # # @param [Machine] machine # @param [Boolean] raise_error If true, should raise an exception # if it isn't usable. # @return [Boolean] def usable?(machine, raise_error=false) end # DEPRECATED: This will be removed. # # @deprecated def prepare(machine, folders, opts) end # This is called after the machine is booted and after networks # are setup. # # This might be called with new folders while the machine is running. # If so, then this should add only those folders without removing # any existing ones. # # No return value. def enable(machine, folders, opts) end # This is called to remove the synced folders from a running # machine. # # This is not guaranteed to be called, but this should be implemented # by every synced folder implementation. # # @param [Machine] machine The machine to modify. # @param [Hash] folders The folders to remove. This will not contain # any folders that should remain. # @param [Hash] opts Any options for the synced folders. def disable(machine, folders, opts) end # This is called after destroying the machine during a # `vagrant destroy` and also prior to syncing folders during # a `vagrant up`. # # No return value. # # @param [Machine] machine # @param [Hash] opts def cleanup(machine, opts) end end end end end vagrant-2.0.2/lib/vagrant/registry.rb000066400000000000000000000044661323370221500175720ustar00rootroot00000000000000module Vagrant # Register components in a single location that can be queried. # # This allows certain components (such as guest systems, configuration # pieces, etc.) to be registered and queried, lazily. class Registry def initialize @items = {} @results_cache = {} end # Register a key with a lazy-loaded value. # # If a key with the given name already exists, it is overwritten. def register(key, &block) raise ArgumentError, "block required" if !block_given? @items[key] = block end # Get a value by the given key. # # This will evaluate the block given to `register` and return the # resulting value. def get(key) return nil if !@items.key?(key) return @results_cache[key] if @results_cache.key?(key) @results_cache[key] = @items[key].call end alias :[] :get # Checks if the given key is registered with the registry. # # @return [Boolean] def key?(key) @items.key?(key) end alias_method :has_key?, :key? # Returns an array populated with the keys of this object. # # @return [Array] def keys @items.keys end # Iterate over the keyspace. def each(&block) @items.each do |key, _| yield key, get(key) end end # Return the number of elements in this registry. # # @return [Integer] def length @items.keys.length end alias_method :size, :length # Checks if this registry has any items. # # @return [Boolean] def empty? @items.keys.empty? end # Merge one registry with another and return a completely new # registry. Note that the result cache is completely busted, so # any gets on the new registry will result in a cache miss. def merge(other) self.class.new.tap do |result| result.merge!(self) result.merge!(other) end end # Like #{merge} but merges into self. def merge!(other) @items.merge!(other.__internal_state[:items]) self end # Converts this registry to a hash def to_hash result = {} self.each do |key, value| result[key] = value end result end def __internal_state { items: @items, results_cache: @results_cache } end end end vagrant-2.0.2/lib/vagrant/shared_helpers.rb000066400000000000000000000073401323370221500207040ustar00rootroot00000000000000require "pathname" require "tempfile" require "thread" module Vagrant @@global_lock = Mutex.new # This is the default endpoint of the Vagrant Cloud in # use. API calls will be made to this for various functions # of Vagrant that may require remote access. # # @return [String] DEFAULT_SERVER_URL = "https://vagrantcloud.com" # Max number of seconds to wait for joining an active thread. # # @return [Integer] # @note This is not the maxium time for a thread to complete. THREAD_MAX_JOIN_TIMEOUT = 60 # This holds a global lock for the duration of the block. This should # be invoked around anything that is modifying process state (such as # environmental variables). def self.global_lock @@global_lock.synchronize do return yield end end # This returns a true/false showing whether we're running from the # environment setup by the Vagrant installers. # # @return [Boolean] def self.in_installer? !!ENV["VAGRANT_INSTALLER_ENV"] end # Returns the path to the embedded directory of the Vagrant installer, # if there is one (if we're running in an installer). # # @return [String] def self.installer_embedded_dir return nil if !Vagrant.in_installer? ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"] end # Should the plugin system be initialized # # @return [Boolean] def self.plugins_init? !ENV['VAGRANT_DISABLE_PLUGIN_INIT'] end # This returns whether or not 3rd party plugins should and can be loaded. # # @return [Boolean] def self.plugins_enabled? !ENV["VAGRANT_NO_PLUGINS"] end # Whether or not super quiet mode is enabled. This is ill-advised. # # @return [Boolean] def self.very_quiet? !!ENV["VAGRANT_I_KNOW_WHAT_IM_DOING_PLEASE_BE_QUIET"] end # The current log level for Vagrant # # @return [String] def self.log_level ENV["VAGRANT_LOG"] end # Returns the URL prefix to the server. # # @return [String] def self.server_url(config_server_url=nil) result = ENV["VAGRANT_SERVER_URL"] result = config_server_url if result == "" or result == nil result || DEFAULT_SERVER_URL end # The source root is the path to the root directory of the Vagrant source. # # @return [Pathname] def self.source_root @source_root ||= Pathname.new(File.expand_path('../../../', __FILE__)) end # This returns the path to the ~/.vagrant.d folder where Vagrant's # per-user state is stored. # # @return [Pathname] def self.user_data_path # Use user spcified env var if available path = ENV["VAGRANT_HOME"] # On Windows, we default to the USERPROFILE directory if it # is available. This is more compatible with Cygwin and sharing # the home directory across shells. if !path && ENV["USERPROFILE"] path = "#{ENV["USERPROFILE"]}/.vagrant.d" end # Fallback to the default path ||= "~/.vagrant.d" Pathname.new(path).expand_path end # This returns true/false if the running version of Vagrant is # a pre-release version (development) # # @return [Boolean] def self.prerelease? Gem::Version.new(Vagrant::VERSION).prerelease? end # This allows control over dependency resolution when installing # plugins into vagrant. When true, dependency libraries that Vagrant # core relies upon will be hard constraints. # # @return [Boolean] def self.strict_dependency_enforcement if ENV["VAGRANT_DISABLE_STRICT_DEPENDENCY_ENFORCEMENT"] false else true end end # Use Ruby Resolv in place of libc # # @return [boolean] enabled or not def self.enable_resolv_replace if !ENV["VAGRANT_DISABLE_RESOLV_REPLACE"] begin require "resolv-replace" true rescue false end else false end end end vagrant-2.0.2/lib/vagrant/ui.rb000066400000000000000000000265731323370221500163420ustar00rootroot00000000000000require "delegate" require "io/console" require "thread" require "log4r" require "vagrant/util/platform" require "vagrant/util/safe_puts" module Vagrant module UI # Vagrant UIs handle communication with the outside world (typically # through a shell). They must respond to the following methods: # # * `info` # * `warn` # * `error` # * `success` class Interface # Opts can be used to set some options. These options are implementation # specific. See the implementation for more docs. attr_accessor :opts # @return [IO] UI input. Defaults to `$stdin`. attr_accessor :stdin # @return [IO] UI output. Defaults to `$stdout`. attr_accessor :stdout # @return [IO] UI error output. Defaults to `$stderr`. attr_accessor :stderr def initialize @logger = Log4r::Logger.new("vagrant::ui::interface") @opts = {} @stdin = $stdin @stdout = $stdout @stderr = $stderr end def initialize_copy(original) super @opts = original.opts.dup end [:ask, :detail, :warn, :error, :info, :output, :success].each do |method| define_method(method) do |message, *opts| # Log normal console messages begin @logger.info { "#{method}: #{message}" } rescue ThreadError # We're being called in a trap-context. Wrap in a thread. Thread.new do @logger.info { "#{method}: #{message}" } end.join(THREAD_MAX_JOIN_TIMEOUT) end end end [:clear_line, :report_progress].each do |method| # By default do nothing, these aren't logged define_method(method) { |*args| } end # @return [false] def color? return false end # For machine-readable output. # # @param [String] type The type of the data # @param [Array] data The data associated with the type def machine(type, *data) @logger.info("Machine: #{type} #{data.inspect}") end end # This is a UI implementation that does nothing. class Silent < Interface def ask(*args) super # Silent can't do this, obviously. raise Errors::UIExpectsTTY end end class MachineReadable < Interface include Util::SafePuts def initialize super @lock = Mutex.new end def ask(*args) super # Machine-readable can't ask for input raise Errors::UIExpectsTTY end [:detail, :warn, :error, :info, :output, :success].each do |method| define_method(method) do |message, *args, **opts| machine("ui", method.to_s, message, *args, **opts) end end def machine(type, *data) opts = {} opts = data.pop if data.last.kind_of?(Hash) target = opts[:target] || "" # Prepare the data by replacing characters that aren't outputted data.each_index do |i| data[i] = data[i].to_s.dup data[i].gsub!(",", "%!(VAGRANT_COMMA)") data[i].gsub!("\n", "\\n") data[i].gsub!("\r", "\\r") end # Avoid locks in a trap context introduced from Ruby 2.0 Thread.new do @lock.synchronize do safe_puts("#{Time.now.utc.to_i},#{target},#{type},#{data.join(",")}") end end.join(THREAD_MAX_JOIN_TIMEOUT) end end # This is a UI implementation that outputs the text as is. It # doesn't add any color. class Basic < Interface include Util::SafePuts def initialize super @lock = Mutex.new end # Use some light meta-programming to create the various methods to # output text to the UI. These all delegate the real functionality # to `say`. [:detail, :info, :warn, :error, :output, :success].each do |method| class_eval <<-CODE def #{method}(message, *args) super(message) say(#{method.inspect}, message, *args) end CODE end def ask(message, opts=nil) super(message) # We can't ask questions when the output isn't a TTY. raise Errors::UIExpectsTTY if !@stdin.tty? && !Vagrant::Util::Platform.windows? # Setup the options so that the new line is suppressed opts ||= {} opts[:echo] = true if !opts.key?(:echo) opts[:new_line] = false if !opts.key?(:new_line) opts[:prefix] = false if !opts.key?(:prefix) # Output the data say(:info, message, opts) input = nil if opts[:echo] || !@stdin.respond_to?(:noecho) input = @stdin.gets else begin input = @stdin.noecho(&:gets) # Output a newline because without echo, the newline isn't # echoed either. say(:info, "\n", opts) rescue Errno::EBADF # This means that stdin doesn't support echoless input. say(:info, "\n#{I18n.t("vagrant.stdin_cant_hide_input")}\n ", opts) # Ask again, with echo enabled input = ask(message, opts.merge(echo: true)) end end # Get the results and chomp off the newline. We do a logical OR # here because `gets` can return a nil, for example in the case # that ctrl-D is pressed on the input. (input || "").chomp end # This is used to output progress reports to the UI. # Send this method progress/total and it will output it # to the UI. Send `clear_line` to clear the line to show # a continuous progress meter. def report_progress(progress, total, show_parts=true) if total && total > 0 percent = (progress.to_f / total.to_f) * 100 line = "Progress: #{percent.to_i}%" line << " (#{progress} / #{total})" if show_parts else line = "Progress: #{progress}" end info(line, new_line: false) end def clear_line # See: https://en.wikipedia.org/wiki/ANSI_escape_code reset = "\r\033[K" info(reset, new_line: false) end # This method handles actually outputting a message of a given type # to the console. def say(type, message, **opts) defaults = { new_line: true, prefix: true } opts = defaults.merge(@opts).merge(opts) # Don't output if we're hiding details return if type == :detail && opts[:hide_detail] # Determine whether we're expecting to output our # own new line or not. printer = opts[:new_line] ? :puts : :print # Determine the proper IO channel to send this message # to based on the type of the message channel = type == :error || opts[:channel] == :error ? @stderr : @stdout # Output! We wrap this in a lock so that it safely outputs only # one line at a time. We wrap this in a thread because as of Ruby 2.0 # we can't acquire locks in a trap context (ctrl-c), so we have to # do this. Thread.new do @lock.synchronize do safe_puts(format_message(type, message, **opts), io: channel, printer: printer) end end.join(THREAD_MAX_JOIN_TIMEOUT) end def format_message(type, message, **opts) Util::CredentialScrubber.desensitize(message) end end # Prefixed wraps an existing UI and adds a prefix to it. class Prefixed < Interface # The prefix for `output` messages. OUTPUT_PREFIX = "==> " def initialize(ui, prefix) super() @prefix = prefix @ui = ui end def initialize_copy(original) super @ui = original.instance_variable_get(:@ui).dup end # Use some light meta-programming to create the various methods to # output text to the UI. These all delegate the real functionality # to `say`. [:ask, :detail, :info, :warn, :error, :output, :success].each do |method| class_eval <<-CODE def #{method}(message, *args, **opts) super(message) if !@ui.opts.key?(:bold) && !opts.key?(:bold) opts[:bold] = #{method.inspect} != :detail && \ #{method.inspect} != :ask end if !opts.key?(:target) opts[:target] = @prefix end @ui.#{method}(format_message(#{method.inspect}, message, **opts), *args, **opts) end CODE end [:clear_line, :report_progress].each do |method| # By default do nothing, these aren't formatted define_method(method) { |*args| @ui.send(method, *args) } end # For machine-readable output, set the prefix in the # options hash and continue it on. def machine(type, *data) opts = {} opts = data.pop if data.last.is_a?(Hash) opts[:target] = @prefix data << opts @ui.machine(type, *data) end # Return the parent's opts. # # @return [Hash] def opts @ui.opts end def format_message(type, message, **opts) opts = self.opts.merge(opts) prefix = "" if !opts.key?(:prefix) || opts[:prefix] prefix = OUTPUT_PREFIX prefix = " " * OUTPUT_PREFIX.length if \ type == :detail || type == :ask || opts[:prefix_spaces] end message = Util::CredentialScrubber.desensitize(message) # Fast-path if there is no prefix return message if prefix.empty? target = @prefix target = opts[:target] if opts.key?(:target) target = "#{target}:" if target != "" # Get the lines. The first default is because if the message # is an empty string, then we want to still use the empty string. lines = [message] lines = message.split("\n") if message != "" # Otherwise, make sure to prefix every line properly lines.map do |line| "#{prefix}#{target} #{line}" end.join("\n") end end # This is a UI implementation that outputs color for various types # of messages. This should only be used with a TTY that supports color, # but is up to the user of the class to verify this is the case. class Colored < Basic # Terminal colors COLORS = { red: 31, green: 32, yellow: 33, blue: 34, magenta: 35, cyan: 36, white: 37, } # @return [true] def color? return true end # This is called by `say` to format the message for output. def format_message(type, message, **opts) # Get the format of the message before adding color. message = super opts = @opts.merge(opts) # Special case some colors for certain message types opts[:color] = :red if type == :error opts[:color] = :green if type == :success opts[:color] = :yellow if type == :warn # If it is a detail, it is not bold. Every other message type # is bolded. bold = !!opts[:bold] colorseq = "#{bold ? 1 : 0 }" if opts[:color] && opts[:color] != :default color = COLORS[opts[:color]] colorseq += ";#{color}" end # Color the message and make sure to reset the color at the end "\033[#{colorseq}m#{message}\033[0m" end end end end vagrant-2.0.2/lib/vagrant/util.rb000066400000000000000000000025361323370221500166730ustar00rootroot00000000000000module Vagrant module Util autoload :Busy, 'vagrant/util/busy' autoload :CheckpointClient, 'vagrant/util/checkpoint_client' autoload :CommandDeprecation, 'vagrant/util/command_deprecation' autoload :Counter, 'vagrant/util/counter' autoload :CredentialScrubber, 'vagrant/util/credential_scrubber' autoload :DeepMerge, 'vagrant/util/deep_merge' autoload :Env, 'vagrant/util/env' autoload :HashWithIndifferentAccess, 'vagrant/util/hash_with_indifferent_access' autoload :GuestInspection, 'vagrant/util/guest_inspection' autoload :LoggingFormatter, 'vagrant/util/logging_formatter' autoload :Platform, 'vagrant/util/platform' autoload :Retryable, 'vagrant/util/retryable' autoload :SafeExec, 'vagrant/util/safe_exec' autoload :SilenceWarnings, 'vagrant/util/silence_warnings' autoload :StackedProcRunner, 'vagrant/util/stacked_proc_runner' autoload :StringBlockEditor, 'vagrant/util/string_block_editor' autoload :Subprocess, 'vagrant/util/subprocess' autoload :TemplateRenderer, 'vagrant/util/template_renderer' autoload :Which, 'vagrant/util/which' end end vagrant-2.0.2/lib/vagrant/util/000077500000000000000000000000001323370221500163405ustar00rootroot00000000000000vagrant-2.0.2/lib/vagrant/util/ansi_escape_code_remover.rb000066400000000000000000000024251323370221500236730ustar00rootroot00000000000000module Vagrant module Util module ANSIEscapeCodeRemover # Removes ANSI escape code sequences from the text and returns # it. # # This removes all the ANSI escape codes listed here along with # the escape codes for VT100 terminals: # # http://ascii-table.com/ansi-escape-sequences.php def remove_ansi_escape_codes(text) # An array of regular expressions which match various kinds # of escape sequences. I can't think of a better single regular # expression or any faster way to do this. matchers = [/\e\[\d*[ABCD]/, # Matches things like \e[4D /\e\[(\d*;)?\d*[HF]/, # Matches \e[1;2H or \e[H /\e\[(s|u|2J|K)/, # Matches \e[s, \e[2J, etc. /\e\[=\d*[hl]/, # Matches \e[=24h /\e\[\?[1-9][hl]/, # Matches \e[?2h /\e\[20[hl]/, # Matches \e[20l] /\e[DME78H]/, # Matches \eD, \eH, etc. /\e\[[0-2]?[JK]/, # Matches \e[0J, \e[K, etc. ] # Take each matcher and replace it with emptiness. matchers.each do |matcher| text.gsub!(matcher, "") end text end end end end vagrant-2.0.2/lib/vagrant/util/busy.rb000066400000000000000000000040721323370221500176520ustar00rootroot00000000000000module Vagrant module Util # Utility class which allows blocks of code to be marked as "busy" # with a specified interrupt handler. During busy areas of code, it # is often undesirable for SIGINTs to immediately kill the application. # This class is a helper to cleanly register callbacks to handle this # situation. class Busy @@registered = [] @@mutex = Mutex.new class << self # Mark a given block of code as a "busy" block of code, which will # register a SIGINT handler for the duration of the block. When a # SIGINT occurs, the `sig_callback` proc will be called. It is up # to the callback to behave properly and exit the application. def busy(sig_callback) register(sig_callback) return yield ensure unregister(sig_callback) end # Registers a SIGINT handler. This typically is called from {busy}. # Callbacks are only registered once, so calling this multiple times # with the same callback has no consequence. def register(sig_callback) @@mutex.synchronize do registered << sig_callback registered.uniq! # Register the handler if this is our first callback. Signal.trap("INT") { fire_callbacks } if registered.length == 1 end end # Unregisters a SIGINT handler. def unregister(sig_callback) @@mutex.synchronize do registered.delete(sig_callback) # Remove the signal trap if no more registered callbacks exist Signal.trap("INT", "DEFAULT") if registered.empty? end end # Fires all the registered callbacks. def fire_callbacks registered.reverse.each { |r| r.call } end # Helper method to get access to the class variable. This is mostly # exposed for tests. This shouldn't be mucked with directly, since it's # structure may change at any time. def registered; @@registered; end end end end end vagrant-2.0.2/lib/vagrant/util/checkpoint_client.rb000066400000000000000000000116161323370221500223570ustar00rootroot00000000000000require "log4r" require "singleton" module Vagrant module Util class CheckpointClient include Singleton # Maximum number of seconds to wait for check to complete CHECKPOINT_TIMEOUT = 10 # @return [Log4r::Logger] attr_reader :logger # @return [Boolean] attr_reader :enabled # @return [Hash] attr_reader :files # @return [Vagrant::Environment] attr_reader :env def initialize @logger = Log4r::Logger.new("vagrant::checkpoint_client") @enabled = false end # Setup will attempt to load the checkpoint library and define # required paths # # @param [Vagrant::Environment] env # @return [self] def setup(env) begin require "checkpoint" @enabled = true rescue LoadError @logger.warn("checkpoint library not found. disabling.") end @files = { signature: env.data_dir.join("checkpoint_signature"), cache: env.data_dir.join("checkpoint_cache") } @checkpoint_thread = nil @env = env self end # Check has completed def complete? !@checkpoint_thread.nil? && !@checkpoint_thread.alive? end # Result of check # # @return [Hash, nil] def result if !enabled || @checkpoint_thread.nil? nil elsif !defined?(@result) @checkpoint_thread.join(CHECKPOINT_TIMEOUT) @result = @checkpoint_thread[:result] else @result end end # Run check # # @return [self] def check if enabled && @checkpoint_thread.nil? logger.debug("starting plugin check") @checkpoint_thread = Thread.new do begin Thread.current[:result] = Checkpoint.check( product: "vagrant", version: VERSION, signature_file: files[:signature], cache_file: files[:cache] ) if !Thread.current[:result].is_a?(Hash) Thread.current[:result] = nil end logger.debug("plugin check complete") rescue => e logger.debug("plugin check failure - #{e}") end end end self end # Display any alerts or version update information # # @return [boolean] true if displayed, false if not def display if !defined?(@displayed) if !complete? @logger.debug("waiting for checkpoint to complete...") end # Don't display if information is cached if result && !result["cached"] version_check alerts_check else @logger.debug("no information received from checkpoint") end @displayed = true else false end end def alerts_check if result["alerts"] && !result["alerts"].empty? result["alerts"].group_by{|a| a["level"]}.each_pair do |_, alerts| alerts.each do |alert| date = nil begin date = Time.at(alert["date"]) rescue date = Time.now end output = I18n.t("vagrant.alert", message: alert["message"], date: date, url: alert["url"] ) case alert["level"] when "info" alert_ui = Vagrant::UI::Prefixed.new(env.ui, "vagrant") alert_ui.info(output) when "warn" alert_ui = Vagrant::UI::Prefixed.new(env.ui, "vagrant-warning") alert_ui.warn(output) when "critical" alert_ui = Vagrant::UI::Prefixed.new(env.ui, "vagrant-alert") alert_ui.error(output) end end env.ui.info("") end else @logger.debug("no alert notifications to display") end end def version_check latest_version = Gem::Version.new(result["current_version"]) installed_version = Gem::Version.new(VERSION) ui = Vagrant::UI::Prefixed.new(env.ui, "vagrant") if latest_version > installed_version @logger.info("new version of Vagrant available - #{latest_version}") ui.info(I18n.t("vagrant.version_upgrade_available", latest_version: latest_version)) env.ui.info("") else @logger.debug("vagrant is currently up to date") end end # @private # Reset the cached values for platform. This is not considered a public # API and should only be used for testing. def reset! logger = @logger instance_variables.each(&method(:remove_instance_variable)) @logger = logger @enabled = false end end end end vagrant-2.0.2/lib/vagrant/util/command_deprecation.rb000066400000000000000000000030631323370221500226620ustar00rootroot00000000000000module Vagrant module Util # Automatically add deprecation notices to commands module CommandDeprecation # @return [String] generated name of command def deprecation_command_name name_parts = self.class.name.split("::") [ name_parts[1].sub('Command', ''), name_parts[3] ].compact.map(&:downcase).join(" ") end def self.included(klass) klass.class_eval do class << self if method_defined?(:synopsis) alias_method :non_deprecated_synopsis, :synopsis def synopsis if !non_deprecated_synopsis.to_s.empty? "#{non_deprecated_synopsis} [DEPRECATED]" else non_deprecated_synopsis end end end end alias_method :non_deprecated_execute, :execute def execute(*args, &block) @env[:ui].warn(I18n.t("vagrant.commands.deprecated", name: deprecation_command_name ) + "\n") non_deprecated_execute(*args, &block) end end end # Mark command deprecation complete and fully disable # the command's functionality module Complete def self.included(klass) klass.include(CommandDeprecation) klass.class_eval do def execute(*_) raise Vagrant::Errors::CommandDeprecated, name: deprecation_command_name end end end end end end end vagrant-2.0.2/lib/vagrant/util/counter.rb000066400000000000000000000010241323370221500203410ustar00rootroot00000000000000require 'thread' module Vagrant module Util # Atomic counter implementation. This is useful for incrementing # a counter which is guaranteed to only be used once in its class. module Counter def get_and_update_counter(name=nil) name ||= :global mutex.synchronize do @__counter ||= Hash.new(1) result = @__counter[name] @__counter[name] += 1 result end end def mutex @__counter_mutex ||= Mutex.new end end end end vagrant-2.0.2/lib/vagrant/util/credential_scrubber.rb000066400000000000000000000034601323370221500226710ustar00rootroot00000000000000module Vagrant module Util # Utility class to remove credential information from strings class CredentialScrubber # String used to replace credential information REPLACEMENT_TEXT = "*****".freeze # Attempt to remove detected credentials from string # # @param [String] string # @return [String] def self.scrub(string) string = url_scrubber(string) end # Detect URLs and remove any embedded credentials # # @param [String] string # @return [String] def self.url_scrubber(string) string.gsub(%r{(ftp|https?)://[^\s]+@[^\s]+}) do |address| uri = URI.parse(address) uri.user = uri.password = REPLACEMENT_TEXT uri.to_s end end # Remove sensitive information from string # # @param [String] string # @return [String] def self.desensitize(string) string = string.dup sensitive_strings.each do |remove| string.gsub!(remove, REPLACEMENT_TEXT) end string end # Register a sensitive string to be scrubbed def self.sensitive(string) sensitive_strings.push(string).uniq! nil end # Deregister a sensitive string and allow output def self.unsensitive(string) sensitive_strings.delete(string) nil end # @return [Array] def self.sensitive_strings if !defined?(@_sensitive_strings) @_sensitive_strings = [] end @_sensitive_strings end # @private # Reset the cached values for scrubber. 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 vagrant-2.0.2/lib/vagrant/util/deep_merge.rb000066400000000000000000000010131323370221500207540ustar00rootroot00000000000000module Vagrant module Util module DeepMerge # This was lifted straight from Rails 4.0.2 as a basic Hash # deep merge. def self.deep_merge(myself, other_hash, &block) myself = myself.dup other_hash.each_pair do |k,v| tv = myself[k] if tv.is_a?(Hash) && v.is_a?(Hash) myself[k] = deep_merge(tv, v, &block) else myself[k] = block && tv ? block.call(k, tv, v) : v end end myself end end end end vagrant-2.0.2/lib/vagrant/util/downloader.rb000066400000000000000000000236731323370221500210360ustar00rootroot00000000000000require "uri" require "log4r" require "digest/md5" require "digest/sha1" require "vagrant/util/busy" require "vagrant/util/platform" require "vagrant/util/subprocess" module Vagrant module Util # This class downloads files using various protocols by subprocessing # to cURL. cURL is a much more capable and complete download tool than # a hand-rolled Ruby library, so we defer to its expertise. class Downloader # Custom user agent provided to cURL so that requests to URL shorteners # are properly tracked. # # Vagrant/1.7.4 (+https://www.vagrantup.com; ruby2.1.0) USER_AGENT = "Vagrant/#{VERSION} (+https://www.vagrantup.com; #{RUBY_ENGINE}#{RUBY_VERSION})".freeze # Supported file checksum CHECKSUM_MAP = { :md5 => Digest::MD5, :sha1 => Digest::SHA1 }.freeze attr_reader :source attr_reader :destination def initialize(source, destination, options=nil) options ||= {} @logger = Log4r::Logger.new("vagrant::util::downloader") @source = source.to_s @destination = destination.to_s begin url = URI.parse(@source) if url.scheme && url.scheme.start_with?("http") && url.user auth = "#{URI.unescape(url.user)}" auth += ":#{URI.unescape(url.password)}" if url.password url.user = nil url.password = nil options[:auth] ||= auth @source = url.to_s end rescue URI::InvalidURIError # Ignore, since its clearly not HTTP end # Get the various optional values @auth = options[:auth] @ca_cert = options[:ca_cert] @ca_path = options[:ca_path] @continue = options[:continue] @headers = options[:headers] @insecure = options[:insecure] @ui = options[:ui] @client_cert = options[:client_cert] @location_trusted = options[:location_trusted] @checksums = { :md5 => options[:md5], :sha1 => options[:sha1] } end # This executes the actual download, downloading the source file # to the destination with the given options used to initialize this # class. # # If this method returns without an exception, the download # succeeded. An exception will be raised if the download failed. def download! # This variable can contain the proc that'll be sent to # the subprocess execute. data_proc = nil extra_subprocess_opts = {} if @ui # If we're outputting progress, then setup the subprocess to # tell us output so we can parse it out. extra_subprocess_opts[:notify] = :stderr progress_data = "" progress_regexp = /(\r(.+?))\r/ # Setup the proc that'll receive the real-time data from # the downloader. data_proc = Proc.new do |type, data| # Type will always be "stderr" because that is the only # type of data we're subscribed for notifications. # Accumulate progress_data progress_data << data while true # If we have a full amount of column data (two "\r") then # we report new progress reports. Otherwise, just keep # accumulating. match = progress_regexp.match(progress_data) break if !match data = match[2] progress_data.gsub!(match[1], "") # Ignore the first \r and split by whitespace to grab the columns columns = data.strip.split(/\s+/) # COLUMN DATA: # # 0 - % total # 1 - Total size # 2 - % received # 3 - Received size # 4 - % transferred # 5 - Transferred size # 6 - Average download speed # 7 - Average upload speed # 9 - Total time # 9 - Time spent # 10 - Time left # 11 - Current speed output = "Progress: #{columns[0]}% (Rate: #{columns[11]}/s, Estimated time remaining: #{columns[10]})" @ui.clear_line @ui.detail(output, new_line: false) end end end @logger.info("Downloader starting download: ") @logger.info(" -- Source: #{@source}") @logger.info(" -- Destination: #{@destination}") retried = false begin # Get the command line args and the subprocess opts based # on our downloader settings. options, subprocess_options = self.options options += ["--output", @destination] options << @source # Merge in any extra options we set subprocess_options.merge!(extra_subprocess_opts) # Go! execute_curl(options, subprocess_options, &data_proc) rescue Errors::DownloaderError => e # If we already retried, raise it. raise if retried @logger.error("Exit code: #{e.extra_data[:code]}") # If its any error other than 33, it is an error. raise if e.extra_data[:code].to_i != 33 # Exit code 33 means that the server doesn't support ranges. # In this case, try again without resume. @logger.error("Error is server doesn't support byte ranges. Retrying from scratch.") @continue = false retried = true retry ensure # If we're outputting to the UI, clear the output to # avoid lingering progress meters. if @ui @ui.clear_line # Windows doesn't clear properly for some reason, so we just # output one more newline. @ui.detail("") if Platform.windows? end end validate_download!(@source, @destination, @checksums) # Everything succeeded true end # Does a HEAD request of the URL and returns the output. def head options, subprocess_options = self.options options.unshift("-I") options << @source @logger.info("HEAD: #{@source}") result = execute_curl(options, subprocess_options) result.stdout end protected # Apply any checksum validations based on provided # options content # # @param source [String] Source of file # @param path [String, Pathname] local file path # @param checksums [Hash] User provided options # @option checksums [String] :md5 Compare MD5 checksum # @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 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 # Create the callback that is called if we are interrupted interrupted = false int_callback = Proc.new do @logger.info("Downloader interrupted!") interrupted = true end # Execute! result = Busy.busy(int_callback) do Subprocess.execute("curl", *options, &data_proc) end # If the download was interrupted, then raise a specific error raise Errors::DownloaderInterrupted if interrupted # If it didn't exit successfully, we need to parse the data and # show an error message. if result.exit_code != 0 @logger.warn("Downloader exit code: #{result.exit_code}") parts = result.stderr.split(/\n*curl:\s+\(\d+\)\s*/, 2) parts[1] ||= "" raise Errors::DownloaderError, code: result.exit_code, message: parts[1].chomp end result end # Returns the varoius cURL and subprocess options. # # @return [Array] def options # Build the list of parameters to execute with cURL options = [ "-q", "--fail", "--location", "--max-redirs", "10", "--user-agent", USER_AGENT, ] options += ["--cacert", @ca_cert] if @ca_cert options += ["--capath", @ca_path] if @ca_path options += ["--continue-at", "-"] if @continue options << "--insecure" if @insecure options << "--cert" << @client_cert if @client_cert options << "-u" << @auth if @auth options << "--location-trusted" if @location_trusted if @headers Array(@headers).each do |header| options << "-H" << header end end # Specify some options for the subprocess subprocess_options = {} # If we're in Vagrant, then we use the packaged CA bundle if Vagrant.in_installer? subprocess_options[:env] ||= {} subprocess_options[:env]["CURL_CA_BUNDLE"] = ENV["CURL_CA_BUNDLE"] end return [options, subprocess_options] end end end end vagrant-2.0.2/lib/vagrant/util/env.rb000066400000000000000000000032111323370221500174520ustar00rootroot00000000000000module Vagrant module Util class Env def self.with_original_env original_env = ENV.to_hash if defined?(::Bundler) && defined?(::Bundler::ORIGINAL_ENV) ENV.replace(::Bundler::ORIGINAL_ENV) end ENV.update(Vagrant.original_env) yield ensure ENV.replace(original_env.to_hash) end # Execute the given command, removing any Ruby-specific environment # variables. This is an "enhanced" version of `Bundler.with_clean_env`, # which only removes Bundler-specific values. We need to remove all # values, specifically: # # - _ORIGINAL_GEM_PATH # - GEM_PATH # - GEM_HOME # - GEM_ROOT # - BUNDLE_BIN_PATH # - BUNDLE_GEMFILE # - RUBYLIB # - RUBYOPT # - RUBY_ENGINE # - RUBY_ROOT # - RUBY_VERSION # # This will escape Vagrant's environment entirely, which is required if # calling an executable that lives in another Ruby environment. The # original environment restored at the end of this call. # # @param [Proc] block # the block to execute with the cleaned environment def self.with_clean_env with_original_env do if ENV["BUNDLE_ORIG_MANPATH"] ENV["MANPATH"] = ENV["BUNDLE_ORIG_MANPATH"] end ENV.delete_if { |k,_| k[0,7] == "BUNDLE_" } if ENV.has_key? "RUBYOPT" ENV["RUBYOPT"] = ENV["RUBYOPT"].sub("-rbundler/setup", "") ENV["RUBYOPT"] = ENV["RUBYOPT"].sub("-I#{File.expand_path('..', __FILE__)}", "") end yield end end end end end vagrant-2.0.2/lib/vagrant/util/file_checksum.rb000066400000000000000000000023321323370221500214660ustar00rootroot00000000000000# This is an "interface" that should be implemented by any digest class # passed into FileChecksum. Note that this isn't strictly enforced at # the moment, and this class isn't directly used. It is merely here for # documentation of structure of the class. class DigestClass def update(string); end def hexdigest; end end class FileChecksum BUFFER_SIZE = 1024 * 8 # 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 end # This calculates the checksum of the file and returns it as a # string. # # @return [String] def checksum digest = @digest_klass.new buf = '' File.open(@path, "rb") do |f| while !f.eof begin f.readpartial(BUFFER_SIZE, buf) digest.update(buf) rescue EOFError # Although we check for EOF earlier, this seems to happen # sometimes anyways [GH-2716]. break end end end return digest.hexdigest end end vagrant-2.0.2/lib/vagrant/util/file_mode.rb000066400000000000000000000004121323370221500206050ustar00rootroot00000000000000module Vagrant module Util class FileMode # This returns the file permissions as a string from # an octal number. def self.from_octal(octal) perms = sprintf("%o", octal) perms.reverse[0..2].reverse end end end end vagrant-2.0.2/lib/vagrant/util/guest_inspection.rb000066400000000000000000000027401323370221500222520ustar00rootroot00000000000000module Vagrant module Util # Helper methods for inspecting guests to determine if specific services # or applications are installed and in use module GuestInspection # Linux specific inspection helpers module Linux ## systemd helpers # systemd is in use # # @return [Boolean] def systemd?(comm) comm.test("systemctl | grep '^-\.mount'") end # systemd-networkd.service is in use # # @return [Boolean] def systemd_networkd?(comm) comm.test("sudo systemctl status systemd-networkd.service") end # systemd hostname set is via hostnamectl # # @return [Boolean] def hostnamectl?(comm) comm.test("hostnamectl") end ## netplan helpers # netplan is installed # # @return [Boolean] def netplan?(comm) comm.test("netplan -h") end ## nmcli helpers # nmcli is installed # # @return [Boolean] def nmcli?(comm) comm.test("nmcli") end # NetworkManager currently controls device # # @param comm [Communicator] # @param device_name [String] # @return [Boolean] def nm_controlled?(comm, device_name) comm.test("nmcli d show #{device_name}") && !comm.test("nmcli d show #{device_name} | grep unmanaged") end end end end end vagrant-2.0.2/lib/vagrant/util/hash_with_indifferent_access.rb000066400000000000000000000026431323370221500245460ustar00rootroot00000000000000module Vagrant module Util # A hash with indifferent access. Mostly taken from Thor/Rails (thanks). # Normally I'm not a fan of using an indifferent access hash since Symbols # are basically memory leaks in Ruby, but since Vagrant is typically a quick # one-off binary run and it doesn't use too many hash keys where this is # used, the effect should be minimal. # # hash[:foo] #=> 'bar' # hash['foo'] #=> 'bar' # class HashWithIndifferentAccess < ::Hash def initialize(hash={}, &block) super(&block) hash.each do |key, value| self[convert_key(key)] = value end end def [](key) super(convert_key(key)) end def []=(key, value) super(convert_key(key), value) end def delete(key) super(convert_key(key)) end def values_at(*indices) indices.collect { |key| self[convert_key(key)] } end def merge(other) dup.merge!(other) end def merge!(other) other.each do |key, value| self[convert_key(key)] = value end self end def key?(key) super(convert_key(key)) end alias_method :include?, :key? alias_method :has_key?, :key? alias_method :member?, :key? protected def convert_key(key) key.is_a?(Symbol) ? key.to_s : key end end end end vagrant-2.0.2/lib/vagrant/util/io.rb000066400000000000000000000046101323370221500172750ustar00rootroot00000000000000require "vagrant/util/platform" module Vagrant module Util class IO # The chunk size for reading from subprocess IO. READ_CHUNK_SIZE = 4096 # Reads data from an IO object while it can, returning the data it reads. # When it encounters a case when it can't read anymore, it returns the # data. # # @return [String] def self.read_until_block(io) data = "" while true begin if Platform.windows? # Windows doesn't support non-blocking reads on # file descriptors or pipes so we have to get # a bit more creative. # Check if data is actually ready on this IO device. # We have to do this since `readpartial` will actually block # until data is available, which can cause blocking forever # in some cases. results = ::IO.select([io], nil, nil, 1.0) break if !results || results[0].empty? # Read! data << io.readpartial(READ_CHUNK_SIZE).encode("UTF-8", Encoding.default_external) else # Do a simple non-blocking read on the IO object data << io.read_nonblock(READ_CHUNK_SIZE) end rescue Exception => e # The catch-all rescue here is to support multiple Ruby versions, # since we use some Ruby 1.9 specific exceptions. breakable = false if e.is_a?(EOFError) # An `EOFError` means this IO object is done! breakable = true elsif defined?(::IO::WaitReadable) && e.is_a?(::IO::WaitReadable) # IO::WaitReadable is only available on Ruby 1.9+ # An IO::WaitReadable means there may be more IO but this # IO object is not ready to be read from yet. No problem, # we read as much as we can, so we break. breakable = true elsif e.is_a?(Errno::EAGAIN) # Otherwise, we just look for the EAGAIN error which should be # all that IO::WaitReadable does in Ruby 1.9. breakable = true end # Break out if we're supposed to. Otherwise re-raise the error # because it is a real problem. break if breakable raise end end data end end end end vagrant-2.0.2/lib/vagrant/util/is_port_open.rb000066400000000000000000000027041323370221500213700ustar00rootroot00000000000000require "socket" require "timeout" module Vagrant module Util # Contains the method {#is_port_open?} to check if a port is open # (listening) or closed (not in use). This method isn't completely # fool-proof, but it works enough of the time to be useful. module IsPortOpen # Checks if a port is open (listening) on a given host and port. # # @param [String] host Hostname or IP address. # @param [Integer] port Port to check. # @return [Boolean] `true` if the port is open (listening), `false` # otherwise. def is_port_open?(host, port) # We wrap this in a timeout because once in awhile the TCPSocket # _will_ hang, but this signals that the port is closed. Timeout.timeout(1) do # Attempt to make a connection s = TCPSocket.new(host, port) # A connection was made! Properly clean up the socket, not caring # at all if any exception is raised, because we already know the # result. s.close rescue nil # The port is open if we reached this point, since we were able # to connect. return true end rescue Timeout::Error, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, \ Errno::ENETUNREACH, Errno::EACCES, Errno::ENOTCONN, \ Errno::EADDRNOTAVAIL # Any of the above exceptions signal that the port is closed. return false end end end end vagrant-2.0.2/lib/vagrant/util/keypair.rb000066400000000000000000000032531323370221500203340ustar00rootroot00000000000000require "base64" require "openssl" require "vagrant/util/retryable" module Vagrant module Util class Keypair extend Retryable # Creates an SSH keypair and returns it. # # @param [String] password Password for the key, or nil for no password. # @return [Array] PEM-encoded public and private key, # respectively. The final element is the OpenSSH encoded public # key. def self.create(password=nil) # This sometimes fails with RSAError. It is inconsistent and strangely # sleeps seem to fix it. We just retry this a few times. See GH-5056 rsa_key = nil retryable(on: OpenSSL::PKey::RSAError, sleep: 2, tries: 5) do rsa_key = OpenSSL::PKey::RSA.new(2048) end public_key = rsa_key.public_key private_key = rsa_key.to_pem if password cipher = OpenSSL::Cipher.new('des3') private_key = rsa_key.to_pem(cipher, password) end # Generate the binary necessary for the OpenSSH public key. binary = [7].pack("N") binary += "ssh-rsa" ["e", "n"].each do |m| val = public_key.send(m) data = val.to_s(2) first_byte = data[0,1].unpack("c").first if val < 0 data[0] = [0x80 & first_byte].pack("c") elsif first_byte < 0 data = 0.chr + data end binary += [data.length].pack("N") + data end openssh_key = "ssh-rsa #{Base64.encode64(binary).gsub("\n", "")} vagrant" public_key = public_key.to_pem return [public_key, private_key, openssh_key] end end end end vagrant-2.0.2/lib/vagrant/util/line_ending_helpers.rb000066400000000000000000000005151323370221500226630ustar00rootroot00000000000000module Vagrant module Util module LineEndingHelpers # Converts line endings to unix-style line endings in the # given string. # # @param [String] string Original string # @return [String] The fixed string def dos_to_unix(string) string.gsub("\r\n", "\n") end end end end vagrant-2.0.2/lib/vagrant/util/logging_formatter.rb000066400000000000000000000012651323370221500224020ustar00rootroot00000000000000require "vagrant/util/credential_scrubber" require "log4r/formatter/formatter" module Vagrant module Util # Wrapper for logging formatting to provide # information scrubbing prior to being written # to output target class LoggingFormatter < Log4r::BasicFormatter # @return [Log4r::PatternFormatter] attr_reader :formatter # Creates a new formatter wrapper instance. # # @param [Log4r::Formatter] def initialize(formatter) @formatter = formatter end # Format event and scrub output def format(event) msg = formatter.format(event) CredentialScrubber.desensitize(msg) end end end end vagrant-2.0.2/lib/vagrant/util/network_ip.rb000066400000000000000000000020041323370221500210420ustar00rootroot00000000000000require "ipaddr" module Vagrant module Util module NetworkIP # Returns the network address of the given IP and subnet. # # If the IP address is an IPv6 address, subnet should be a prefix # length such as "64". # # @return [String] def network_address(ip, subnet) # If this is an IPv6 address, then just mask it if subnet.to_s =~ /^\d+$/ ip = IPAddr.new(ip) return ip.mask(subnet.to_i).to_s end ip = ip_parts(ip) netmask = ip_parts(subnet) # Bitwise-AND each octet to get the network address # in octets and join each part with a period to get # the resulting network address. ip.map { |part| part & netmask.shift }.join(".") end protected # Splits an IP into the four octets and returns each as an # integer in an array. # # @return [Array] def ip_parts(ip) ip.split(".").map { |i| i.to_i } end end end end vagrant-2.0.2/lib/vagrant/util/platform.rb000066400000000000000000000416601323370221500205200ustar00rootroot00000000000000require "rbconfig" require "shellwords" require "tmpdir" require "vagrant/util/subprocess" require "vagrant/util/powershell" require "vagrant/util/which" module Vagrant module Util # This class just contains some platform checking code. class Platform class << self def cygwin? if !defined?(@_cygwin) @_cygwin = ENV["VAGRANT_DETECTED_OS"].to_s.downcase.include?("cygwin") || platform.include?("cygwin") || ENV["OSTYPE"].to_s.downcase.include?("cygwin") end @_cygwin end def msys? if !defined?(@_msys) @_msys = ENV["VAGRANT_DETECTED_OS"].to_s.downcase.include?("msys") || platform.include?("msys") || ENV["OSTYPE"].to_s.downcase.include?("msys") end @_msys end def wsl? if !defined?(@_wsl) @_wsl = false SilenceWarnings.silence! do # Use PATH values to check for `/mnt/c` path indicative of WSL if ENV.fetch("PATH", "").downcase.include?("/mnt/c") # Validate WSL via uname output uname = Subprocess.execute("uname", "-r") if uname.exit_code == 0 && uname.stdout.downcase.include?("microsoft") @_wsl = true end end end end @_wsl end [:darwin, :bsd, :freebsd, :linux, :solaris].each do |type| define_method("#{type}?") do platform.include?(type.to_s) end end def windows? return @_windows if defined?(@_windows) @_windows = %w[mingw mswin].any? { |t| platform.include?(t) } return @_windows end # Checks if the user running Vagrant on Windows has administrative # privileges. # # From: https://support.microsoft.com/en-us/kb/243330 # SID: S-1-5-19 # # @return [Boolean] def windows_admin? return @_windows_admin if defined?(@_windows_admin) @_windows_admin = -> { ps_cmd = '(new-object System.Security.Principal.WindowsPrincipal([System.Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)' output = Vagrant::Util::PowerShell.execute_cmd(ps_cmd) return output == 'True' }.call return @_windows_admin end # Checks if the user running Vagrant on Windows is a member of the # "Hyper-V Administrators" group. # # From: https://support.microsoft.com/en-us/kb/243330 # SID: S-1-5-32-578 # Name: BUILTIN\Hyper-V Administrators # # @return [Boolean] def windows_hyperv_admin? return @_windows_hyperv_admin if defined?(@_windows_hyperv_admin) @_windows_hyperv_admin = -> { ps_cmd = "[System.Security.Principal.WindowsIdentity]::GetCurrent().Groups | ForEach-Object { if ($_.Value -eq 'S-1-5-32-578'){ Write-Host 'true'; break }}" output = Vagrant::Util::PowerShell.execute_cmd(ps_cmd) return output == 'true' }.call return @_windows_hyperv_admin end # This takes any path and converts it from a Windows path to a # Cygwin style path. # # @param [String] path # @return [String] def cygwin_path(path) begin cygpath = Vagrant::Util::Which.which("cygpath") if cygpath.nil? # If Which can't find it, just attempt to invoke it directly cygpath = "cygpath" else cygpath.gsub!("/", '\\') end process = Subprocess.execute( cygpath, "-u", "-a", path.to_s) return process.stdout.chomp rescue Errors::CommandUnavailableWindows => e # Sometimes cygpath isn't available (msys). Instead, do what we # can with bash tricks. process = Subprocess.execute( "bash", "--noprofile", "--norc", "-c", "cd #{Shellwords.escape(path)} && pwd") return process.stdout.chomp end end # This takes any path and converts it from a Windows path to a # msys style path. # # @param [String] path # @return [String] def msys_path(path) begin # We have to revert to the old env # path here, otherwise it looks like # msys2 ends up using the wrong cygpath # binary and ends up with a `/cygdrive` # when it doesn't exist in msys2 original_path_env = ENV['PATH'] ENV['PATH'] = ENV['VAGRANT_OLD_ENV_PATH'] cygwin_path(path) ensure ENV['PATH'] = original_path_env end end # This takes any path and converts it to a full-length Windows # path on Windows machines in Cygwin. # # @return [String] def cygwin_windows_path(path) return path if !cygwin? # Replace all "\" with "/", otherwise cygpath doesn't work. path = path.gsub("\\", "/") # Call out to cygpath and gather the result process = Subprocess.execute("cygpath", "-w", "-l", "-a", path.to_s) return process.stdout.chomp end # This checks if the filesystem is case sensitive. This is not a # 100% correct check, since it is possible that the temporary # directory runs a different filesystem than the root directory. # However, this works in many cases. def fs_case_sensitive? return @_fs_case_sensitive if defined?(@_fs_case_sensitive) @_fs_case_sensitive = Dir.mktmpdir("vagrant-fs-case-sensitive") do |dir| tmp_file = File.join(dir, "FILE") File.open(tmp_file, "w") do |f| f.write("foo") end # The filesystem is case sensitive if the lowercased version # of the filename is NOT reported as existing. !File.file?(File.join(dir, "file")) end return @_fs_case_sensitive end # This expands the path and ensures proper casing of each part # of the path. def fs_real_path(path, **opts) path = Pathname.new(File.expand_path(path)) if path.exist? && !fs_case_sensitive? # If the path contains a Windows short path, then we attempt to # expand. The require below is embedded here since it requires # windows to work. if windows? && path.to_s =~ /~\d(\/|\\)/ require_relative "windows_path" path = Pathname.new(WindowsPath.longname(path.to_s)) end # Build up all the parts of the path original = [] while !path.root? original.unshift(path.basename.to_s) path = path.parent end # Traverse each part and join it into the resulting path original.each do |single| Dir.entries(path).each do |entry| if entry.downcase == single.encode('filesystem').downcase path = path.join(entry) end end end end if windows? # Fix the drive letter to be uppercase. path = path.to_s if path[1] == ":" path[0] = path[0].upcase end path = Pathname.new(path) end path end # Converts a given path to UNC format by adding a prefix and converting slashes. # @param [String] path Path to convert to UNC for Windows # @return [String] def windows_unc_path(path) path = path.gsub("/", "\\") # Convert to UNC path if path =~ /^[a-zA-Z]:\\?$/ # If the path is just a drive letter, then return that as-is path + "\\" elsif path.start_with?("\\\\") # If the path already starts with `\\` assume UNC and return as-is path else "\\\\?\\" + path.gsub("/", "\\") end end # Returns a boolean noting whether the terminal supports color. # output. def terminal_supports_colors? return @_terminal_supports_colors if defined?(@_terminal_supports_colors) @_terminal_supports_colors = -> { if windows? return true if ENV.key?("ANSICON") return true if cygwin? return true if ENV["TERM"] == "cygwin" return false end return true }.call return @_terminal_supports_colors end def platform return @_platform if defined?(@_platform) @_platform = RbConfig::CONFIG["host_os"].downcase return @_platform end # Determine if given path is within the WSL rootfs. Returns # true if within the subsystem, or false if outside the subsystem. # # @param [String] path Path to check # @return [Boolean] path is within subsystem def wsl_path?(path) wsl? && !path.to_s.downcase.start_with?("/mnt/") end # Convert a WSL path to the local Windows path. This is useful # for conversion when calling out to Windows executables from # the WSL # # @param [String, Pathname] path Path to convert # @return [String] def wsl_to_windows_path(path) if wsl? && wsl_windows_access? if wsl_path?(path) parts = path.split("/") parts.delete_if(&:empty?) [wsl_windows_appdata_local, "lxss", *parts].join("\\") else path = path.to_s.sub("/mnt/", "") parts = path.split("/") parts.first << ":" path = parts.join("\\") path end else path end end # Takes a windows path and formats it to the # 'unix' style (i.e. `/cygdrive/c` or `/c/`) # # @param [Pathname, String] path Path to convert # @param [Hash] hash of arguments # @return [String] def format_windows_path(path, *args) path = cygwin_path(path) if cygwin? path = msys_path(path) if msys? path = wsl_to_windows_path(path) if wsl? if windows? || wsl? path = windows_unc_path(path) if !args.include?(:disable_unc) end path end # Automatically convert a given path to a Windows path. Will only # be applied if running on a Windows host. If running on Windows # host within the WSL, the actual Windows path will be returned. # # @param [Pathname, String] path Path to convert # @return [String] def windows_path(path) path = cygwin_windows_path(path) path = wsl_to_windows_path(path) if windows? || wsl? path = windows_unc_path(path) end path end # Allow Vagrant to access Vagrant managed machines outside the # Windows Subsystem for Linux # # @return [Boolean] def wsl_windows_access? if !defined?(@_wsl_windows_access) @_wsl_windows_access = wsl? && ENV["VAGRANT_WSL_ENABLE_WINDOWS_ACCESS"] end @_wsl_windows_access end # The allowed windows system path Vagrant can manage from the Windows # Subsystem for Linux # # @return [Pathname] def wsl_windows_accessible_path if !defined?(@_wsl_windows_accessible_path) access_path = ENV["VAGRANT_WSL_WINDOWS_ACCESS_USER_HOME_PATH"] if access_path.to_s.empty? access_path = wsl_windows_home.gsub("\\", "/").sub(":", "") access_path[0] = access_path[0].downcase access_path = "/mnt/#{access_path}" end @_wsl_windows_accessible_path = Pathname.new(access_path) end @_wsl_windows_accessible_path end # Checks given path to determine if Vagrant is allowed to bypass checks # # @param [String] path Path to check # @return [Boolean] Vagrant is allowed to bypass checks def wsl_windows_access_bypass?(path) wsl? && wsl_windows_access? && path.to_s.start_with?(wsl_windows_accessible_path.to_s) end # If running within the Windows Subsystem for Linux, this will provide # simple setup to allow sharing of the user's VAGRANT_HOME directory # within the subsystem # # @param [Environment] env # @param [Logger] logger Optional logger to display information def wsl_init(env, logger=nil) if wsl? if ENV["VAGRANT_WSL_ENABLE_WINDOWS_ACCESS"] wsl_validate_matching_vagrant_versions! shared_user = ENV["VAGRANT_WSL_WINDOWS_ACCESS_USER"] if shared_user.to_s.empty? shared_user = wsl_windows_username end if logger logger.warn("Windows Subsystem for Linux detected. Allowing access to user: #{shared_user}") logger.warn("Vagrant will be allowed to control Vagrant managed machines within the user's home path.") end if ENV["VAGRANT_HOME"] || ENV["VAGRANT_WSL_DISABLE_VAGRANT_HOME"] logger.warn("VAGRANT_HOME environment variable already set. Not overriding!") if logger else home_path = wsl_windows_accessible_path.to_s ENV["VAGRANT_HOME"] = File.join(home_path, ".vagrant.d") if logger logger.info("Overriding VAGRANT_HOME environment variable to configured windows user. (#{ENV["VAGRANT_HOME"]})") end true end else if env.local_data_path.to_s.start_with?("/mnt/") raise Vagrant::Errors::WSLVagrantAccessError end end end end # Fetch the Windows username currently in use # # @return [String, Nil] def wsl_windows_username if !@_wsl_windows_username result = Util::Subprocess.execute("cmd.exe", "/c", "echo %USERNAME%") if result.exit_code == 0 @_wsl_windows_username = result.stdout.strip end end @_wsl_windows_username end # Fetch the Windows user home directory # # @return [String, Nil] def wsl_windows_home if !@_wsl_windows_home result = Util::Subprocess.execute("cmd.exe", "/c" "echo %USERPROFILE%") if result.exit_code == 0 @_wsl_windows_home = result.stdout.gsub("\"", "").strip end end @_wsl_windows_home end # Fetch the Windows user local app data directory # # @return [String, Nil] def wsl_windows_appdata_local if !@_wsl_windows_appdata_local result = Util::Subprocess.execute("cmd.exe", "/c", "echo %LOCALAPPDATA%") if result.exit_code == 0 @_wsl_windows_appdata_local = result.stdout.gsub("\"", "").strip end end @_wsl_windows_appdata_local end # Confirm Vagrant versions installed within the WSL and the Windows system # are the same. Raise error if they do not match. def wsl_validate_matching_vagrant_versions! valid = false if Util::Which.which("vagrant.exe") result = Util::Subprocess.execute("vagrant.exe", "version") if result.exit_code == 0 windows_version = result.stdout.match(/Installed Version: (?[\w.-]+)/) if windows_version windows_version = windows_version[:version].strip valid = windows_version == Vagrant::VERSION end end if !valid raise Vagrant::Errors::WSLVagrantVersionMismatch, wsl_version: Vagrant::VERSION, windows_version: windows_version || "unknown" end end end # systemd is in use def systemd? if !defined?(@_systemd) if !windows? result = Vagrant::Util::Subprocess.execute("ps", "-o", "comm=", "1") @_systemd = result.stdout.chomp == "systemd" else @_systemd = false end end @_systemd end # @private # Reset the cached values for platform. This is not considered a public # API and should only be used for testing. def reset! instance_variables.each(&method(:remove_instance_variable)) end end end end end vagrant-2.0.2/lib/vagrant/util/powershell.rb000066400000000000000000000124311323370221500210520ustar00rootroot00000000000000require "tmpdir" require_relative "subprocess" require_relative "which" module Vagrant module Util # Executes PowerShell scripts. # # This is primarily a convenience wrapper around Subprocess that # properly sets powershell flags for you. class PowerShell # NOTE: Version checks are only on Major MINIMUM_REQUIRED_VERSION = 3 # @return [Boolean] powershell executable available on PATH def self.available? if !defined?(@_powershell_available) @_powershell_available = !!Which.which("powershell") end @_powershell_available end # Execute a powershell script. # # @param [String] path Path to the PowerShell script to execute. # @return [Subprocess::Result] def self.execute(path, *args, **opts, &block) validate_install! if opts.delete(:sudo) || opts.delete(:runas) powerup_command(path, args, opts) else command = [ "powershell", "-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "&('#{path}')", args ].flatten # Append on the options hash since Subprocess doesn't use # Ruby 2.0 style options yet. command << opts Subprocess.execute(*command, &block) end end # Execute a powershell command. # # @param [String] command PowerShell command to execute. # @return [nil, String] Returns nil if exit code is non-zero. # Returns stdout string if exit code is zero. def self.execute_cmd(command) validate_install! c = [ "powershell", "-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", command ].flatten.compact r = Subprocess.execute(*c) return nil if r.exit_code != 0 return r.stdout.chomp end # Returns the version of PowerShell that is installed. # # @return [String] def self.version if !defined?(@_powershell_version) command = [ "powershell", "-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", "Write-Output $PSVersionTable.PSVersion.Major" ].flatten r = Subprocess.execute(*command) @_powershell_version = r.exit_code != 0 ? nil : r.stdout.chomp end @_powershell_version end # Validates that powershell is installed, available, and # at or above minimum required version # # @return [Boolean] # @raises [] def self.validate_install! if !defined?(@_powershell_validation) raise Errors::PowerShellNotFound if !available? if version.to_i < MINIMUM_REQUIRED_VERSION raise Errors::PowerShellInvalidVersion, minimum_version: MINIMUM_REQUIRED_VERSION, installed_version: version ? version : "N/A" end @_powershell_validation = true end @_powershell_validation end # Powerup the given command to perform privileged operations. # # @param [String] path # @param [Array] args # @return [Array] def self.powerup_command(path, args, opts) Dir.mktmpdir("vagrant") do |dpath| all_args = ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", path] + args arg_list = "@('" + all_args.join("', '") + "')" stdout = File.join(dpath, "stdout.txt") stderr = File.join(dpath, "stderr.txt") exitcode = File.join(dpath, "exitcode.txt") script = "$sp = Start-Process -FilePath powershell -ArgumentList #{arg_list} " \ "-PassThru -Wait -RedirectStandardOutput '#{stdout}' -RedirectStandardError '#{stderr}' -WindowStyle Hidden; " \ "if($sp){ Set-Content -Path '#{exitcode}' -Value $sp.ExitCode;exit $sp.ExitCode; }else{ exit 1 }" # escape quotes so we can nest our script within a start-process script.gsub!("'", "''") cmd = [ "powershell", "-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", "$p = Start-Process -FilePath powershell -ArgumentList " \ "@('-NoLogo', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', '#{script}') " \ "-PassThru -Wait -WindowStyle Hidden -Verb RunAs; if($p){ exit $p.ExitCode; }else{ exit 1 }" ] result = Subprocess.execute(*cmd.push(opts)) if File.exist?(stdout) r_stdout = File.read(stdout) else r_stdout = result.stdout end if File.exist?(stderr) r_stderr = File.read(stderr) else r_stderr = result.stderr end code = 1 if File.exist?(exitcode) code_txt = File.read(exitcode).strip if code_txt.match(/^\d+$/) code = code_txt.to_i end end Subprocess::Result.new(code, r_stdout, r_stderr) end end end end end vagrant-2.0.2/lib/vagrant/util/presence.rb000066400000000000000000000021511323370221500204700ustar00rootroot00000000000000module Vagrant module Util module Presence extend self # Determines if the given object is "present". A String is considered # present if the stripped contents are not empty. An Array/Hash is # considered present if they have a length of more than 1. "true" is # always present and `false` and `nil` are always not present. Any other # object is considered to be present. # # @return [true, false] def present?(obj) case obj when String !obj.strip.empty? when Symbol !obj.to_s.strip.empty? when Array !obj.compact.empty? when Hash !obj.empty? when TrueClass, FalseClass obj when NilClass false when Object true end end # Returns the presence of the object. If the object is {present?}, it is # returned. Otherwise `false` is returned. # # @return [Object, false] def presence(obj) if present?(obj) obj else false end end end end end vagrant-2.0.2/lib/vagrant/util/retryable.rb000066400000000000000000000016221323370221500206570ustar00rootroot00000000000000require "log4r" module Vagrant module Util module Retryable # Retries a given block a specified number of times in the # event the specified exception is raised. If the retries # run out, the final exception is raised. # # This code is adapted slightly from the following blog post: # http://blog.codefront.net/2008/01/14/retrying-code-blocks-in-ruby-on-exceptions-whatever/ def retryable(opts=nil) logger = nil opts = { tries: 1, on: Exception }.merge(opts || {}) begin return yield rescue *opts[:on] => e if (opts[:tries] -= 1) > 0 logger = Log4r::Logger.new("vagrant::util::retryable") logger.info("Retryable exception raised: #{e.inspect}") sleep opts[:sleep].to_f if opts[:sleep] retry end raise end end end end end vagrant-2.0.2/lib/vagrant/util/safe_chdir.rb000066400000000000000000000014651323370221500207620ustar00rootroot00000000000000require 'thread' module Vagrant module Util class SafeChdir @@chdir_lock = Mutex.new # Safely changes directory of this process by putting a lock around # it so that it is thread safe. This will yield a block and when the # block exits it changes back to the original directory. # # @param [String] dir Dir to change to temporarily def self.safe_chdir(dir) lock = @@chdir_lock begin @@chdir_lock.synchronize {} rescue ThreadError # If we already hold the lock, just create a new lock so we # definitely don't block and don't get an error. lock = Mutex.new end lock.synchronize do Dir.chdir(dir) do return yield end end end end end end vagrant-2.0.2/lib/vagrant/util/safe_env.rb000066400000000000000000000005341323370221500204550ustar00rootroot00000000000000module Vagrant module Util class SafeEnv # This yields an environment hash to change and catches any issues # while changing the environment variables and raises a helpful error # to end users. def self.change_env yield ENV rescue Errno::EINVAL raise Errors::EnvInval end end end end vagrant-2.0.2/lib/vagrant/util/safe_exec.rb000066400000000000000000000056041323370221500206140ustar00rootroot00000000000000module Vagrant module Util # This module provies a `safe_exec` method which is a drop-in # replacement for `Kernel.exec` which addresses a specific issue # which manifests on OS X 10.5 (GH-51) and perhaps other operating systems. # This issue causes `exec` to fail if there is more than one system # thread. In that case, `safe_exec` automatically falls back to # forking. class SafeExec @@logger = Log4r::Logger.new("vagrant::util::safe_exec") def self.exec(command, *args) # Create a list of things to rescue from. Since this is OS # specific, we need to do some defined? checks here to make # sure they exist. rescue_from = [] rescue_from << Errno::EOPNOTSUPP if defined?(Errno::EOPNOTSUPP) rescue_from << Errno::E045 if defined?(Errno::E045) rescue_from << SystemCallError fork_instead = false begin if fork_instead if Vagrant::Util::Platform.windows? @@logger.debug("Using subprocess because windows platform") args = args.dup << {notify: [:stdout, :stderr]} result = Vagrant::Util::Subprocess.execute(command, *args) do |type, data| case type when :stdout @@logger.info(data, new_line: false) when :stderr @@logger.info(data, new_line: false) end end Kernel.exit(result.exit_code) else pid = fork Kernel.exec(command, *args) Process.wait(pid) end else if Vagrant::Util::Platform.windows? # Re-generate strings to ensure common encoding @@logger.debug("Converting command and arguments to common UTF-8 encoding for exec.") @@logger.debug("Command: `#{command.inspect}` Args: `#{args.inspect}`") begin command = "#{command}".encode("UTF-8") rescue Encoding::UndefinedConversionError => e @@logger.warn("Failed to convert command - #{e.class}: #{e} (`#{command}`)") end args = args.map do |arg| begin "#{arg}".encode("UTF-8") rescue Encoding::UndefinedConversionError => e @@logger.warn("Failed to convert command argument - #{e.class}: #{e} (`#{arg}`)") arg end end @@logger.debug("Converted - Command: `#{command.inspect}` Args: `#{args.inspect}`") end Kernel.exec(command, *args) end rescue *rescue_from # We retried already, raise the issue and be done raise if fork_instead # The error manifested itself, retry with a fork. fork_instead = true retry end end end end end vagrant-2.0.2/lib/vagrant/util/safe_puts.rb000066400000000000000000000015711323370221500206620ustar00rootroot00000000000000module Vagrant module Util # This module provides a `safe_puts` method which outputs to # the given IO object, and rescues any broken pipe errors and # ignores them. This is useful in cases where you're outputting # to stdout, for example, and the stdout is closed, but you want to # keep running. module SafePuts # Uses `puts` on the given IO object and safely ignores any # Errno::EPIPE. # # @param [String] message Message to output. # @param [Hash] opts Options hash. def safe_puts(message=nil, opts=nil) message ||= "" opts = { io: $stdout, printer: :puts }.merge(opts || {}) begin opts[:io].send(opts[:printer], message) rescue Errno::EPIPE # This is what makes this a `safe` puts. return end end end end end vagrant-2.0.2/lib/vagrant/util/scoped_hash_override.rb000066400000000000000000000021651323370221500230500ustar00rootroot00000000000000module Vagrant module Util # This allows for hash options to be overridden by a scope key # prefix. An example speaks best here. Imagine the following hash: # # original = { # id: "foo", # mitchellh__id: "bar", # mitchellh__other: "foo" # } # # scoped = scoped_hash_override(original, "mitchellh") # # scoped == { # id: "bar", # other: "foo" # } # module ScopedHashOverride def scoped_hash_override(original, scope) # Convert the scope to a string in case a symbol was given since # we use string comparisons for everything. scope = scope.to_s # Shallow copy the hash for the result result = original.dup original.each do |key, value| parts = key.to_s.split("__", 2) # If we don't have the proper parts, then bail next if parts.length != 2 # If this is our scope, then override if parts[0] == scope result[parts[1].to_sym] = value end end result end end end end vagrant-2.0.2/lib/vagrant/util/shell_quote.rb000066400000000000000000000005661323370221500212200ustar00rootroot00000000000000module Vagrant module Util module ShellQuote # This will auto-escape the text with the given quote mark type. # # @param [String] text Text to escape # @param [String] quote The quote character, such as " def self.escape(text, quote) text.gsub(/#{quote}/) do |m| "#{m}\\#{m}#{m}" end end end end end vagrant-2.0.2/lib/vagrant/util/silence_warnings.rb000066400000000000000000000004061323370221500222170ustar00rootroot00000000000000module Vagrant module Util module SilenceWarnings # This silences any Ruby warnings. def self.silence! original = $VERBOSE $VERBOSE = nil return yield ensure $VERBOSE = original end end end end vagrant-2.0.2/lib/vagrant/util/ssh.rb000066400000000000000000000213541323370221500174670ustar00rootroot00000000000000require "log4r" require 'childprocess' require "vagrant/util/file_mode" require "vagrant/util/platform" require "vagrant/util/safe_exec" require "vagrant/util/safe_puts" require "vagrant/util/subprocess" require "vagrant/util/which" module Vagrant module Util # This is a class that has helpers on it for dealing with SSH. These # helpers don't depend on any part of Vagrant except what is given # via the parameters. class SSH extend SafePuts LOGGER = Log4r::Logger.new("vagrant::util::ssh") # Checks that the permissions for a private key are valid, and fixes # them if possible. SSH requires that permissions on the private key # are 0600 on POSIX based systems. This will make a best effort to # fix these permissions if they are not properly set. # # @param [Pathname] key_path The path to the private key. def self.check_key_permissions(key_path) # Don't do anything if we're on Windows, since Windows doesn't worry # about key permissions. return if Platform.windows? || Platform.wsl_windows_access_bypass?(key_path) LOGGER.debug("Checking key permissions: #{key_path}") stat = key_path.stat if !stat.owned? && Process.uid != 0 # The SSH key must be owned by ourselves, unless we're root raise Errors::SSHKeyBadOwner, key_path: key_path end if FileMode.from_octal(stat.mode) != "600" LOGGER.info("Attempting to correct key permissions to 0600") key_path.chmod(0600) # Re-stat the file to get the new mode, and verify it worked stat = key_path.stat if FileMode.from_octal(stat.mode) != "600" raise Errors::SSHKeyBadPermissions, key_path: key_path end end rescue Errno::EPERM # This shouldn't happen since we verify we own the file, but # it is possible in theory, so we raise an error. raise Errors::SSHKeyBadPermissions, key_path: key_path end # Halts the running of this process and replaces it with a full-fledged # SSH shell into a remote machine. # # Note: This method NEVER returns. The process ends after this. # # @param [Hash] ssh_info This is the SSH information. For the keys # required please see the documentation of {Machine#ssh_info}. # @param [Hash] opts These are additional options that are supported # by exec. def self.exec(ssh_info, opts={}) # Ensure the platform supports ssh. On Windows there are several programs which # include ssh, notably git, mingw and cygwin, but make sure ssh is in the path! # First try using the original path provided ssh_path = Which.which("ssh", original_path: true) # If we didn't find an ssh executable, see if we shipped one if !ssh_path ssh_path = Which.which("ssh") if ssh_path && Platform.windows? && (Platform.cygwin? || Platform.msys?) LOGGER.warn("Failed to locate native SSH executable. Using vendored version.") LOGGER.warn("If display issues are encountered, install the ssh package for your environment.") end end if !ssh_path if Platform.windows? raise Errors::SSHUnavailableWindows, host: ssh_info[:host], port: ssh_info[:port], username: ssh_info[:username], key_path: ssh_info[:private_key_path].join(", ") end raise Errors::SSHUnavailable end if Platform.windows? # On Windows, we need to detect whether SSH is actually "plink" # underneath the covers. In this case, we tell the user. r = Subprocess.execute(ssh_path) if r.stdout.include?("PuTTY Link") || r.stdout.include?("Plink: command-line connection utility") raise Errors::SSHIsPuttyLink, host: ssh_info[:host], port: ssh_info[:port], username: ssh_info[:username], key_path: ssh_info[:private_key_path].join(", ") end end # If plain mode is enabled then we don't do any authentication (we don't # set a user or an identity file) plain_mode = opts[:plain_mode] options = {} options[:host] = ssh_info[:host] options[:port] = ssh_info[:port] options[:username] = ssh_info[:username] options[:private_key_path] = ssh_info[:private_key_path] log_level = ssh_info[:log_level] || "FATAL" # Command line options command_options = [ "-p", options[:port].to_s, "-o", "LogLevel=#{log_level}"] if ssh_info[:compression] command_options += ["-o", "Compression=yes"] end if ssh_info[:dsa_authentication] command_options += ["-o", "DSAAuthentication=yes"] end # Solaris/OpenSolaris/Illumos uses SunSSH which doesn't support the # IdentitiesOnly option. Also, we don't enable it in plain mode or if # if keys_only is false so that SSH and Net::SSH properly search our identities # and tries to do it itself. if !Platform.solaris? && !plain_mode && ssh_info[:keys_only] command_options += ["-o", "IdentitiesOnly=yes"] end # no strict hostkey checking unless paranoid if ! ssh_info[:verify_host_key] command_options += [ "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null"] end # If we're not in plain mode and :private_key_path is set attach the private key path(s). if !plain_mode && options[:private_key_path] options[:private_key_path].each do |path| # Use '-o' instead of '-i' because '-i' does not call # percent_expand in misc.c, but '-o' does. when passing the path, # replace '%' in the path with '%%' to escape the '%' path = path.to_s.gsub('%', '%%') command_options += ["-o", "IdentityFile=\"#{path}\""] end end if ssh_info[:forward_x11] # Both are required so that no warnings are shown regarding X11 command_options += [ "-o", "ForwardX11=yes", "-o", "ForwardX11Trusted=yes"] end if ssh_info[:proxy_command] command_options += ["-o", "ProxyCommand=#{ssh_info[:proxy_command]}"] end if ssh_info[:forward_env] command_options += ["-o", "SendEnv=#{ssh_info[:forward_env].join(" ")}"] end # Configurables -- extra_args should always be last due to the way the # ssh args parser works. e.g. if the user wants to use the -t option, # any shell command(s) she'd like to run on the remote server would # have to be the last part of the 'ssh' command: # # $ ssh localhost -t -p 2222 "cd mydirectory; bash" # # Without having extra_args be last, the user loses this ability command_options += ["-o", "ForwardAgent=yes"] if ssh_info[:forward_agent] # Note about :extra_args # ssh_info[:extra_args] comes from a machines ssh config in a Vagrantfile, # where as opts[:extra_args] comes from running the ssh command command_options += Array(ssh_info[:extra_args]) if ssh_info[:extra_args] command_options.concat(opts[:extra_args]) if opts[:extra_args] # Build up the host string for connecting host_string = options[:host] host_string = "#{options[:username]}@#{host_string}" if !plain_mode command_options.unshift(host_string) # On Cygwin we want to get rid of any DOS file warnings because # we really don't care since both work. ENV["nodosfilewarning"] = "1" if Platform.cygwin? # If an ssh command is defined, use that. If an ssh binary was # discovered on the path, use that. Otherwise fail to just trying `ssh` ssh = ssh_info[:ssh_command] || ssh_path || 'ssh' # Invoke SSH with all our options if !opts[:subprocess] LOGGER.info("Invoking SSH: #{ssh} #{command_options.inspect}") SafeExec.exec(ssh, *command_options) return end # If we're still here, it means we're supposed to subprocess # out to ssh rather than exec it. LOGGER.info("Executing SSH in subprocess: #{ssh} #{command_options.inspect}") process = ChildProcess.build(ssh, *command_options) process.io.inherit! # Forward configured environment variables. if ssh_info[:forward_env] ssh_info[:forward_env].each do |key| process.environment[key] = ENV[key] end end process.start process.wait return process.exit_code end end end end vagrant-2.0.2/lib/vagrant/util/stacked_proc_runner.rb000066400000000000000000000021531323370221500227200ustar00rootroot00000000000000module Vagrant module Util # Represents the "stacked proc runner" behavior which is used a # couple places within Vagrant. This allows procs to "stack" on # each other, then all execute in a single action. An example of # its uses can be seen in the {Config} class. module StackedProcRunner # Returns the proc stack. This should always be called as the # accessor of the stack. The instance variable itself should _never_ # be used. # # @return [Array] def proc_stack @_proc_stack ||= [] end # Adds (pushes) a proc to the stack. The actual proc added here is # not executed, but merely stored. # # @param [Proc] block def push_proc(&block) proc_stack << block end # Executes all the procs on the stack, passing in the given arguments. # The stack is not cleared afterwords. It is up to the user of this # mixin to clear the stack by calling `proc_stack.clear`. def run_procs!(*args) proc_stack.each do |proc| proc.call(*args) end end end end endvagrant-2.0.2/lib/vagrant/util/string_block_editor.rb000066400000000000000000000044271323370221500227220ustar00rootroot00000000000000module Vagrant module Util # This class modifies strings by creating and managing Vagrant-owned # "blocks" via wrapping them in specially formed comments. # # This is useful when modifying a file that someone else owns and adding # automatic entries into it. Example: /etc/exports or some other # configuration file. # # Vagrant marks ownership of a block in the string by wrapping it in # VAGRANT-BEGIN and VAGRANT-END comments with a unique ID. Example: # # foo # # VAGRANT-BEGIN: id # some contents # created by vagrant # # VAGRANT-END: id # # The goal of this class is to be able to insert and remove these # blocks without modifying anything else in the string. # # The strings usually come from files but it is up to the caller to # manage the file resource. class StringBlockEditor # The current string value. This is the value that is modified by # the methods below. # # @return [String] attr_reader :value def initialize(string) @value = string end # This returns the keys (or ids) that are in the string. # # @return [] def keys regexp = /^#\s*VAGRANT-BEGIN:\s*(.+?)$\r?\n?(.*)$\r?\n?^#\s*VAGRANT-END:\s(\1)$/m @value.scan(regexp).map do |match| match[0] end end # This deletes the block with the given key if it exists. def delete(key) key = Regexp.quote(key) regexp = /^#\s*VAGRANT-BEGIN:\s*#{key}$.*^#\s*VAGRANT-END:\s*#{key}$\r?\n?/m @value.gsub!(regexp, "") end # This gets the value of the block with the given key. def get(key) key = Regexp.quote(key) regexp = /^#\s*VAGRANT-BEGIN:\s*#{key}$\r?\n?(.*?)\r?\n?^#\s*VAGRANT-END:\s*#{key}$\r?\n?/m match = regexp.match(@value) return nil if !match match[1] end # This inserts a block with the given key and value. # # @param [String] key # @param [String] value def insert(key, value) # Insert the new block into the value new_block = < ex # Raise our own version of the error so that users of the class # don't need to be aware of ChildProcess raise LaunchError.new(ex.message) end # Make sure the stdin does not buffer process.io.stdin.sync = true if RUBY_PLATFORM != "java" # On Java, we have to close after. See down the method... # Otherwise, we close the writers right here, since we're # not on the writing side. stdout_writer.close stderr_writer.close end # Create a dictionary to store all the output we see. io_data = { stdout: "", stderr: "" } # Record the start time for timeout purposes start_time = Time.now.to_i open_readers = [stdout, stderr] open_writers = notify_stdin ? [process.io.stdin] : [] @logger.debug("Selecting on IO") while true results = ::IO.select(open_readers, open_writers, nil, 0.1) results ||= [] readers = results[0] writers = results[1] # Check if we have exceeded our timeout raise TimeoutExceeded, process.pid if timeout && (Time.now.to_i - start_time) > timeout # Check the readers to see if they're ready if readers && !readers.empty? readers.each do |r| # Read from the IO object data = IO.read_until_block(r) # We don't need to do anything if the data is empty next if data.empty? io_name = r == stdout ? :stdout : :stderr @logger.debug("#{io_name}: #{data.chomp}") io_data[io_name] += data yield io_name, data if block_given? && notify_table[io_name] end end # Break out if the process exited. We have to do this before # attempting to write to stdin otherwise we'll get a broken pipe # error. break if process.exited? # Check the writers to see if they're ready, and notify any listeners if writers && !writers.empty? && block_given? yield :stdin, process.io.stdin # if the callback closed stdin, we should remove it, because # IO.select() will throw if called with a closed io. if process.io.stdin.closed? open_writers = [] end end end # Wait for the process to end. begin remaining = (timeout || 32000) - (Time.now.to_i - start_time) remaining = 0 if remaining < 0 @logger.debug("Waiting for process to exit. Remaining to timeout: #{remaining}") process.poll_for_exit(remaining) rescue ChildProcess::TimeoutError raise TimeoutExceeded, process.pid end @logger.debug("Exit status: #{process.exit_code}") # Read the final output data, since it is possible we missed a small # amount of text between the time we last read data and when the # process exited. [stdout, stderr].each do |io| # Read the extra data, ignoring if there isn't any extra_data = IO.read_until_block(io) next if extra_data == "" # Log it out and accumulate io_name = io == stdout ? :stdout : :stderr io_data[io_name] += extra_data @logger.debug("#{io_name}: #{extra_data.chomp}") # Yield to any listeners any remaining data yield io_name, extra_data if block_given? && notify_table[io_name] end if RUBY_PLATFORM == "java" # On JRuby, we need to close the writers after the process, # for some reason. See GH-711. stdout_writer.close stderr_writer.close end # Return an exit status container return Result.new(process.exit_code, io_data[:stdout], io_data[:stderr]) ensure if process && process.alive? # Make sure no matter what happens, the process exits process.stop(2) end end protected # An error which raises when a process fails to start class LaunchError < StandardError; end # An error which occurs when the process doesn't end within # the given timeout. class TimeoutExceeded < StandardError attr_reader :pid def initialize(pid) super() @pid = pid end end # Container class to store the results of executing a subprocess. class Result attr_reader :exit_code attr_reader :stdout attr_reader :stderr def initialize(exit_code, stdout, stderr) @exit_code = exit_code @stdout = stdout @stderr = stderr end end private # This is, quite possibly, the saddest function in all of Vagrant. # # If a user is running Vagrant via Bundler (but not via the official # installer), we want to reset to the "original" environment so that when # shelling out to other Ruby processes (specifically), the original # environment is restored. This is super important for things like # rbenv and chruby, who rely on environment variables to locate gems, but # Bundler stomps on those environment variables like an angry T-Rex after # watching Jurassic Park 2 and realizing they replaced you with CGI. # # If a user is running in Vagrant via the official installer, BUT trying # to execute a subprocess *outside* of the installer, we want to reset to # the "original" environment. In this case, the Vagrant installer actually # knows what the original environment was and replaces it completely. # # Finally, we reset any Bundler-specific environment variables, since the # subprocess being called could, itself, be Bundler. And Bundler does not # behave very nicely in these circumstances. # # This function was added in Vagrant 1.7.3, but there is a failsafe # because the author doesn't trust himself that this functionality won't # break existing assumptions, so users can specify # `VAGRANT_SKIP_SUBPROCESS_JAILBREAK` and none of the above will happen. # # This function modifies the given hash in place! # # @return [nil] def jailbreak(env = {}) return if ENV.key?("VAGRANT_SKIP_SUBPROCESS_JAILBREAK") if defined?(::Bundler) && defined?(::Bundler::ORIGINAL_ENV) env.replace(::Bundler::ORIGINAL_ENV) end env.merge!(Vagrant.original_env) # Bundler does this, so I guess we should as well, since I think it # other subprocesses that use Bundler will reload it env["MANPATH"] = ENV["BUNDLE_ORIG_MANPATH"] # Replace all current environment BUNDLE_ variables to nil ENV.each do |k,_| env[k] = nil if k[0,7] == "BUNDLE_" end # If RUBYOPT was set, unset it with Bundler if ENV.key?("RUBYOPT") env["RUBYOPT"] = ENV["RUBYOPT"].sub("-rbundler/setup", "") end nil end end end end vagrant-2.0.2/lib/vagrant/util/template_renderer.rb000066400000000000000000000054171323370221500223750ustar00rootroot00000000000000require 'ostruct' require "pathname" require 'erubis' module Vagrant module Util # This class is used to render the ERB templates in the # `GEM_ROOT/templates` directory. class TemplateRenderer < OpenStruct class << self # Render a given template and return the result. This method optionally # takes a block which will be passed the renderer prior to rendering, which # allows the caller to set any view variables within the renderer itself. # # @return [String] Rendered template def render(*args) render_with(:render, *args) end # Render a given string and return the result. This method optionally # takes a block which will be passed the renderer prior to rendering, which # allows the caller to set any view variables within the renderer itself. # # @param [String] template The template data string. # @return [String] Rendered template def render_string(*args) render_with(:render_string, *args) end # Method used internally to DRY out the other renderers. This method # creates and sets up the renderer before calling a specified method on it. def render_with(method, template, data={}) renderer = new(template, data) yield renderer if block_given? renderer.send(method.to_sym) end end def initialize(template, data = {}) super() @template_root = data.delete(:template_root) @template_root ||= Vagrant.source_root.join("templates") @template_root = Pathname.new(@template_root) data[:template] = template data.each do |key, value| send("#{key}=", value) end end # Renders the template using the class intance as the binding. Because the # renderer inherits from `OpenStruct`, additional view variables can be # added like normal accessors. # # @return [String] def render old_template = template result = nil File.open(full_template_path, 'r') do |f| self.template = f.read result = render_string end result ensure self.template = old_template end # Renders a template, handling the template as a string, but otherwise # acting the same way as {#render}. # # @return [String] def render_string Erubis::Eruby.new(template, trim: true).result(binding) end # Returns the full path to the template, taking into accoun the gem directory # and adding the `.erb` extension to the end. # # @return [String] def full_template_path @template_root.join("#{template}.erb").to_s.squeeze("/") end end end end vagrant-2.0.2/lib/vagrant/util/which.rb000066400000000000000000000034431323370221500177730ustar00rootroot00000000000000require "vagrant/util/platform" module Vagrant module Util class Which # Cross-platform way of finding an executable in the PATH. # # which('ruby') #=> /usr/bin/ruby # # This code is adapted from the following post by mislav: # http://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby # # @param [String] cmd The command to search for in the PATH. # @param [Hash] opts Optional flags # @option [Boolean] :original_path Search within original path if available # @return [String] The full path to the executable or `nil` if not found. def self.which(cmd, **opts) exts = nil if !Platform.windows? || ENV['PATHEXT'].nil? # If the PATHEXT variable is empty, we're on *nix and need to find # the exact filename exts = [''] elsif File.extname(cmd).length != 0 # On Windows: if filename contains an extension, we must match that # exact filename exts = [''] else # On Windows: otherwise try to match all possible executable file # extensions (.EXE .COM .BAT etc.) exts = ENV['PATHEXT'].split(';') end if opts[:original_path] search_path = ENV.fetch('VAGRANT_OLD_ENV_PATH', ENV['PATH']) else search_path = ENV['PATH'] end SilenceWarnings.silence! do search_path.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '').split(File::PATH_SEPARATOR).each do |path| exts.each do |ext| exe = "#{path}#{File::SEPARATOR}#{cmd}#{ext}" return exe if File.executable? exe end end end return nil end end end end vagrant-2.0.2/lib/vagrant/util/windows_path.rb000066400000000000000000000020701323370221500213720ustar00rootroot00000000000000require "fiddle/import" module Vagrant module Util module WindowsPath module API extend Fiddle::Importer dlload 'kernel32.dll' extern("int GetLongPathNameA(char*, char*, int)", :stdcall) end # Converts a Windows shortname to a long name. This only works # for ASCII paths currently and doesn't use the wide character # support. def self.longname(name) # We loop over the API call in case we didn't allocate enough # buffer space. In general it is usually enough. bufferlen = 250 buffer = nil while true buffer = ' ' * bufferlen len = API.GetLongPathNameA(name.to_s, buffer, buffer.size) if bufferlen < len # If the length returned is larger than our buffer length, # it is the API telling us it needs more space. Allocate it # and retry. bufferlen = len continue end break end return buffer.rstrip.chomp("\0") end end end end vagrant-2.0.2/lib/vagrant/vagrantfile.rb000066400000000000000000000221531323370221500202150ustar00rootroot00000000000000require "vagrant/util/template_renderer" module Vagrant # This class provides a way to load and access the contents # of a Vagrantfile. # # This class doesn't actually load Vagrantfiles, parse them, # merge them, etc. That is the job of {Config::Loader}. This # class, on the other hand, has higher-level operations on # a loaded Vagrantfile such as looking up the defined machines, # loading the configuration of a specific machine/provider combo, # etc. class Vagrantfile # This is the configuration loaded as-is given the loader and # keys to #initialize. attr_reader :config # Initializes by loading a Vagrantfile. # # @param [Config::Loader] loader Configuration loader that should # already be configured with the proper Vagrantflie locations. # This usually comes from {Vagrant::Environment} # @param [Array] keys The Vagrantfiles to load and the # order to load them in (keys within the loader). def initialize(loader, keys) @keys = keys @loader = loader @config, _ = loader.load(keys) end # Returns a {Machine} for the given name and provider that # is represented by this Vagrantfile. # # @param [Symbol] name Name of the machine. # @param [Symbol] provider The provider the machine should # be backed by (required for provider overrides). # @param [BoxCollection] boxes BoxCollection to look up the # box Vagrantfile. # @param [Pathname] data_path Path where local machine data # can be stored. # @param [Environment] env The environment running this machine # @return [Machine] def machine(name, provider, boxes, data_path, env) # Load the configuration for the machine results = machine_config(name, provider, boxes) box = results[:box] config = results[:config] config_errors = results[:config_errors] config_warnings = results[:config_warnings] provider_cls = results[:provider_cls] provider_options = results[:provider_options] # If there were warnings or errors we want to output them if !config_warnings.empty? || !config_errors.empty? # The color of the output depends on whether we have warnings # or errors... level = config_errors.empty? ? :warn : :error output = Util::TemplateRenderer.render( "config/messages", warnings: config_warnings, errors: config_errors).chomp env.ui.send(level, I18n.t("vagrant.general.config_upgrade_messages", name: name, output: output)) # If we had errors, then we bail raise Errors::ConfigUpgradeErrors if !config_errors.empty? end # Get the provider configuration from the final loaded configuration provider_config = config.vm.get_provider_config(provider) # Create machine data directory if it doesn't exist # XXX: Permissions error here. FileUtils.mkdir_p(data_path) # Create the machine and cache it for future calls. This will also # return the machine from this method. return Machine.new(name, provider, provider_cls, provider_config, provider_options, config, data_path, box, env, self) end # Returns the configuration for a single machine. # # When loading a box Vagrantfile, it will be prepended to the # key order specified when initializing this class. Sub-machine # and provider-specific overrides are appended at the end. The # actual order is: # # - box # - keys specified for #initialize # - sub-machine # - provider # # The return value is a hash with the following keys (symbols) # and values: # # - box: the {Box} backing the machine # - config: the actual configuration # - config_errors: list of errors, if any # - config_warnings: list of warnings, if any # - provider_cls: class of the provider backing the machine # - provider_options: options for the provider # # @param [Symbol] name Name of the machine. # @param [Symbol] provider The provider the machine should # be backed by (required for provider overrides). # @param [BoxCollection] boxes BoxCollection to look up the # box Vagrantfile. # @return [Hash] Various configuration parameters for a # machine. See the main documentation body for more info. def machine_config(name, provider, boxes) keys = @keys.dup sub_machine = @config.vm.defined_vms[name] if !sub_machine raise Errors::MachineNotFound, name: name, provider: provider end provider_plugin = nil provider_cls = nil provider_options = {} box_formats = nil if provider != nil provider_plugin = Vagrant.plugin("2").manager.providers[provider] if !provider_plugin raise Errors::ProviderNotFound, machine: name, provider: provider 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 end end # Add the sub-machine configuration to the loader and keys vm_config_key = "#{object_id}_machine_#{name}" @loader.set(vm_config_key, sub_machine.config_procs) keys << vm_config_key # Load once so that we can get the proper box value config, config_warnings, config_errors = @loader.load(keys) # Track the original box so we know if we changed box = nil original_box = config.vm.box # The proc below loads the box and provider overrides. This is # in a proc because it may have to recurse if the provider override # changes the box. load_box_proc = lambda do local_keys = keys.dup # Load the box Vagrantfile, if there is one if config.vm.box && boxes box = boxes.find(config.vm.box, box_formats, config.vm.box_version) if box box_vagrantfile = find_vagrantfile(box.directory) if box_vagrantfile box_config_key = "#{boxes.object_id}_#{box.name}_#{box.provider}".to_sym @loader.set(box_config_key, box_vagrantfile) local_keys.unshift(box_config_key) config, config_warnings, config_errors = @loader.load(local_keys) end end end # Load provider overrides provider_overrides = config.vm.get_provider_overrides(provider) if !provider_overrides.empty? config_key = "#{object_id}_vm_#{name}_#{config.vm.box}_#{provider}".to_sym @loader.set(config_key, provider_overrides) local_keys << config_key config, config_warnings, config_errors = @loader.load(local_keys) end # If the box changed, then we need to reload if original_box != config.vm.box # TODO: infinite loop protection? original_box = config.vm.box load_box_proc.call end end # Load the box and provider overrides load_box_proc.call return { box: box, provider_cls: provider_cls, provider_options: provider_options.dup, config: config, config_warnings: config_warnings, config_errors: config_errors, } end # Returns a list of the machines that are defined within this # Vagrantfile. # # @return [Array] def machine_names @config.vm.defined_vm_keys.dup end # Returns a list of the machine names as well as the options that # were specified for that machine. # # @return [Hash] def machine_names_and_options {}.tap do |r| @config.vm.defined_vms.each do |name, subvm| r[name] = subvm.options || {} end end end # Returns the name of the machine that is designated as the # "primary." # # In the case of a single-machine environment, this is just the # single machine name. In the case of a multi-machine environment, # then this is the machine that is marked as primary, or nil if # no primary machine was specified. # # @return [Symbol] def primary_machine_name # If it is a single machine environment, then return the name return machine_names.first if machine_names.length == 1 # If it is a multi-machine environment, then return the primary @config.vm.defined_vms.each do |name, subvm| return name if subvm.options[:primary] end # If no primary was specified, nil it is nil end protected def find_vagrantfile(search_path) ["Vagrantfile", "vagrantfile"].each do |vagrantfile| current_path = search_path.join(vagrantfile) return current_path if current_path.file? end nil end end end vagrant-2.0.2/lib/vagrant/version.rb000066400000000000000000000004301323370221500173720ustar00rootroot00000000000000module Vagrant # This will always be up to date with the current version of Vagrant, # since it is used to generate the gemspec and is also the source of # the version for `vagrant -v` VERSION = File.read( File.expand_path("../../../version.txt", __FILE__)).chomp end vagrant-2.0.2/plugins/000077500000000000000000000000001323370221500146345ustar00rootroot00000000000000vagrant-2.0.2/plugins/README.md000066400000000000000000000003531323370221500161140ustar00rootroot00000000000000# Vagrant Core Plugins These are plugins that ship with Vagrant. Vagrant core uses its own plugin system to power a lot of the core pieces that ship with Vagrant. Each plugin will have its own README which explains its specific role. vagrant-2.0.2/plugins/commands/000077500000000000000000000000001323370221500164355ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/box/000077500000000000000000000000001323370221500172255ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/box/command/000077500000000000000000000000001323370221500206435ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/box/command/add.rb000066400000000000000000000065501323370221500217260ustar00rootroot00000000000000require 'optparse' require_relative 'download_mixins' module VagrantPlugins module CommandBox module Command class Add < Vagrant.plugin("2", :command) include DownloadMixins def execute options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant box add [options] " o.separator "" o.separator "Options:" o.separator "" o.on("-c", "--clean", "Clean any temporary download files") do |c| options[:clean] = c end o.on("-f", "--force", "Overwrite an existing box if it exists") do |f| options[:force] = f end build_download_options(o, options) o.on("--location-trusted", "Trust 'Location' header from HTTP redirects and use the same credentials for subsequent urls as for the initial one") do |l| options[:location_trusted] = l end o.on("--provider PROVIDER", String, "Provider the box should satisfy") do |p| options[:provider] = p end o.on("--box-version VERSION", String, "Constrain version of the added box") do |v| options[:version] = v end o.separator "" o.separator "The box descriptor can be the name of a box on HashiCorp's Vagrant Cloud," o.separator "or a URL, or a local .box file, or a local .json file containing" o.separator "the catalog metadata." o.separator "" o.separator "The options below only apply if you're adding a box file directly," o.separator "and not using a Vagrant server or a box structured like 'user/box':" o.separator "" o.on("--checksum CHECKSUM", String, "Checksum for the box") do |c| options[:checksum] = c end o.on("--checksum-type TYPE", String, "Checksum type (md5, sha1, sha256)") do |c| options[:checksum_type] = c.to_sym end o.on("--name BOX", String, "Name of the box") do |n| options[:name] = n end end # Parse the options argv = parse_options(opts) return if !argv if argv.empty? || argv.length > 2 raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp end url = argv[0] if argv.length == 2 options[:name] = argv[0] url = argv[1] end @env.action_runner.run(Vagrant::Action.action_box_add, { box_url: url, box_name: options[:name], box_provider: options[:provider], box_version: options[:version], box_checksum_type: options[:checksum_type], box_checksum: options[:checksum], box_clean: options[:clean], 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_insecure: options[:insecure], box_download_location_trusted: options[:location_trusted], ui: Vagrant::UI::Prefixed.new(@env.ui, "box"), }) # Success, exit status 0 0 end end end end end vagrant-2.0.2/plugins/commands/box/command/download_mixins.rb000066400000000000000000000016051323370221500243700ustar00rootroot00000000000000module VagrantPlugins module CommandBox module DownloadMixins # This adds common download command line flags to the given # OptionParser, storing the result in the `options` dictionary. # # @param [OptionParser] parser # @param [Hash] options def build_download_options(parser, options) # Add the options parser.on("--insecure", "Do not validate SSL certificates") do |i| options[:insecure] = i end parser.on("--cacert FILE", String, "CA certificate for SSL download") do |c| options[:ca_cert] = c end parser.on("--capath DIR", String, "CA certificate directory for SSL download") do |c| options[:ca_path] = c end parser.on("--cert FILE", String, "A client SSL cert, if needed") do |c| options[:client_cert] = c end end end end end vagrant-2.0.2/plugins/commands/box/command/list.rb000066400000000000000000000041561323370221500221510ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandBox module Command class List < Vagrant.plugin("2", :command) def execute options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant box list [options]" o.separator "" o.separator "Options:" o.separator "" o.on("-i", "--box-info", "Displays additional information about the boxes") do |i| options[:info] = i end end # Parse the options argv = parse_options(opts) return if !argv boxes = @env.boxes.all if boxes.empty? return @env.ui.warn(I18n.t("vagrant.commands.box.no_installed_boxes"), prefix: false) end list_boxes(boxes, options[:info]) # Success, exit status 0 0 end private def list_boxes(boxes, extra_info) # Find the longest box name longest_box = boxes.max_by { |x| x[0].length } longest_box_length = longest_box[0].length # Go through each box and output the information about it. We # ignore the "v1" param for now since I'm not yet sure if its # important for the user to know what boxes need to be upgraded # and which don't, since we plan on doing that transparently. boxes.each do |name, version, provider| @env.ui.info("#{name.ljust(longest_box_length)} (#{provider}, #{version})") @env.ui.machine("box-name", name) @env.ui.machine("box-provider", provider) @env.ui.machine("box-version", version) info_file = @env.boxes.find(name, provider, version). directory.join("info.json") if info_file.file? info = JSON.parse(info_file.read) info.each do |k, v| @env.ui.machine("box-info", k, v) if extra_info @env.ui.info(" - #{k}: #{v}", prefix: false) end end end end end end end end end vagrant-2.0.2/plugins/commands/box/command/outdated.rb000066400000000000000000000056421323370221500230100ustar00rootroot00000000000000require 'optparse' require_relative 'download_mixins' module VagrantPlugins module CommandBox module Command class Outdated < Vagrant.plugin("2", :command) include DownloadMixins def execute options = {} download_options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant box outdated [options]" o.separator "" o.separator "Checks if there is a new version available for the box" o.separator "that are you are using. If you pass in the --global flag," o.separator "all boxes will be checked for updates." o.separator "" o.separator "Options:" o.separator "" o.on("--global", "Check all boxes installed") do |g| options[:global] = g end build_download_options(o, download_options) end argv = parse_options(opts) return if !argv # If we're checking the boxes globally, then do that. if options[:global] outdated_global(download_options) return 0 end with_target_vms(argv) do |machine| @env.action_runner.run(Vagrant::Action.action_box_outdated, { box_outdated_force: true, box_outdated_refresh: true, box_outdated_success_ui: true, machine: machine, }.merge(download_options)) end end def outdated_global(download_options) boxes = {} @env.boxes.all.reverse.each do |name, version, provider| box = @env.boxes.find(name, provider, version) if !box.metadata_url @env.ui.output(I18n.t( "vagrant.box_outdated_no_metadata", name: box.name, provider: box.provider)) next end md = nil begin md = box.load_metadata(download_options) rescue Vagrant::Errors::BoxMetadataDownloadError => e @env.ui.error(I18n.t( "vagrant.box_outdated_metadata_error", name: box.name, provider: box.provider, message: e.extra_data[:message])) next end current = Gem::Version.new(box.version) latest = Gem::Version.new(md.versions.last) if latest <= current @env.ui.success(I18n.t( "vagrant.box_up_to_date", name: box.name, provider: box.provider, version: box.version)) else @env.ui.warn(I18n.t( "vagrant.box_outdated", name: box.name, provider: box.provider, current: box.version, latest: latest.to_s,)) end end end end end end end vagrant-2.0.2/plugins/commands/box/command/prune.rb000066400000000000000000000104741323370221500223270ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandBox module Command class Prune < Vagrant.plugin("2", :command) def execute options = {} options[:force] = false options[:dry_run] = false opts = OptionParser.new do |o| o.banner = "Usage: vagrant box prune [options]" o.separator "" o.separator "Options:" o.separator "" o.on("-p PROVIDER", "--provider PROVIDER", String, "The specific provider type for the boxes to destroy.") do |p| options[:provider] = p end o.on("-n", "--dry-run", "Only print the boxes that would be removed.") do |f| options[:dry_run] = f end o.on("--name NAME", String, "The specific box name to check for outdated versions.") do |name| options[:name] = name end o.on("-f", "--force", "Destroy without confirmation even when box is in use.") do |f| options[:force] = f end end # Parse the options argv = parse_options(opts) return if !argv boxes = @env.boxes.all.sort if boxes.empty? 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]) # Success, exit status 0 0 end private def delete_oldest_boxes(boxes, only_provider, skip_confirm, only_name, dry_run) # Find the longest box name longest_box = boxes.max_by { |x| x[0].length } longest_box_length = longest_box[0].length # Hash map to keep track of newest versions newest_boxes = Hash.new # First find the newest version for every installed box boxes.each do |name, version, provider| next if only_provider and only_provider != provider.to_s next if only_name and only_name != name # Nested to make sure it works for boxes with different providers if newest_boxes.has_key?(name) if newest_boxes[name].has_key?(provider) saved = Gem::Version.new(newest_boxes[name][provider]) current = Gem::Version.new(version) if current > saved newest_boxes[name][provider] = version end else newest_boxes[name][provider] = version end else newest_boxes[name] = Hash.new newest_boxes[name][provider] = version end end @env.ui.info("The following boxes will be kept..."); newest_boxes.each do |name, providers| providers.each do |provider, version| @env.ui.info("#{name.ljust(longest_box_length)} (#{provider}, #{version})") @env.ui.machine("box-name", name) @env.ui.machine("box-provider", provider) @env.ui.machine("box-version", version) end end @env.ui.info("", prefix: false) @env.ui.info("Checking for older boxes..."); # Track if we removed anything so the user can be informed removed_any_box = false boxes.each do |name, version, provider| next if !newest_boxes.has_key?(name) or !newest_boxes[name].has_key?(provider) current = Gem::Version.new(version) saved = Gem::Version.new(newest_boxes[name][provider]) if current < saved removed_any_box = true # Use the remove box action if dry_run @env.ui.info("Would remove #{name} #{provider} #{version}") else @env.action_runner.run(Vagrant::Action.action_box_remove, { box_name: name, box_provider: provider, box_version: version, force_confirm_box_remove: skip_confirm, box_remove_all_versions: false, }) end end end if !removed_any_box @env.ui.info("No old versions of boxes to remove..."); end end end end end end vagrant-2.0.2/plugins/commands/box/command/remove.rb000066400000000000000000000037251323370221500224740ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandBox module Command class Remove < Vagrant.plugin("2", :command) def execute options = {} options[:force] = false opts = OptionParser.new do |o| o.banner = "Usage: vagrant box remove " o.separator "" o.separator "Options:" o.separator "" o.on("-f", "--force", "Remove without confirmation.") do |f| options[:force] = f end o.on("--provider PROVIDER", String, "The specific provider type for the box to remove") do |p| options[:provider] = p end o.on("--box-version VERSION", String, "The specific version of the box to remove") do |v| options[:version] = v end o.on("--all", "Remove all available versions of the box") do |a| options[:all] = a end end # Parse the options argv = parse_options(opts) return if !argv if argv.empty? || argv.length > 2 raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp end if argv.length == 2 # @deprecated @env.ui.warn("WARNING: The second argument to `vagrant box remove`") @env.ui.warn("is deprecated. Please use the --provider flag. This") @env.ui.warn("feature will stop working in the next version.") options[:provider] = argv[1] end @env.action_runner.run(Vagrant::Action.action_box_remove, { box_name: argv[0], box_provider: options[:provider], box_version: options[:version], force_confirm_box_remove: options[:force], box_remove_all_versions: options[:all], }) # Success, exit status 0 0 end end end end end vagrant-2.0.2/plugins/commands/box/command/repackage.rb000066400000000000000000000024101323370221500231070ustar00rootroot00000000000000require "fileutils" require 'optparse' require "pathname" module VagrantPlugins module CommandBox module Command class Repackage < Vagrant.plugin("2", :command) def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant box repackage " end # Parse the options argv = parse_options(opts) return if !argv raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp if argv.length != 3 box_name = argv[0] box_provider = argv[1].to_sym box_version = argv[2] # Verify the box exists that we want to repackage box = @env.boxes.find(box_name, box_provider, "= #{box_version}") if !box raise Vagrant::Errors::BoxNotFoundWithProviderAndVersion, name: box_name, provider: box_provider.to_s, version: box_version end # Repackage the box output_name = @env.vagrantfile.config.package.name || "package.box" output_path = Pathname.new(File.expand_path(output_name, FileUtils.pwd)) box.repackage(output_path) # Success, exit status 0 0 end end end end end vagrant-2.0.2/plugins/commands/box/command/root.rb000066400000000000000000000053101323370221500221520ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandBox module Command class Root < Vagrant.plugin("2", :command) def self.synopsis "manages boxes: installation, removal, etc." end def initialize(argv, env) super @main_args, @sub_command, @sub_args = split_main_and_subcommand(argv) @subcommands = Vagrant::Registry.new @subcommands.register(:add) do require File.expand_path("../add", __FILE__) Add end @subcommands.register(:list) do require File.expand_path("../list", __FILE__) List end @subcommands.register(:outdated) do require_relative "outdated" Outdated end @subcommands.register(:remove) do require File.expand_path("../remove", __FILE__) Remove end @subcommands.register(:prune) do require_relative "prune" Prune end @subcommands.register(:repackage) do require File.expand_path("../repackage", __FILE__) Repackage end @subcommands.register(:update) do require_relative "update" Update end end def execute if @main_args.include?("-h") || @main_args.include?("--help") # Print the help for all the box commands. return help end # If we reached this far then we must have a subcommand. If not, # then we also just print the help and exit. command_class = @subcommands.get(@sub_command.to_sym) if @sub_command return help if !command_class || !@sub_command @logger.debug("Invoking command class: #{command_class} #{@sub_args.inspect}") # Initialize and execute the command class command_class.new(@sub_args, @env).execute end # Prints the help out for this command def help opts = OptionParser.new do |opts| opts.banner = "Usage: vagrant box []" opts.separator "" opts.separator "Available subcommands:" # Add the available subcommands as separators in order to print them # out as well. keys = [] @subcommands.each { |key, value| keys << key.to_s } keys.sort.each do |key| opts.separator " #{key}" end opts.separator "" opts.separator "For help on any individual subcommand run `vagrant box -h`" end @env.ui.info(opts.help, prefix: false) end end end end end vagrant-2.0.2/plugins/commands/box/command/update.rb000066400000000000000000000131231323370221500224520ustar00rootroot00000000000000require 'optparse' require_relative 'download_mixins' module VagrantPlugins module CommandBox module Command class Update < Vagrant.plugin("2", :command) include DownloadMixins def execute options = {} download_options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant box update [options]" o.separator "" o.separator "Updates the box that is in use in the current Vagrant environment," o.separator "if there any updates available. This does not destroy/recreate the" o.separator "machine, so you'll have to do that to see changes." o.separator "" o.separator "To update a specific box (not tied to a Vagrant environment), use the" o.separator "--box flag." o.separator "" o.separator "Options:" o.separator "" o.on("--box BOX", String, "Update a specific box") do |b| options[:box] = b end o.on("--provider PROVIDER", String, "Update box with specific provider") do |p| options[:provider] = p.to_sym end o.on("-f", "--force", "Overwrite an existing box if it exists") do |f| options[:force] = f end build_download_options(o, download_options) end argv = parse_options(opts) return if !argv if options[:box] update_specific(options[:box], options[:provider], download_options, options[:force]) else update_vms(argv, options[:provider], download_options, options[:force]) end 0 end def update_specific(name, provider, download_options, force) boxes = {} @env.boxes.all.each do |n, v, p| boxes[n] ||= {} boxes[n][p] ||= [] boxes[n][p] << v end if !boxes[name] raise Vagrant::Errors::BoxNotFound, name: name.to_s end if !provider if boxes[name].length > 1 raise Vagrant::Errors::BoxUpdateMultiProvider, name: name.to_s, providers: boxes[name].keys.map(&:to_s).sort.join(", ") end provider = boxes[name].keys.first elsif !boxes[name][provider] raise Vagrant::Errors::BoxNotFoundWithProvider, name: name.to_s, provider: provider.to_s, providers: boxes[name].keys.map(&:to_s).sort.join(", ") end to_update = [ [name, provider, boxes[name][provider].sort.last], ] to_update.each do |n, p, v| box = @env.boxes.find(n, p, v) box_update(box, "> #{v}", @env.ui, download_options, force) end end def update_vms(argv, provider, download_options, force) machines = {} with_target_vms(argv, provider: provider) do |machine| if !machine.config.vm.box machine.ui.output(I18n.t( "vagrant.errors.box_update_no_name")) next end if !machine.box machine.ui.output(I18n.t( "vagrant.errors.box_update_no_box", name: machine.config.vm.box)) next end name = machine.box.name provider = machine.box.provider version = machine.config.vm.box_version || machine.box.version machines["#{name}_#{provider}_#{version}"] = machine end machines.each do |_, machine| box = machine.box version = machine.config.vm.box_version # Get download options from machine configuration if not specified # on the command line. download_options[:ca_cert] ||= machine.config.vm.box_download_ca_cert download_options[:ca_path] ||= machine.config.vm.box_download_ca_path download_options[:client_cert] ||= machine.config.vm.box_download_client_cert if download_options[:insecure].nil? download_options[:insecure] = machine.config.vm.box_download_insecure end box_update(box, version, machine.ui, download_options, force) end end def box_update(box, version, ui, download_options, force) ui.output(I18n.t("vagrant.box_update_checking", name: box.name)) ui.detail("Latest installed version: #{box.version}") ui.detail("Version constraints: #{version}") ui.detail("Provider: #{box.provider}") update = box.has_update?(version, download_options: download_options) if !update ui.success(I18n.t( "vagrant.box_up_to_date_single", name: box.name, version: box.version)) return end ui.output(I18n.t( "vagrant.box_updating", name: update[0].name, provider: update[2].name, old: box.version, new: update[1].version)) @env.action_runner.run(Vagrant::Action.action_box_add, { box_url: box.metadata_url, box_provider: update[2].name, box_version: update[1].version, ui: ui, box_force: force, box_client_cert: download_options[:client_cert], box_download_ca_cert: download_options[:ca_cert], box_download_ca_path: download_options[:ca_path], box_download_insecure: download_options[:insecure] }) end end end end end vagrant-2.0.2/plugins/commands/box/plugin.rb000066400000000000000000000005151323370221500210510ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandBox class Plugin < Vagrant.plugin("2") name "box command" description "The `box` command gives you a way to manage boxes." command("box") do require File.expand_path("../command/root", __FILE__) Command::Root end end end end vagrant-2.0.2/plugins/commands/cap/000077500000000000000000000000001323370221500172005ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/cap/command.rb000066400000000000000000000041541323370221500211470ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandCap class Command < Vagrant.plugin("2", :command) def self.synopsis "checks and executes capability" end def execute options = {} options[:check] = false opts = OptionParser.new do |o| o.banner = "Usage: vagrant cap [options] TYPE NAME [args]" o.separator "" o.separator "This is an advanced command. If you don't know what this" o.separator "does and you aren't explicitly trying to use it, you probably" o.separator "don't want to use this." o.separator "" o.separator "This command checks or executes arbitrary capabilities that" o.separator "Vagrant has for hosts, guests, and providers." o.separator "" o.separator "Options:" o.separator "" o.on("--check", "Only checks for a capability, does not execute") do |f| options[:check] = f end end # Parse the options argv = parse_options(opts) return if !argv if argv.length < 2 raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp end type = argv.shift.to_sym name = argv.shift.to_sym # Get the proper capability host to check cap_host = nil if type == :host cap_host = @env.host else with_target_vms([]) do |vm| cap_host = case type when :provider vm.provider when :guest vm.guest else raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp end end end # If we're just checking, then just return exit codes if options[:check] return 0 if cap_host.capability?(name) return 1 end # Otherwise, call it cap_host.capability(name, *argv) # Success, exit status 0 0 end end end end vagrant-2.0.2/plugins/commands/cap/plugin.rb000066400000000000000000000005361323370221500210270ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandCap class Plugin < Vagrant.plugin("2") name "cap command" description <<-DESC The `cap` command checks and executes arbitrary capabilities. DESC command("cap", primary: false) do require_relative "command" Command end end end end vagrant-2.0.2/plugins/commands/destroy/000077500000000000000000000000001323370221500201265ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/destroy/command.rb000066400000000000000000000040361323370221500220740ustar00rootroot00000000000000module VagrantPlugins module CommandDestroy class Command < Vagrant.plugin("2", :command) def self.synopsis "stops and deletes all traces of the vagrant machine" end def execute options = {} options[:force] = false opts = OptionParser.new do |o| o.banner = "Usage: vagrant destroy [options] [name|id]" o.separator "" o.separator "Options:" o.separator "" o.on("-f", "--force", "Destroy without confirmation.") do |f| options[:force] = f end o.on("--[no-]parallel", "Enable or disable parallelism if provider supports it (automatically enables force)") do |p| options[:parallel] = p end end # Parse the options argv = parse_options(opts) return if !argv if options[:parallel] && !options[:force] @env.ui.warn(I18n.t("vagrant.commands.destroy.warning")) sleep(5) options[:force] = true end @logger.debug("'Destroy' each target VM...") machines = [] init_states = {} declined = 0 @env.batch(options[:parallel]) do |batch| with_target_vms(argv, reverse: true) do |vm| # gather states to be checked after destroy init_states[vm.name] = vm.state.id machines << vm batch.action(vm, :destroy, force_confirm_destroy: options[:force]) end end machines.each do |m| if m.state.id == init_states[m.name] declined += 1 end end # Nothing was declined return 0 if declined == 0 # Everything was declined, and all states are `not_created` return 0 if declined == machines.length && declined == init_states.values.count(:not_created) # Everything was declined, state was not changed return 1 if declined == machines.length # Some was declined return 2 end end end end vagrant-2.0.2/plugins/commands/destroy/plugin.rb000066400000000000000000000007221323370221500217520ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandDestroy class Plugin < Vagrant.plugin("2") name "destroy command" description <<-DESC The `destroy` command deletes and removes the files and record of your virtual machines. All data is lost and a new VM will have to be created using `up` DESC command("destroy") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-2.0.2/plugins/commands/global-status/000077500000000000000000000000001323370221500212165ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/global-status/command.rb000066400000000000000000000050651323370221500231670ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandGlobalStatus class Command < Vagrant.plugin("2", :command) def self.synopsis "outputs status Vagrant environments for this user" end def execute options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant global-status" o.separator "" o.on("--prune", "Prune invalid entries.") do |p| options[:prune] = true end end # Parse the options argv = parse_options(opts) return if !argv columns = [ ["id", :id], ["name", :name], ["provider", :provider], ["state", :state], ["directory", :vagrantfile_path], ] widths = {} widths[:id] = 8 widths[:name] = 6 widths[:provider] = 6 widths[:state] = 6 widths[:vagrantfile_path] = 35 entries = [] prune = [] @env.machine_index.each do |entry| # If we're pruning and this entry is invalid, skip it # and prune it later. if options[:prune] && !entry.valid?(@env.home_path) prune << entry next end entries << entry columns.each do |_, method| # Skip the id next if method == :id widths[method] ||= 0 cur = entry.send(method).to_s.length widths[method] = cur if cur > widths[method] end end # Prune all the entries to prune prune.each do |entry| deletable = @env.machine_index.get(entry.id) @env.machine_index.delete(deletable) if deletable end total_width = 0 columns.each do |header, method| header = header.ljust(widths[method]) if widths[method] @env.ui.info("#{header} ", new_line: false) total_width += header.length + 1 end @env.ui.info("") @env.ui.info("-" * total_width) if entries.empty? @env.ui.info(I18n.t("vagrant.global_status_none")) return 0 end entries.each do |entry| columns.each do |_, method| v = entry.send(method).to_s v = v[0...7] if method == :id v = v.ljust(widths[method]) if widths[method] @env.ui.info("#{v} ", new_line: false) end @env.ui.info("") end @env.ui.info(" \n" + I18n.t("vagrant.global_status_footer")) # Success, exit status 0 0 end end end end vagrant-2.0.2/plugins/commands/global-status/plugin.rb000066400000000000000000000007151323370221500230440ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandGlobalStatus class Plugin < Vagrant.plugin("2") name "global-status command" description <<-DESC The `global-status` command shows what the running state (running/saved/..) is of all the Vagrant environments known to the system. DESC command("global-status") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-2.0.2/plugins/commands/halt/000077500000000000000000000000001323370221500173655ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/halt/command.rb000066400000000000000000000016601323370221500213330ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandHalt class Command < Vagrant.plugin("2", :command) def self.synopsis "stops the vagrant machine" end def execute options = {} options[:force] = false opts = OptionParser.new do |o| o.banner = "Usage: vagrant halt [options] [name|id]" o.separator "" o.separator "Options:" o.separator "" o.on("-f", "--force", "Force shut down (equivalent of pulling power)") do |f| options[:force] = f end end # Parse the options argv = parse_options(opts) return if !argv @logger.debug("Halt command: #{argv.inspect} #{options.inspect}") with_target_vms(argv, reverse: true) do |vm| vm.action(:halt, force_halt: options[:force]) end # Success, exit status 0 0 end end end end vagrant-2.0.2/plugins/commands/halt/plugin.rb000066400000000000000000000006151323370221500212120ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandHalt class Plugin < Vagrant.plugin("2") name "halt command" description <<-DESC The `halt` command shuts your virtual machine down forcefully. The command `up` recreates it. DESC command("halt") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-2.0.2/plugins/commands/help/000077500000000000000000000000001323370221500173655ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/help/command.rb000066400000000000000000000004731323370221500213340ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandHelp class Command < Vagrant.plugin("2", :command) def self.synopsis "shows the help for a subcommand" end def execute return @env.cli([]) if @argv.empty? @env.cli([@argv[0], "-h"]) end end end end vagrant-2.0.2/plugins/commands/help/plugin.rb000066400000000000000000000005361323370221500212140ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandHelp class Plugin < Vagrant.plugin("2") name "help command" description <<-DESC The `help` command shows help for the given command. DESC command("help") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-2.0.2/plugins/commands/init/000077500000000000000000000000001323370221500174005ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/init/command.rb000066400000000000000000000063271323370221500213530ustar00rootroot00000000000000require 'optparse' require 'vagrant/util/template_renderer' module VagrantPlugins module CommandInit class Command < Vagrant.plugin("2", :command) def self.synopsis "initializes a new Vagrant environment by creating a Vagrantfile" end def execute options = { force: false, minimal: false, output: "Vagrantfile", template: nil } opts = OptionParser.new do |o| o.banner = "Usage: vagrant init [options] [name [url]]" o.separator "" o.separator "Options:" o.separator "" o.on("--box-version VERSION", "Version of the box to add") do |f| options[:box_version] = f end o.on("-f", "--force", "Overwrite existing Vagrantfile") do |f| options[:force] = f end o.on("-m", "--minimal", "Use minimal Vagrantfile template (no help comments). Ignored with --template") do |m| options[:minimal] = m end o.on("--output FILE", String, "Output path for the box. '-' for stdout") do |output| options[:output] = output end o.on("--template FILE", String, "Path to custom Vagrantfile template") do |template| options[:template] = template end end # Parse the options argv = parse_options(opts) return if !argv save_path = nil if options[:output] != "-" save_path = Pathname.new(options[:output]).expand_path(@env.cwd) save_path.delete if save_path.exist? && options[:force] raise Vagrant::Errors::VagrantfileExistsError if save_path.exist? end # Determine the template and template root to use template_root = "" if options[:template].nil? options[:template] = "Vagrantfile" if options[:minimal] options[:template] = "Vagrantfile.min" end template_root = ::Vagrant.source_root.join("templates/commands/init") end # Strip the .erb extension off the template if the user passes it in options[:template] = options[:template].chomp(".erb") # Make sure the template actually exists full_template_path = Vagrant::Util::TemplateRenderer.new(options[:template], template_root: template_root).full_template_path if !File.file?(full_template_path) raise Vagrant::Errors::VagrantfileTemplateNotFoundError, path: full_template_path end contents = Vagrant::Util::TemplateRenderer.render(options[:template], box_name: argv[0] || "base", box_url: argv[1], box_version: options[:box_version], template_root: template_root ) if save_path # Write out the contents begin save_path.open("w+") do |f| f.write(contents) end rescue Errno::EACCES raise Vagrant::Errors::VagrantfileWriteError end @env.ui.info(I18n.t("vagrant.commands.init.success"), prefix: false) else @env.ui.info(contents, prefix: false) end # Success, exit status 0 0 end end end end vagrant-2.0.2/plugins/commands/init/plugin.rb000066400000000000000000000006061323370221500212250ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandInit class Plugin < Vagrant.plugin("2") name "init command" description <<-DESC The `init` command sets up your working directory to be a Vagrant-managed environment. DESC command("init") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-2.0.2/plugins/commands/list-commands/000077500000000000000000000000001323370221500212075ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/list-commands/command.rb000066400000000000000000000022361323370221500231550ustar00rootroot00000000000000require "optparse" module VagrantPlugins module CommandListCommands class Command < Vagrant.plugin("2", :command) def self.synopsis "outputs all available Vagrant subcommands, even non-primary ones" end def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant list-commands" end argv = parse_options(opts) return if !argv # Add the available subcommands as separators in order to print them # out as well. commands = {} longest = 0 Vagrant.plugin("2").manager.commands.each do |key, data| key = key.to_s klass = data[0].call commands[key] = klass.synopsis longest = key.length if key.length > longest end command_output = [] commands.keys.sort.each do |key| command_output << "#{key.ljust(longest+2)} #{commands[key]}" @env.ui.machine("cli-command", key.dup) end @env.ui.info( I18n.t("vagrant.list_commands", list: command_output.join("\n"))) # Success, exit status 0 0 end end end end vagrant-2.0.2/plugins/commands/list-commands/plugin.rb000066400000000000000000000006421323370221500230340ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandListCommands class Plugin < Vagrant.plugin("2") name "list-commands command" description <<-DESC The `list-commands` command will list all commands that Vagrant understands, even hidden ones. DESC command("list-commands", primary: false) do require_relative "command" Command end end end end vagrant-2.0.2/plugins/commands/login/000077500000000000000000000000001323370221500175455ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/login/client.rb000066400000000000000000000167451323370221500213650ustar00rootroot00000000000000require "rest_client" require "vagrant/util/downloader" require "vagrant/util/presence" module VagrantPlugins module LoginCommand class Client APP = "app".freeze include Vagrant::Util::Presence attr_accessor :username_or_email attr_accessor :password attr_reader :two_factor_default_delivery_method attr_reader :two_factor_delivery_methods # Initializes a login client with the given Vagrant::Environment. # # @param [Vagrant::Environment] env def initialize(env) @logger = Log4r::Logger.new("vagrant::login::client") @env = env end # Removes the token, effectively logging the user out. def clear_token @logger.info("Clearing token") token_path.delete if token_path.file? end # Checks if the user is logged in by verifying their authentication # token. # # @return [Boolean] def logged_in? token = self.token return false if !token with_error_handling do url = "#{Vagrant.server_url}/api/v1/authenticate" + "?access_token=#{token}" RestClient.get(url, content_type: :json) true end rescue Errors::Unauthorized false end # Login logs a user in and returns the token for that user. The token # is _not_ stored unless {#store_token} is called. # # @param [String] description # @param [String] code # @return [String] token The access token, or nil if auth failed. def login(description: nil, code: nil) @logger.info("Logging in '#{username_or_email}'") response = post( "/api/v1/authenticate", { user: { login: username_or_email, password: password }, token: { description: description }, two_factor: { code: code } } ) response["token"] end # Requests a 2FA code # @param [String] delivery_method def request_code(delivery_method) @env.ui.warn("Requesting 2FA code via #{delivery_method.upcase}...") response = post( "/api/v1/two-factor/request-code", { user: { login: username_or_email, password: password }, two_factor: { delivery_method: delivery_method.downcase } } ) two_factor = response['two_factor'] obfuscated_destination = two_factor['obfuscated_destination'] @env.ui.success("2FA code sent to #{obfuscated_destination}.") end # Issues a post to a Vagrant Cloud path with the given payload. # @param [String] path # @param [Hash] payload # @return [Hash] response data def post(path, payload) with_error_handling do url = File.join(Vagrant.server_url, path) proxy = nil proxy ||= ENV["HTTPS_PROXY"] || ENV["https_proxy"] proxy ||= ENV["HTTP_PROXY"] || ENV["http_proxy"] RestClient.proxy = proxy response = RestClient::Request.execute( method: :post, url: url, payload: JSON.dump(payload), proxy: proxy, headers: { accept: :json, content_type: :json, user_agent: Vagrant::Util::Downloader::USER_AGENT, }, ) JSON.load(response.to_s) end end # Stores the given token locally, removing any previous tokens. # # @param [String] token def store_token(token) @logger.info("Storing token in #{token_path}") token_path.open("w") do |f| f.write(token) end nil end # Reads the access token if there is one. This will first read the # `VAGRANT_CLOUD_TOKEN` environment variable and then fallback to the stored # access token on disk. # # @return [String] def token if present?(ENV["VAGRANT_CLOUD_TOKEN"]) && token_path.exist? @env.ui.warn <<-EOH.strip Vagrant detected both the VAGRANT_CLOUD_TOKEN environment variable and a Vagrant login token are present on this system. The VAGRANT_CLOUD_TOKEN environment variable takes precedence over the locally stored token. To remove this error, either unset the VAGRANT_CLOUD_TOKEN environment variable or remove the login token stored on disk: ~/.vagrant.d/data/vagrant_login_token EOH end if present?(ENV["VAGRANT_CLOUD_TOKEN"]) @logger.debug("Using authentication token from environment variable") return ENV["VAGRANT_CLOUD_TOKEN"] end if token_path.exist? @logger.debug("Using authentication token from disk at #{token_path}") return token_path.read.strip end if present?(ENV["ATLAS_TOKEN"]) @logger.warn("ATLAS_TOKEN detected within environment. Using ATLAS_TOKEN in place of VAGRANT_CLOUD_TOKEN.") return ENV["ATLAS_TOKEN"] end @logger.debug("No authentication token in environment or #{token_path}") nil end protected def with_error_handling(&block) yield rescue RestClient::Unauthorized @logger.debug("Unauthorized!") raise Errors::Unauthorized rescue RestClient::BadRequest => e @logger.debug("Bad request:") @logger.debug(e.message) @logger.debug(e.backtrace.join("\n")) parsed_response = JSON.parse(e.response) errors = parsed_response["errors"].join("\n") raise Errors::ServerError, errors: errors rescue RestClient::NotAcceptable => e @logger.debug("Got unacceptable response:") @logger.debug(e.message) @logger.debug(e.backtrace.join("\n")) parsed_response = JSON.parse(e.response) if two_factor = parsed_response['two_factor'] store_two_factor_information two_factor if two_factor_default_delivery_method != APP request_code two_factor_default_delivery_method end raise Errors::TwoFactorRequired end begin errors = parsed_response["errors"].join("\n") raise Errors::ServerError, errors: errors rescue JSON::ParserError; end raise "An unexpected error occurred: #{e.inspect}" rescue SocketError @logger.info("Socket error") raise Errors::ServerUnreachable, url: Vagrant.server_url.to_s end def token_path @env.data_dir.join("vagrant_login_token") end def store_two_factor_information(two_factor) @two_factor_default_delivery_method = two_factor['default_delivery_method'] @two_factor_delivery_methods = two_factor['delivery_methods'] @env.ui.warn "2FA is enabled for your account." if two_factor_default_delivery_method == APP @env.ui.info "Enter the code from your authenticator." else @env.ui.info "Default method is " \ "'#{two_factor_default_delivery_method}'." end other_delivery_methods = two_factor_delivery_methods - [APP] if other_delivery_methods.any? other_delivery_methods_sentence = other_delivery_methods .map { |word| "'#{word}'" } .join(' or ') @env.ui.info "You can also type #{other_delivery_methods_sentence} " \ "to request a new code." end end end end end vagrant-2.0.2/plugins/commands/login/command.rb000066400000000000000000000077561323370221500215270ustar00rootroot00000000000000require 'socket' module VagrantPlugins module LoginCommand class Command < Vagrant.plugin("2", "command") def self.synopsis "log in to HashiCorp's Vagrant Cloud" end def execute options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant login" o.separator "" o.on("-c", "--check", "Only checks if you're logged in") do |c| options[:check] = c end o.on("-d", "--description DESCRIPTION", String, "Description for the Vagrant Cloud token") do |t| options[:description] = t end o.on("-k", "--logout", "Logs you out if you're logged in") do |k| options[:logout] = k end o.on("-t", "--token TOKEN", String, "Set the Vagrant Cloud token") do |t| options[:token] = t end o.on("-u", "--username USERNAME_OR_EMAIL", String, "Specify your Vagrant Cloud username or email address") do |t| options[:login] = t end end # Parse the options argv = parse_options(opts) return if !argv @client = Client.new(@env) @client.username_or_email = options[:login] # Determine what task we're actually taking based on flags if options[:check] return execute_check elsif options[:logout] return execute_logout elsif options[:token] return execute_token(options[:token]) end # Let the user know what is going on. @env.ui.output(I18n.t("login_command.command_header") + "\n") # If it is a private cloud installation, show that if Vagrant.server_url != Vagrant::DEFAULT_SERVER_URL @env.ui.output("Vagrant Cloud URL: #{Vagrant.server_url}") end # Ask for the username if @client.username_or_email @env.ui.output("Vagrant Cloud username or email: #{@client.username_or_email}") end until @client.username_or_email @client.username_or_email = @env.ui.ask("Vagrant Cloud username or email: ") end until @client.password @client.password = @env.ui.ask("Password (will be hidden): ", echo: false) end description = options[:description] if description @env.ui.output("Token description: #{description}") else description_default = "Vagrant login from #{Socket.gethostname}" until description description = @env.ui.ask("Token description (Defaults to #{description_default.inspect}): ") end description = description_default if description.empty? end code = nil begin token = @client.login(description: description, code: code) rescue Errors::TwoFactorRequired until code code = @env.ui.ask("2FA code: ") if @client.two_factor_delivery_methods.include?(code.downcase) delivery_method, code = code, nil @client.request_code delivery_method end end retry end @client.store_token(token) @env.ui.success(I18n.t("login_command.logged_in")) 0 end def execute_check if @client.logged_in? @env.ui.success(I18n.t("login_command.check_logged_in")) return 0 else @env.ui.error(I18n.t("login_command.check_not_logged_in")) return 1 end end def execute_logout @client.clear_token @env.ui.success(I18n.t("login_command.logged_out")) return 0 end def execute_token(token) @client.store_token(token) @env.ui.success(I18n.t("login_command.token_saved")) if @client.logged_in? @env.ui.success(I18n.t("login_command.check_logged_in")) return 0 else @env.ui.error(I18n.t("login_command.invalid_token")) return 1 end end end end end vagrant-2.0.2/plugins/commands/login/errors.rb000066400000000000000000000007361323370221500214140ustar00rootroot00000000000000module VagrantPlugins module LoginCommand module Errors class Error < Vagrant::Errors::VagrantError error_namespace("login_command.errors") end class ServerError < Error error_key(:server_error) end class ServerUnreachable < Error error_key(:server_unreachable) end class Unauthorized < Error error_key(:unauthorized) end class TwoFactorRequired < Error end end end end vagrant-2.0.2/plugins/commands/login/locales/000077500000000000000000000000001323370221500211675ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/login/locales/en.yml000066400000000000000000000024131323370221500223140ustar00rootroot00000000000000en: login_command: errors: server_error: |- The Vagrant Cloud server responded with a not-OK response: %{errors} server_unreachable: |- The Vagrant Cloud server is not currently accepting connections. Please check your network connection and try again later. unauthorized: |- Invalid username or password. Please try again. check_logged_in: |- You are already logged in. check_not_logged_in: |- You are not currently logged in. Please run `vagrant login` and provide your login information to authenticate. command_header: |- In a moment we will ask for your username and password to HashiCorp's Vagrant Cloud. After authenticating, we will store an access token locally on disk. Your login details will be transmitted over a secure connection, and are never stored on disk locally. If you do not have an Vagrant Cloud account, sign up at https://www.vagrantcloud.com invalid_login: |- Invalid username or password. Please try again. invalid_token: |- Invalid token. Please try again. logged_in: |- You are now logged in. logged_out: |- You are logged out. token_saved: |- The token was successfully saved. vagrant-2.0.2/plugins/commands/login/middleware/000077500000000000000000000000001323370221500216625ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/login/middleware/add_authentication.rb000066400000000000000000000023211323370221500260340ustar00rootroot00000000000000require "cgi" require "uri" require_relative "../client" module VagrantPlugins module LoginCommand class AddAuthentication ALLOWED_AUTHENTICATION_HOSTS = %w[ app.vagrantup.com atlas.hashicorp.com vagrantcloud.com ].freeze def initialize(app, env) @app = app end def call(env) client = Client.new(env[:env]) token = client.token if token && Vagrant.server_url server_uri = URI.parse(Vagrant.server_url) env[:box_urls].map! do |url| u = URI.parse(url) replace = u.host == server_uri.host if !replace if ALLOWED_AUTHENTICATION_HOSTS.include?(u.host) && ALLOWED_AUTHENTICATION_HOSTS.include?(server_uri.host) replace = true end end if replace 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 end end end vagrant-2.0.2/plugins/commands/login/plugin.rb000066400000000000000000000015511323370221500213720ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module LoginCommand autoload :Client, File.expand_path("../client", __FILE__) autoload :Errors, File.expand_path("../errors", __FILE__) class Plugin < Vagrant.plugin("2") name "vagrant-login" description <<-DESC Provides the login command and internal API access to Vagrant Cloud. DESC command(:login) do require_relative "command" init! Command end action_hook(:cloud_authenticated_boxes, :authenticate_box_url) do |hook| require_relative "middleware/add_authentication" hook.prepend(AddAuthentication) end protected def self.init! return if defined?(@_init) I18n.load_path << File.expand_path("../locales/en.yml", __FILE__) I18n.reload! @_init = true end end end end vagrant-2.0.2/plugins/commands/package/000077500000000000000000000000001323370221500200305ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/package/command.rb000066400000000000000000000052771323370221500220060ustar00rootroot00000000000000require 'optparse' require 'securerandom' module VagrantPlugins module CommandPackage class Command < Vagrant.plugin("2", :command) def self.synopsis "packages a running vagrant environment into a box" end def execute options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant package [options] [name|id]" o.separator "" o.separator "Options:" o.separator "" o.on("--base NAME", "Name of a VM in VirtualBox to package as a base box (VirtualBox Only)") do |b| options[:base] = b end o.on("--output NAME", "Name of the file to output") do |output| options[:output] = output end o.on("--include FILE,FILE..", Array, "Comma separated additional files to package with the box") do |i| options[:include] = i end o.on("--vagrantfile FILE", "Vagrantfile to package with the box") do |v| options[:vagrantfile] = v end end # Parse the options argv = parse_options(opts) return if !argv @logger.debug("package options: #{options.inspect}") if options[:base] package_base(options) else package_target(argv[0], options) end # Success, exit status 0 0 end protected def package_base(options) # XXX: This whole thing is hardcoded and very temporary. The whole # `vagrant package --base` process is deprecated for something much # better in the future. We just hardcode this to keep VirtualBox working # for now. provider = Vagrant.plugin("2").manager.providers[:virtualbox] tmp_data_directory = File.join(@env.tmp_path, SecureRandom.uuid) FileUtils.mkdir_p(tmp_data_directory) begin vm = Vagrant::Machine.new( options[:base], :virtualbox, provider[0], nil, provider[1], @env.vagrantfile.config, Pathname.new(tmp_data_directory), nil, @env, @env.vagrantfile, true) @logger.debug("Packaging base VM: #{vm.name}") package_vm(vm, options) ensure FileUtils.rm_rf(tmp_data_directory) end end def package_target(name, options) with_target_vms(name, single_target: true) do |vm| @logger.debug("Packaging VM: #{vm.name}") package_vm(vm, options) end end def package_vm(vm, options) opts = options.inject({}) do |acc, data| k,v = data acc["package.#{k}"] = v acc end vm.action(:package, opts) end end end end vagrant-2.0.2/plugins/commands/package/plugin.rb000066400000000000000000000006421323370221500216550ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandPackage class Plugin < Vagrant.plugin("2") name "package command" description <<-DESC The `package` command will take a previously existing Vagrant environment and package it into a box file. DESC command("package") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-2.0.2/plugins/commands/plugin/000077500000000000000000000000001323370221500177335ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/plugin/action.rb000066400000000000000000000041471323370221500215430ustar00rootroot00000000000000require "pathname" require "vagrant/action/builder" module VagrantPlugins module CommandPlugin module Action # This middleware sequence will remove all plugins. def self.action_expunge Vagrant::Action::Builder.new.tap do |b| b.use ExpungePlugins end end # This middleware sequence will install a plugin. def self.action_install Vagrant::Action::Builder.new.tap do |b| b.use InstallGem end end # This middleware sequence licenses paid addons. def self.action_license Vagrant::Action::Builder.new.tap do |b| b.use PluginExistsCheck b.use LicensePlugin end end # This middleware sequence will list all installed plugins. def self.action_list Vagrant::Action::Builder.new.tap do |b| b.use ListPlugins end end # This middleware sequence will repair installed plugins. def self.action_repair Vagrant::Action::Builder.new.tap do |b| b.use RepairPlugins end end # This middleware sequence will uninstall a plugin. def self.action_uninstall Vagrant::Action::Builder.new.tap do |b| b.use PluginExistsCheck b.use UninstallPlugin end end # This middleware sequence will update a plugin. def self.action_update Vagrant::Action::Builder.new.tap do |b| b.use UpdateGems end end # The autoload farm action_root = Pathname.new(File.expand_path("../action", __FILE__)) autoload :ExpungePlugins, action_root.join("expunge_plugins") autoload :InstallGem, action_root.join("install_gem") autoload :LicensePlugin, action_root.join("license_plugin") autoload :ListPlugins, action_root.join("list_plugins") autoload :PluginExistsCheck, action_root.join("plugin_exists_check") autoload :RepairPlugins, action_root.join("repair_plugins") autoload :UninstallPlugin, action_root.join("uninstall_plugin") autoload :UpdateGems, action_root.join("update_gems") end end end vagrant-2.0.2/plugins/commands/plugin/action/000077500000000000000000000000001323370221500212105ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/plugin/action/expunge_plugins.rb000066400000000000000000000034461323370221500247600ustar00rootroot00000000000000require "vagrant/plugin/manager" module VagrantPlugins module CommandPlugin module Action # This middleware removes user installed plugins by # removing: # * ~/.vagrant.d/plugins.json # * ~/.vagrant.d/gems # Usage should be restricted to when a repair is # unsuccessful and the only reasonable option remaining # is to re-install all plugins class ExpungePlugins def initialize(app, env) @app = app end def call(env) if !env[:force] result = nil attempts = 0 while attempts < 5 && result.nil? attempts += 1 result = env[:ui].ask( I18n.t("vagrant.commands.plugin.expunge_confirm") + " [N]: " ) result = result.to_s.downcase.strip result = "n" if result.empty? if !["y", "yes", "n", "no"].include?(result) result = nil env[:ui].error("Please answer Y or N") else result = result[0,1] end end if result != 'y' abort_action = true end end if !abort_action plugins_json = File.join(env[:home_path], "plugins.json") plugins_gems = env[:gems_path] if File.exist?(plugins_json) FileUtils.rm(plugins_json) end if File.directory?(plugins_gems) FileUtils.rm_rf(plugins_gems) end env[:ui].info(I18n.t("vagrant.commands.plugin.expunge_complete")) @app.call(env) else env[:ui].info(I18n.t("vagrant.commands.plugin.expunge_aborted")) end end end end end end vagrant-2.0.2/plugins/commands/plugin/action/install_gem.rb000066400000000000000000000051011323370221500240300ustar00rootroot00000000000000require "log4r" require "vagrant/plugin/manager" require "vagrant/util/platform" module VagrantPlugins module CommandPlugin module Action # This action takes the `:plugin_name` variable in the environment # and installs it using the RubyGems API. class InstallGem def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::plugins::plugincommand::installgem") end def call(env) entrypoint = env[:plugin_entry_point] plugin_name = env[:plugin_name] sources = env[:plugin_sources] version = env[:plugin_version] # Install the gem plugin_name_label = plugin_name plugin_name_label += " --version '#{version}'" if version env[:ui].info(I18n.t("vagrant.commands.plugin.installing", name: plugin_name_label)) manager = Vagrant::Plugin::Manager.instance plugin_spec = manager.install_plugin( plugin_name, version: version, require: entrypoint, sources: sources, verbose: !!env[:plugin_verbose], ) # Record it so we can uninstall if something goes wrong @installed_plugin_name = plugin_spec.name # Tell the user env[:ui].success(I18n.t("vagrant.commands.plugin.installed", name: plugin_spec.name, version: plugin_spec.version.to_s)) # If the plugin's spec includes a post-install message display it post_install_message = plugin_spec.post_install_message if post_install_message if post_install_message.is_a?(Array) post_install_message = post_install_message.join(" ") end env[:ui].info(I18n.t("vagrant.commands.plugin.post_install", name: plugin_spec.name, message: post_install_message.to_s)) end # Continue @app.call(env) end def recover(env) # If any error happens, we uninstall it and remove it from # the state file. We can only do this if we successfully installed # the gem in the first place. if @installed_plugin_name new_env = env.dup new_env.delete(:interrupted) new_env[:plugin_name] = @installed_plugin_name new_env[:action_runner].run(Action.action_uninstall, new_env) end end end end end end vagrant-2.0.2/plugins/commands/plugin/action/license_plugin.rb000066400000000000000000000025551323370221500245440ustar00rootroot00000000000000require "fileutils" require "pathname" require "rubygems" require "set" require "log4r" module VagrantPlugins module CommandPlugin module Action # This middleware licenses a plugin by copying the license file to # the proper place. class LicensePlugin def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::plugins::plugincommand::license") end def call(env) # Verify the license file exists license_file = Pathname.new(env[:plugin_license_path]) if !license_file.file? raise Vagrant::Errors::PluginInstallLicenseNotFound, name: env[:plugin_name], path: license_file.to_s end # Copy it in. final_path = env[:home_path].join("license-#{env[:plugin_name]}.lic") @logger.info("Copying license from: #{license_file}") @logger.info("Copying license to: #{final_path}") env[:ui].info(I18n.t("vagrant.commands.plugin.installing_license", name: env[:plugin_name])) FileUtils.cp(license_file, final_path) # Installed! env[:ui].success(I18n.t("vagrant.commands.plugin.installed_license", name: env[:plugin_name])) @app.call(env) end end end end end vagrant-2.0.2/plugins/commands/plugin/action/list_plugins.rb000066400000000000000000000043231323370221500242530ustar00rootroot00000000000000require "vagrant/plugin/manager" module VagrantPlugins module CommandPlugin module Action # This middleware lists all the installed plugins. # # This is a bit more complicated than simply listing installed # gems or what is in the state file as installed. Instead, this # actually compares installed gems with what the state file claims # is installed, and outputs the appropriate truly installed # plugins. class ListPlugins def initialize(app, env) @app = app end def call(env) manager = Vagrant::Plugin::Manager.instance plugins = manager.installed_plugins specs = Hash[ manager.installed_specs.map do |spec| [spec.name, spec] end ] # Output! if specs.empty? env[:ui].info(I18n.t("vagrant.commands.plugin.no_plugins")) return @app.call(env) end plugins.each do |plugin_name, plugin| spec = specs[plugin_name] next if spec.nil? system = "" system = ", system" if plugin && plugin["system"] env[:ui].info "#{spec.name} (#{spec.version}#{system})" env[:ui].machine("plugin-name", spec.name) env[:ui].machine( "plugin-version", "#{spec.version}#{system}", target: spec.name) if plugin["gem_version"] && plugin["gem_version"] != "" env[:ui].info(I18n.t( "vagrant.commands.plugin.plugin_version", version: plugin["gem_version"])) env[:ui].machine( "plugin-version-constraint", plugin["gem_version"], target: spec.name) end if plugin["require"] && plugin["require"] != "" env[:ui].info(I18n.t( "vagrant.commands.plugin.plugin_require", require: plugin["require"])) env[:ui].machine( "plugin-custom-entrypoint", plugin["require"], target: spec.name) end end @app.call(env) end end end end end vagrant-2.0.2/plugins/commands/plugin/action/plugin_exists_check.rb000066400000000000000000000012131323370221500255640ustar00rootroot00000000000000require "vagrant/plugin/manager" module VagrantPlugins module CommandPlugin module Action # This class checks to see if the plugin is installed already, and # if so, raises an exception/error to output to the user. class PluginExistsCheck def initialize(app, env) @app = app end def call(env) installed = Vagrant::Plugin::Manager.instance.installed_plugins if !installed.key?(env[:plugin_name]) raise Vagrant::Errors::PluginNotInstalled, name: env[:plugin_name] end @app.call(env) end end end end end vagrant-2.0.2/plugins/commands/plugin/action/repair_plugins.rb000066400000000000000000000026031323370221500245610ustar00rootroot00000000000000require "vagrant/plugin/manager" module VagrantPlugins module CommandPlugin module Action # This middleware attempts to repair installed plugins. # # In general, if plugins are failing to properly load the # core issue will likely be one of two issues: # 1. manual modifications within ~/.vagrant.d/ # 2. vagrant upgrade # Running an install on configured plugin set will most # likely fix these issues, which is all this action does. class RepairPlugins def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::plugins::plugincommand::repair") end def call(env) env[:ui].info(I18n.t("vagrant.commands.plugin.repairing")) plugins = Vagrant::Plugin::Manager.instance.installed_plugins begin Vagrant::Bundler.instance.init!(plugins, :repair) env[:ui].info(I18n.t("vagrant.commands.plugin.repair_complete")) rescue Exception => e @logger.error("Failed to repair user installed plugins: #{e.class} - #{e}") e.backtrace.each do |backtrace_line| @logger.debug(backtrace_line) end env[:ui].error(I18n.t("vagrant.commands.plugin.repair_failed", message: e.message)) end # Continue @app.call(env) end end end end end vagrant-2.0.2/plugins/commands/plugin/action/uninstall_plugin.rb000066400000000000000000000012531323370221500251250ustar00rootroot00000000000000module VagrantPlugins module CommandPlugin module Action # This middleware uninstalls a plugin by simply removing it from # the state file. Running a {PruneGems} after should properly remove # it from the gem index. class UninstallPlugin def initialize(app, env) @app = app end def call(env) # Remove it! env[:ui].info(I18n.t("vagrant.commands.plugin.uninstalling", name: env[:plugin_name])) manager = Vagrant::Plugin::Manager.instance manager.uninstall_plugin(env[:plugin_name]) @app.call(env) end end end end end vagrant-2.0.2/plugins/commands/plugin/action/update_gems.rb000066400000000000000000000026431323370221500240370ustar00rootroot00000000000000require "vagrant/plugin/manager" module VagrantPlugins module CommandPlugin module Action class UpdateGems def initialize(app, env) @app = app end def call(env) names = env[:plugin_name] || [] if names.empty? env[:ui].info(I18n.t("vagrant.commands.plugin.updating")) else env[:ui].info(I18n.t("vagrant.commands.plugin.updating_specific", names: names.join(", "))) end manager = Vagrant::Plugin::Manager.instance installed_plugins = manager.installed_plugins new_specs = manager.update_plugins(names) updated_plugins = manager.installed_plugins updated = {} installed_plugins.each do |name, info| update = updated_plugins[name] if update && update["installed_gem_version"] != info["installed_gem_version"] updated[name] = update["installed_gem_version"] end end if updated.empty? env[:ui].success(I18n.t("vagrant.commands.plugin.up_to_date")) end updated.each do |name, version| env[:ui].success(I18n.t("vagrant.commands.plugin.updated", name: name, version: version.to_s)) end # Continue @app.call(env) end end end end end vagrant-2.0.2/plugins/commands/plugin/command/000077500000000000000000000000001323370221500213515ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/plugin/command/base.rb000066400000000000000000000011021323370221500226020ustar00rootroot00000000000000require "vagrant/plugin/state_file" module VagrantPlugins module CommandPlugin module Command class Base < Vagrant.plugin("2", :command) # This is a helper for executing an action sequence with the proper # environment hash setup so that the plugin specific helpers are # in. # # @param [Object] callable the Middleware callable # @param [Hash] env Extra environment hash that is merged in. def action(callable, env=nil) @env.action_runner.run(callable, env) end end end end end vagrant-2.0.2/plugins/commands/plugin/command/expunge.rb000066400000000000000000000046121323370221500233540ustar00rootroot00000000000000require 'optparse' require_relative "base" module VagrantPlugins module CommandPlugin module Command class Expunge < Base def execute options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant plugin expunge [-h]" o.on("--force", "Do not prompt for confirmation") do |force| options[:force] = force end o.on("--reinstall", "Reinstall current plugins after expunge") do |reinstall| options[:reinstall] = reinstall end end # Parse the options argv = parse_options(opts) return if !argv raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp if argv.length > 0 plugins = Vagrant::Plugin::Manager.instance.installed_plugins if !options[:reinstall] && !options[:force] && !plugins.empty? result = nil attempts = 0 while attempts < 5 && result.nil? attempts += 1 result = @env.ui.ask( I18n.t("vagrant.commands.plugin.expunge_request_reinstall") + " [N]: " ) result = result.to_s.downcase.strip result = "n" if result.empty? if !["y", "yes", "n", "no"].include?(result) result = nil @env.ui.error("Please answer Y or N") else result = result[0,1] end end options[:reinstall] = result == "y" end # Remove all installed user plugins action(Action.action_expunge, options) if options[:reinstall] @env.ui.info(I18n.t("vagrant.commands.plugin.expunge_reinstall")) plugins.each do |plugin_name, plugin_info| next if plugin_info["system"] # system plugins do not require re-install # Rebuild information hash to use symbols plugin_info = Hash[ plugin_info.map do |key, value| ["plugin_#{key}".to_sym, value] end ] action( Action.action_install, plugin_info.merge( plugin_name: plugin_name ) ) end end # Success, exit status 0 0 end end end end end vagrant-2.0.2/plugins/commands/plugin/command/install.rb000066400000000000000000000023741323370221500233520ustar00rootroot00000000000000require 'optparse' require_relative "base" require_relative "mixin_install_opts" module VagrantPlugins module CommandPlugin module Command class Install < Base include MixinInstallOpts def execute options = { verbose: false } opts = OptionParser.new do |o| o.banner = "Usage: vagrant plugin install ... [-h]" o.separator "" build_install_opts(o, options) o.on("--verbose", "Enable verbose output for plugin installation") do |v| options[:verbose] = v end end # Parse the options argv = parse_options(opts) return if !argv raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp if argv.length < 1 # Install the gem argv.each do |name| action(Action.action_install, { plugin_entry_point: options[:entry_point], plugin_version: options[:plugin_version], plugin_sources: options[:plugin_sources], plugin_name: name, plugin_verbose: options[:verbose] }) end # Success, exit status 0 0 end end end end end vagrant-2.0.2/plugins/commands/plugin/command/license.rb000066400000000000000000000013401323370221500233160ustar00rootroot00000000000000require 'optparse' require_relative "base" module VagrantPlugins module CommandPlugin module Command class License < Base def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant plugin license [-h]" end # Parse the options argv = parse_options(opts) return if !argv raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp if argv.length < 2 # License the plugin action(Action.action_license, { plugin_license_path: argv[1], plugin_name: argv[0] }) # Success, exit status 0 0 end end end end end vagrant-2.0.2/plugins/commands/plugin/command/list.rb000066400000000000000000000011471323370221500226540ustar00rootroot00000000000000require 'optparse' require_relative "base" module VagrantPlugins module CommandPlugin module Command class List < Base def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant plugin list [-h]" end # Parse the options argv = parse_options(opts) return if !argv raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp if argv.length > 0 # List the installed plugins action(Action.action_list) # Success, exit status 0 0 end end end end end vagrant-2.0.2/plugins/commands/plugin/command/mixin_install_opts.rb000066400000000000000000000020341323370221500256140ustar00rootroot00000000000000module VagrantPlugins module CommandPlugin module Command module MixinInstallOpts def build_install_opts(o, options) options[:plugin_sources] = Vagrant::Bundler::DEFAULT_GEM_SOURCES.dup o.on("--entry-point NAME", String, "The name of the entry point file for loading the plugin.") do |entry_point| options[:entry_point] = entry_point end o.on("--plugin-clean-sources", "Remove all plugin sources defined so far (including defaults)") do |clean| options[:plugin_sources] = [] if clean end o.on("--plugin-source PLUGIN_SOURCE", String, "Add a RubyGems repository source") do |plugin_source| options[:plugin_sources] << plugin_source end o.on("--plugin-version PLUGIN_VERSION", String, "Install a specific version of the plugin") do |plugin_version| options[:plugin_version] = plugin_version end end end end end end vagrant-2.0.2/plugins/commands/plugin/command/repair.rb000066400000000000000000000011661323370221500231640ustar00rootroot00000000000000require 'optparse' require_relative "base" module VagrantPlugins module CommandPlugin module Command class Repair < Base def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant plugin repair [-h]" end # Parse the options argv = parse_options(opts) return if !argv raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp if argv.length > 0 # Attempt to repair installed plugins action(Action.action_repair) # Success, exit status 0 0 end end end end end vagrant-2.0.2/plugins/commands/plugin/command/root.rb000066400000000000000000000051571323370221500226710ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandPlugin module Command class Root < Vagrant.plugin("2", :command) def self.synopsis "manages plugins: install, uninstall, update, etc." end def initialize(argv, env) super @main_args, @sub_command, @sub_args = split_main_and_subcommand(argv) @subcommands = Vagrant::Registry.new @subcommands.register(:expunge) do require_relative "expunge" Expunge end @subcommands.register(:install) do require_relative "install" Install end @subcommands.register(:license) do require_relative "license" License end @subcommands.register(:list) do require_relative "list" List end @subcommands.register(:repair) do require_relative "repair" Repair end @subcommands.register(:update) do require_relative "update" Update end @subcommands.register(:uninstall) do require_relative "uninstall" Uninstall end end def execute if @main_args.include?("-h") || @main_args.include?("--help") # Print the help for all the sub-commands. return help end # If we reached this far then we must have a subcommand. If not, # then we also just print the help and exit. command_class = @subcommands.get(@sub_command.to_sym) if @sub_command return help if !command_class || !@sub_command @logger.debug("Invoking command class: #{command_class} #{@sub_args.inspect}") # Initialize and execute the command class command_class.new(@sub_args, @env).execute end # Prints the help out for this command def help opts = OptionParser.new do |o| o.banner = "Usage: vagrant plugin []" o.separator "" o.separator "Available subcommands:" # Add the available subcommands as separators in order to print them # out as well. keys = [] @subcommands.each { |key, value| keys << key.to_s } keys.sort.each do |key| o.separator " #{key}" end o.separator "" o.separator "For help on any individual command run `vagrant plugin COMMAND -h`" end @env.ui.info(opts.help, prefix: false) end end end end end vagrant-2.0.2/plugins/commands/plugin/command/uninstall.rb000066400000000000000000000013121323370221500237040ustar00rootroot00000000000000require 'optparse' require_relative "base" module VagrantPlugins module CommandPlugin module Command class Uninstall < Base def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant plugin uninstall [ ...] [-h]" end # Parse the options argv = parse_options(opts) return if !argv raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp if argv.length < 1 # Uninstall the gems argv.each do |gem| action(Action.action_uninstall, plugin_name: gem) end # Success, exit status 0 0 end end end end end vagrant-2.0.2/plugins/commands/plugin/command/update.rb000066400000000000000000000012511323370221500231570ustar00rootroot00000000000000require 'optparse' require_relative "base" require_relative "mixin_install_opts" module VagrantPlugins module CommandPlugin module Command class Update < Base include MixinInstallOpts def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant plugin update [names...] [-h]" o.separator "" end # Parse the options argv = parse_options(opts) return if !argv # Update the gem action(Action.action_update, { plugin_name: argv, }) # Success, exit status 0 0 end end end end end vagrant-2.0.2/plugins/commands/plugin/gem_helper.rb000066400000000000000000000053651323370221500224000ustar00rootroot00000000000000require "rubygems" require "rubygems/config_file" require "rubygems/gem_runner" require "log4r" module VagrantPlugins module CommandPlugin # This class provides methods to help with calling out to the # `gem` command but using the RubyGems API. class GemHelper def initialize(gem_home) @gem_home = gem_home.to_s @logger = Log4r::Logger.new("vagrant::plugins::plugincommand::gemhelper") end # This will yield the given block with the proper ENV setup so # that RubyGems only sees the gems in the Vagrant-managed gem # path. def with_environment old_gem_home = ENV["GEM_HOME"] old_gem_path = ENV["GEM_PATH"] ENV["GEM_HOME"] = @gem_home ENV["GEM_PATH"] = @gem_home @logger.debug("Set GEM_* to: #{ENV["GEM_HOME"]}") # Clear paths so that it reads the new GEM_HOME setting Gem.paths = ENV # Set a custom configuration to avoid loading ~/.gemrc loads and # /etc/gemrc and so on. old_config = nil begin old_config = Gem.configuration rescue Psych::SyntaxError # Just ignore this. This means that the ".gemrc" file has # an invalid syntax and can't be loaded. We don't care, because # when we set Gem.configuration to nil later, it'll force a reload # if it is needed. end Gem.configuration = NilGemConfig.new # Clear the sources so that installation uses custom sources old_sources = Gem.sources Gem.sources = Gem.default_sources Vagrant::Bundler::DEFAULT_GEM_SOURCES.each do |source| if !Gem.sources.include?(source) Gem.sources << source end end # Use a silent UI so that we have no output Gem::DefaultUserInteraction.use_ui(Gem::SilentUI.new) do return yield end ensure # Restore the old GEM_* settings ENV["GEM_HOME"] = old_gem_home ENV["GEM_PATH"] = old_gem_path # Reset everything Gem.configuration = old_config Gem.paths = ENV Gem.sources = old_sources.to_a end # This is pretty hacky but it is a custom implementatin of # Gem::ConfigFile so that we don't load any gemrc files. class NilGemConfig < Gem::ConfigFile def initialize # We _can not_ `super` here because that can really mess up # some other configuration state. We need to just set everything # directly. @api_keys = {} @args = [] @backtrace = false @bulk_threshold = 1000 @hash = {} @update_sources = true @verbose = true end end end end end vagrant-2.0.2/plugins/commands/plugin/plugin.rb000066400000000000000000000007071323370221500215620ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandPlugin class Plugin < Vagrant.plugin("2") name "plugin command" description <<-DESC This command helps manage and install plugins within the Vagrant environment. DESC command("plugin") do require File.expand_path("../command/root", __FILE__) Command::Root end end autoload :Action, File.expand_path("../action", __FILE__) end end vagrant-2.0.2/plugins/commands/port/000077500000000000000000000000001323370221500174215ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/port/command.rb000066400000000000000000000046441323370221500213740ustar00rootroot00000000000000require "vagrant/util/presence" require "optparse" module VagrantPlugins module CommandPort class Command < Vagrant.plugin("2", :command) include Vagrant::Util::Presence def self.synopsis "displays information about guest port mappings" end def execute options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant port [options] [name|id]" o.separator "" o.separator "Options:" o.separator "" o.on("--guest PORT", "Output the host port that maps to the given guest port") do |port| options[:guest] = port end o.on("--machine-readable", "Display machine-readable output") end # Parse the options argv = parse_options(opts) return if !argv with_target_vms(argv, single_target: true) do |vm| vm.action_raw(:config_validate, Vagrant::Action::Builtin::ConfigValidate) if !vm.provider.capability?(:forwarded_ports) @env.ui.error(I18n.t("port_command.missing_capability", provider: vm.provider_name, )) return 1 end ports = vm.provider.capability(:forwarded_ports) if !present?(ports) @env.ui.info(I18n.t("port_command.empty_ports")) return 0 end if present?(options[:guest]) return print_single(vm, ports, options[:guest]) else return print_all(vm, ports) end end end private # Print all the guest <=> host port mappings. # @return [0] the exit code def print_all(vm, ports) @env.ui.info(I18n.t("port_command.details")) @env.ui.info("") ports.each do |host, guest| @env.ui.info("#{guest.to_s.rjust(6)} (guest) => #{host} (host)") @env.ui.machine("forwarded_port", guest, host, target: vm.name.to_s) end return 0 end # Print the host mapping that matches the given guest target. # @return [0,1] the exit code def print_single(vm, ports, target) map = ports.find { |_, guest| "#{guest}" == "#{target}" } if !present?(map) @env.ui.error(I18n.t("port_command.no_matching_port", port: target, )) return 1 end @env.ui.info("#{map[0]}") return 0 end end end end vagrant-2.0.2/plugins/commands/port/locales/000077500000000000000000000000001323370221500210435ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/port/locales/en.yml000066400000000000000000000021431323370221500221700ustar00rootroot00000000000000en: port_command: details: |- The forwarded ports for the machine are listed below. Please note that these values may differ from values configured in the Vagrantfile if the provider supports automatic port collision detection and resolution. empty_ports: |- The provider reported there are no forwarded ports for this virtual machine. This can be caused if there are no ports specified in the Vagrantfile or if the virtual machine is not currently running. Please check that the virtual machine is running and try again. missing_capability: |- The %{provider} provider does not support listing forwarded ports. This is most likely a limitation of the provider and not a bug in Vagrant. If you believe this is a bug in Vagrant, please search existing issues before opening a new one. no_matching_port: |- The guest is not currently mapping port %{port} to the host machine. Is the port configured in the Vagrantfile? You may need to run `vagrant reload` if changes were made to the port configuration in the Vagrantfile. vagrant-2.0.2/plugins/commands/port/plugin.rb000066400000000000000000000010401323370221500212370ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandPort class Plugin < Vagrant.plugin("2") name "port command" description <<-DESC The `port` command displays guest port mappings. DESC command("port") do require_relative "command" self.init! Command end protected def self.init! return if defined?(@_init) I18n.load_path << File.expand_path("../locales/en.yml", __FILE__) I18n.reload! @_init = true end end end end vagrant-2.0.2/plugins/commands/powershell/000077500000000000000000000000001323370221500206215ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/powershell/command.rb000066400000000000000000000077371323370221500226020ustar00rootroot00000000000000require "optparse" require "vagrant/util/powershell" require_relative "../../communicators/winrm/helper" module VagrantPlugins module CommandPS class Command < Vagrant.plugin("2", :command) def self.synopsis "connects to machine via powershell remoting" end def execute options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant powershell [-- extra powershell args]" o.separator "" o.separator "Options:" o.separator "" o.on("-c", "--command COMMAND", "Execute a powershell command directly") do |c| options[:command] = c end end # Parse out the extra args to send to the ps session, which # is everything after the "--" split_index = @argv.index("--") if split_index options[:extra_args] = @argv.drop(split_index + 1) @argv = @argv.take(split_index) end # Parse the options and return if we don't have any target. argv = parse_options(opts) return if !argv # Check if the host even supports ps remoting raise Errors::HostUnsupported if !@env.host.capability?(:ps_client) # Execute ps session if we can with_target_vms(argv, single_target: true) do |machine| if !machine.communicate.ready? raise Vagrant::Errors::VMNotCreatedError end if machine.config.vm.communicator != :winrm raise VagrantPlugins::CommunicatorWinRM::Errors::WinRMNotReady end if !options[:command].nil? out_code = machine.communicate.execute(options[:command].dup) do |type,data| machine.ui.detail(data) if type == :stdout end if out_code == 0 machine.ui.success("Command: #{options[:command]} executed succesfully with output code #{out_code}.") end next end ps_info = VagrantPlugins::CommunicatorWinRM::Helper.winrm_info(machine) ps_info[:username] = machine.config.winrm.username ps_info[:password] = machine.config.winrm.password # Extra arguments if we have any ps_info[:extra_args] = options[:extra_args] result = ready_ps_remoting_for(machine, ps_info) machine.ui.detail( "Creating powershell session to #{ps_info[:host]}:#{ps_info[:port]}") machine.ui.detail("Username: #{ps_info[:username]}") begin @env.host.capability(:ps_client, ps_info) ensure if !result["PreviousTrustedHosts"].nil? reset_ps_remoting_for(machine, ps_info) end end end end def ready_ps_remoting_for(machine, ps_info) machine.ui.output(I18n.t("vagrant_ps.detecting")) script_path = File.expand_path("../scripts/enable_psremoting.ps1", __FILE__) args = [] args << "-hostname" << ps_info[:host] args << "-port" << ps_info[:port].to_s args << "-username" << ps_info[:username] args << "-password" << ps_info[:password] result = Vagrant::Util::PowerShell.execute(script_path, *args) if result.exit_code != 0 raise Errors::PowerShellError, script: script_path, stderr: result.stderr end result_output = JSON.parse(result.stdout) raise Errors::PSRemotingUndetected if !result_output["Success"] result_output end def reset_ps_remoting_for(machine, ps_info) machine.ui.output(I18n.t("vagrant_ps.reseting")) script_path = File.expand_path("../scripts/reset_trustedhosts.ps1", __FILE__) args = [] args << "-hostname" << ps_info[:host] result = Vagrant::Util::PowerShell.execute(script_path, *args) if result.exit_code != 0 raise Errors::PowerShellError, script: script_path, stderr: result.stderr end end end end end vagrant-2.0.2/plugins/commands/powershell/errors.rb000066400000000000000000000010251323370221500224600ustar00rootroot00000000000000module VagrantPlugins module CommandPS module Errors # A convenient superclass for all our errors. class PSCommandError < Vagrant::Errors::VagrantError error_namespace("vagrant_ps.errors") end class HostUnsupported < PSCommandError error_key(:host_unsupported) end class PSRemotingUndetected < PSCommandError error_key(:ps_remoting_undetected) end class PowerShellError < PSCommandError error_key(:powershell_error) end end end end vagrant-2.0.2/plugins/commands/powershell/plugin.rb000066400000000000000000000012771323370221500224530ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandPS autoload :Errors, File.expand_path("../errors", __FILE__) class Plugin < Vagrant.plugin("2") name "powershell command" description <<-DESC The powershell command opens a remote PowerShell session to the machine if it supports powershell remoting. DESC command("powershell") do require_relative "command" init! Command end protected def self.init! return if defined?(@_init) I18n.load_path << File.expand_path("templates/locales/command_ps.yml", Vagrant.source_root) I18n.reload! @_init = true end end end end vagrant-2.0.2/plugins/commands/powershell/scripts/000077500000000000000000000000001323370221500223105ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/powershell/scripts/enable_psremoting.ps1000066400000000000000000000036351323370221500264410ustar00rootroot00000000000000Param( [string]$hostname, [string]$port, [string]$username, [string]$password ) # If we are in this script, we know basic winrm is working # If the user is not using a domain acount and chances are # they are not, PS Remoting will not work if the guest is not # listed in the trusted hosts. $encrypted_password = ConvertTo-SecureString $password -asplaintext -force $creds = New-Object System.Management.Automation.PSCredential ( "$hostname\\$username", $encrypted_password) $result = @{ Success = $false PreviousTrustedHosts = $null } try { invoke-command -computername $hostname ` -Credential $creds ` -Port $port ` -ScriptBlock {} ` -ErrorAction Stop $result.Success = $true } catch{} if(!$result.Success) { $newHosts = @() $result.PreviousTrustedHosts=( Get-Item "wsman:\localhost\client\trustedhosts").Value $hostArray=$result.PreviousTrustedHosts.Split(",").Trim() if($hostArray -contains "*") { $result.PreviousTrustedHosts = $null } elseif(!($hostArray -contains $hostname)) { $strNewHosts = $hostname if($result.PreviousTrustedHosts.Length -gt 0){ $strNewHosts = $result.PreviousTrustedHosts + "," + $strNewHosts } Set-Item -Path "wsman:\localhost\client\trustedhosts" ` -Value $strNewHosts -Force try { invoke-command -computername $hostname ` -Credential $creds ` -Port $port ` -ScriptBlock {} ` -ErrorAction Stop $result.Success = $true } catch{ Set-Item -Path "wsman:\localhost\client\trustedhosts" ` -Value $result.PreviousTrustedHosts -Force $result.PreviousTrustedHosts = $null } } } Write-Output $(ConvertTo-Json $result) vagrant-2.0.2/plugins/commands/powershell/scripts/reset_trustedhosts.ps1000066400000000000000000000005541323370221500267160ustar00rootroot00000000000000Param( [string]$hostname ) $trustedHosts = ( Get-Item "wsman:\localhost\client\trustedhosts").Value.Replace( $hostname, '') $trustedHosts = $trustedHosts.Replace(",,","") if($trustedHosts.EndsWith(",")){ $trustedHosts = $trustedHosts.Substring(0,$trustedHosts.length-1) } Set-Item "wsman:\localhost\client\trustedhosts" -Value $trustedHosts -Forcevagrant-2.0.2/plugins/commands/provider/000077500000000000000000000000001323370221500202675ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/provider/command.rb000066400000000000000000000041151323370221500222330ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandProvider class Command < Vagrant.plugin("2", :command) def self.synopsis "show provider for this environment" end def execute options = {} options[:install] = false options[:usable] = false opts = OptionParser.new do |o| o.banner = "Usage: vagrant provider [options] [args]" o.separator "" o.separator "This command interacts with the provider for this environment." o.separator "With no arguments, it'll output the default provider for this" o.separator "environment." o.separator "" o.separator "Options:" o.separator "" o.on("--install", "Installs the provider if possible") do |f| options[:install] = f end o.on("--usable", "Checks if the named provider is usable") do |f| options[:usable] = f end end # Parse the options argv = parse_options(opts) return if !argv # Get the machine machine = nil with_target_vms(argv, single_target: true) do |m| machine = m end # Output some machine readable stuff @env.ui.machine("provider-name", machine.provider_name, target: machine.name.to_s) # Check if we're just doing a usability check if options[:usable] @env.ui.output(machine.provider_name.to_s) return 0 if machine.provider.class.usable?(false) return 1 end # Check if we're requesting installation if options[:install] key = "provider_install_#{machine.provider_name}".to_sym if !@env.host.capability?(key) raise Vagrant::Errors::ProviderCantInstall, provider: machine.provider_name.to_s end @env.host.capability(key) return end # No subtask, just output the provider name @env.ui.output(machine.provider_name.to_s) # Success, exit status 0 0 end end end end vagrant-2.0.2/plugins/commands/provider/plugin.rb000066400000000000000000000006341323370221500221150ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandProvider class Plugin < Vagrant.plugin("2") name "provider command" description <<-DESC The `provider` command is used to interact with the various providers that are installed with Vagrant. DESC command("provider", primary: false) do require_relative "command" Command end end end end vagrant-2.0.2/plugins/commands/provision/000077500000000000000000000000001323370221500204655ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/provision/command.rb000066400000000000000000000017341323370221500224350ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandProvision class Command < Vagrant.plugin("2", :command) def self.synopsis "provisions the vagrant machine" end def execute options = {} options[:provision_types] = nil opts = OptionParser.new do |o| o.banner = "Usage: vagrant provision [vm-name] [--provision-with x,y,z]" o.on("--provision-with x,y,z", Array, "Enable only certain provisioners, by type or by name.") do |list| options[:provision_types] = list.map { |type| type.to_sym } end end # Parse the options argv = parse_options(opts) return if !argv # Go over each VM and provision! @logger.debug("'provision' each target VM...") with_target_vms(argv) do |machine| machine.action(:provision, options) end # Success, exit status 0 0 end end end end vagrant-2.0.2/plugins/commands/provision/plugin.rb000066400000000000000000000006451323370221500223150ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandProvision class Plugin < Vagrant.plugin("2") name "provision command" description <<-DESC The `provision` command provisions your virtual machine based on the configuration of the Vagrantfile. DESC command("provision") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-2.0.2/plugins/commands/push/000077500000000000000000000000001323370221500174145ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/push/command.rb000066400000000000000000000042531323370221500213630ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandPush class Command < Vagrant.plugin("2", :command) def self.synopsis "deploys code in this environment to a configured destination" end # @todo support multiple strategies if requested by the community def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant push [strategy] [options]" end # Parse the options argv = parse_options(opts) return if !argv name = validate_pushes!(@env.pushes, argv[0]) # Validate the configuration @env.machine(@env.machine_names.first, @env.default_provider).action_raw( :config_validate, Vagrant::Action::Builtin::ConfigValidate) @logger.debug("'push' environment with strategy: `#{name}'") @env.push(name) 0 end # Validate that the given list of names corresponds to valid pushes. # # @raise Vagrant::Errors::PushesNotDefined # if there are no pushes defined # @raise Vagrant::Errors::PushStrategyNotProvided # if there are multiple push strategies defined and none were specified # @raise Vagrant::Errors::PushStrategyNotDefined # if the given push name do not correspond to a push strategy # # @param [Array] pushes # the list of pushes defined by the environment # @param [String] name # the name provided by the user on the command line # # @return [Symbol] # the compiled list of pushes # def validate_pushes!(pushes, name = nil) if pushes.nil? || pushes.empty? raise Vagrant::Errors::PushesNotDefined end if name.nil? if pushes.length == 1 return pushes.first.to_sym else raise Vagrant::Errors::PushStrategyNotProvided, pushes: pushes.map(&:to_s) end end name = name.to_sym if !pushes.include?(name) raise Vagrant::Errors::PushStrategyNotDefined, name: name.to_s, pushes: pushes.map(&:to_s) end return name end end end end vagrant-2.0.2/plugins/commands/push/plugin.rb000066400000000000000000000005361323370221500212430ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandPush class Plugin < Vagrant.plugin("2") name "push command" description <<-DESC The `push` command deploys code in this environment. DESC command("push") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-2.0.2/plugins/commands/rdp/000077500000000000000000000000001323370221500172225ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/rdp/command.rb000066400000000000000000000064671323370221500212020ustar00rootroot00000000000000require "optparse" module VagrantPlugins module CommandRDP class Command < Vagrant.plugin("2", :command) def self.synopsis "connects to machine via RDP" end def execute options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant rdp [options] [name|id] [-- extra args]" end # Parse out the extra args to send to the RDP client, which # is everything after the "--" split_index = @argv.index("--") if split_index options[:extra_args] = @argv.drop(split_index + 1) @argv = @argv.take(split_index) end # Parse the options and return if we don't have any target. argv = parse_options(opts) return if !argv # Check if the host even supports RDP raise Errors::HostUnsupported if !@env.host.capability?(:rdp_client) # Execute RDP if we can with_target_vms(argv, single_target: true) do |machine| if !machine.communicate.ready? raise Vagrant::Errors::VMNotCreatedError end machine.ui.output(I18n.t("vagrant_rdp.detecting")) rdp_info = get_rdp_info(machine) raise Errors::RDPUndetected if !rdp_info # Extra arguments if we have any rdp_info[:extra_args] = options[:extra_args] machine.ui.detail( "Address: #{rdp_info[:host]}:#{rdp_info[:port]}") machine.ui.detail("Username: #{rdp_info[:username]}") machine.ui.success(I18n.t("vagrant_rdp.connecting")) @env.host.capability(:rdp_client, rdp_info) end end protected def get_rdp_info(machine) rdp_info = {} if machine.provider.capability?(:rdp_info) rdp_info = machine.provider.capability(:rdp_info) rdp_info ||= {} end ssh_info = machine.ssh_info if !rdp_info[:username] username = ssh_info[:username] if machine.config.vm.communicator == :winrm username = machine.config.winrm.username end rdp_info[:username] = username end if !rdp_info[:password] password = ssh_info[:password] if machine.config.vm.communicator == :winrm password = machine.config.winrm.password end rdp_info[:password] = password end rdp_info[:host] ||= ssh_info[:host] rdp_info[:port] ||= machine.config.rdp.port rdp_info[:username] ||= machine.config.rdp.username if rdp_info[:host] == "127.0.0.1" # We need to find a forwarded port... search_port = machine.config.rdp.search_port ports = nil if machine.provider.capability?(:forwarded_ports) ports = machine.provider.capability(:forwarded_ports) else ports = {}.tap do |result| machine.config.vm.networks.each do |type, netopts| next if type != :forwarded_port next if !netopts[:host] result[netopts[:host]] = netopts[:guest] end end end ports = ports.invert port = ports[search_port] rdp_info[:port] = port return nil if !port end return rdp_info end end end end vagrant-2.0.2/plugins/commands/rdp/config.rb000066400000000000000000000011621323370221500210140ustar00rootroot00000000000000module VagrantPlugins module CommandRDP class Config < Vagrant.plugin("2", :config) attr_accessor :port attr_accessor :search_port attr_accessor :username def initialize @port = UNSET_VALUE @search_port = UNSET_VALUE @username = UNSET_VALUE end def finalize! @port = 3389 if @port == UNSET_VALUE @search_port = 3389 if @search_port == UNSET_VALUE @username = nil if @username == UNSET_VALUE end def validate(machine) errors = _detected_errors { "RDP" => errors } end end end end vagrant-2.0.2/plugins/commands/rdp/errors.rb000066400000000000000000000006311323370221500210630ustar00rootroot00000000000000module VagrantPlugins module CommandRDP module Errors # A convenient superclass for all our errors. class RDPError < Vagrant::Errors::VagrantError error_namespace("vagrant_rdp.errors") end class HostUnsupported < RDPError error_key(:host_unsupported) end class RDPUndetected < RDPError error_key(:rdp_undetected) end end end end vagrant-2.0.2/plugins/commands/rdp/plugin.rb000066400000000000000000000014141323370221500210450ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandRDP autoload :Errors, File.expand_path("../errors", __FILE__) class Plugin < Vagrant.plugin("2") name "rdp command" description <<-DESC The rdp command opens a remote desktop Window to the machine if it supports RDP. DESC command("rdp") do require File.expand_path("../command", __FILE__) init! Command end config("rdp") do require_relative "config" Config end protected def self.init! return if defined?(@_init) I18n.load_path << File.expand_path( "templates/locales/command_rdp.yml", Vagrant.source_root) I18n.reload! @_init = true end end end end vagrant-2.0.2/plugins/commands/reload/000077500000000000000000000000001323370221500177035ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/reload/command.rb000066400000000000000000000031151323370221500216460ustar00rootroot00000000000000require 'optparse' require "vagrant" require Vagrant.source_root.join("plugins/commands/up/start_mixins") module VagrantPlugins module CommandReload class Command < Vagrant.plugin("2", :command) # We assume that the `up` plugin exists and that we'll have access # to this. include VagrantPlugins::CommandUp::StartMixins def self.synopsis "restarts vagrant machine, loads new Vagrantfile configuration" end def execute options = {} options[:provision_ignore_sentinel] = false opts = OptionParser.new do |o| o.banner = "Usage: vagrant reload [vm-name]" o.separator "" build_start_options(o, options) end # Parse the options argv = parse_options(opts) return if !argv # Validate the provisioners validate_provisioner_flags!(options, argv) @logger.debug("'reload' each target VM...") machines = [] with_target_vms(argv) do |machine| machines << machine machine.action(:reload, options) end # Output the post-up messages that we have, if any machines.each do |m| next if !m.config.vm.post_up_message next if m.config.vm.post_up_message == "" # Add a newline to separate things. @env.ui.info("", prefix: false) m.ui.success(I18n.t( "vagrant.post_up_message", name: m.name.to_s, message: m.config.vm.post_up_message)) end # Success, exit status 0 0 end end end end vagrant-2.0.2/plugins/commands/reload/plugin.rb000066400000000000000000000006361323370221500215330ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandReload class Plugin < Vagrant.plugin("2") name "reload command" description <<-DESC The `reload` command will halt, reconfigure your machine based on the Vagrantfile, and bring it back up. DESC command("reload") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-2.0.2/plugins/commands/resume/000077500000000000000000000000001323370221500177355ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/resume/command.rb000066400000000000000000000020611323370221500216770ustar00rootroot00000000000000require 'optparse' require Vagrant.source_root.join("plugins/commands/up/start_mixins") module VagrantPlugins module CommandResume class Command < Vagrant.plugin("2", :command) # We assume that the `up` plugin exists and that we'll have access # to this. include VagrantPlugins::CommandUp::StartMixins def self.synopsis "resume a suspended vagrant machine" end def execute options = {} options[:provision_ignore_sentinel] = false opts = OptionParser.new do |o| o.banner = "Usage: vagrant resume [vm-name]" o.separator "" build_start_options(o, options) end # Parse the options argv = parse_options(opts) return if !argv # Validate the provisioners validate_provisioner_flags!(options, argv) @logger.debug("'resume' each target VM...") with_target_vms(argv) do |machine| machine.action(:resume, options) end # Success, exit status 0 0 end end end end vagrant-2.0.2/plugins/commands/resume/plugin.rb000066400000000000000000000005471323370221500215660ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandResume class Plugin < Vagrant.plugin("2") name "resume command" description <<-DESC The `resume` command resumes a suspend virtual machine. DESC command("resume") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-2.0.2/plugins/commands/snapshot/000077500000000000000000000000001323370221500202745ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/snapshot/command/000077500000000000000000000000001323370221500217125ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/snapshot/command/delete.rb000066400000000000000000000024301323370221500235000ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandSnapshot module Command class Delete < Vagrant.plugin("2", :command) def execute options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant snapshot delete [options] [vm-name] " o.separator "" o.separator "Delete a snapshot taken previously with snapshot save." end # Parse the options argv = parse_options(opts) return if !argv if argv.empty? || argv.length > 2 raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp end name = argv.pop with_target_vms(argv) do |vm| if !vm.provider.capability?(:snapshot_list) raise Vagrant::Errors::SnapshotNotSupported end snapshot_list = vm.provider.capability(:snapshot_list) if snapshot_list.include? name vm.action(:snapshot_delete, snapshot_name: name) else raise Vagrant::Errors::SnapshotNotFound, snapshot_name: name, machine: vm.name.to_s end end # Success, exit status 0 0 end end end end end vagrant-2.0.2/plugins/commands/snapshot/command/list.rb000066400000000000000000000023751323370221500232210ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandSnapshot module Command class List < Vagrant.plugin("2", :command) def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant snapshot list [options] [vm-name]" o.separator "" o.separator "List all snapshots taken for a machine." end # Parse the options argv = parse_options(opts) return if !argv with_target_vms(argv) do |vm| if !vm.id vm.ui.info(I18n.t("vagrant.commands.common.vm_not_created")) next end if !vm.provider.capability?(:snapshot_list) raise Vagrant::Errors::SnapshotNotSupported end snapshots = vm.provider.capability(:snapshot_list) if snapshots.empty? vm.ui.output(I18n.t("vagrant.actions.vm.snapshot.list_none")) vm.ui.detail(I18n.t("vagrant.actions.vm.snapshot.list_none_detail")) next end snapshots.each do |snapshot| vm.ui.output(snapshot, prefix: false) end end # Success, exit status 0 0 end end end end end vagrant-2.0.2/plugins/commands/snapshot/command/pop.rb000066400000000000000000000022061323370221500230350ustar00rootroot00000000000000require 'json' require 'optparse' require 'vagrant' require Vagrant.source_root.join("plugins/commands/up/start_mixins") require_relative "push_shared" module VagrantPlugins module CommandSnapshot module Command class Pop < Vagrant.plugin("2", :command) include PushShared include VagrantPlugins::CommandUp::StartMixins def execute options = {} options[:snapshot_delete] = true opts = OptionParser.new do |o| o.banner = "Usage: vagrant snapshot pop [options] [vm-name]" o.separator "" build_start_options(o, options) o.separator "Restore state that was pushed with `vagrant snapshot push`." o.on("--no-delete", "Don't delete the snapshot after the restore") do options[:snapshot_delete] = false end end # Parse the options argv = parse_options(opts) return if !argv # Validate the provisioners validate_provisioner_flags!(options, argv) return shared_exec(argv, method(:pop), options) end end end end end vagrant-2.0.2/plugins/commands/snapshot/command/push.rb000066400000000000000000000017771323370221500232320ustar00rootroot00000000000000require 'json' require 'optparse' require_relative "push_shared" module VagrantPlugins module CommandSnapshot module Command class Push < Vagrant.plugin("2", :command) include PushShared def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant snapshot push [options] [vm-name]" o.separator "" o.separator "Take a snapshot of the current state of the machine and 'push'" o.separator "it onto the stack of states. You can use `vagrant snapshot pop`" o.separator "to restore back to this state at any time." o.separator "" o.separator "If you use `vagrant snapshot save` or restore at any point after" o.separator "a push, pop will still bring you back to this pushed state." end # Parse the options argv = parse_options(opts) return if !argv return shared_exec(argv, method(:push)) end end end end end vagrant-2.0.2/plugins/commands/snapshot/command/push_shared.rb000066400000000000000000000030031323370221500245400ustar00rootroot00000000000000require 'json' module VagrantPlugins module CommandSnapshot module Command module PushShared def shared_exec(argv, m, opts={}) with_target_vms(argv) do |vm| if !vm.id vm.ui.info("Not created. Cannot push snapshot state.") next end vm.env.lock("machine-snapshot-stack") do m.call(vm, opts) end end # Success, exit with 0 0 end def push(machine, opts={}) snapshot_name = "push_#{Time.now.to_i}_#{rand(10000)}" # Save the snapshot. This will raise an exception if it fails. machine.action(:snapshot_save, snapshot_name: snapshot_name) end def pop(machine, opts={}) # By reverse sorting, we should be able to find the first # pushed snapshot. name = nil snapshots = machine.provider.capability(:snapshot_list) snapshots.sort.reverse.each do |snapshot| if snapshot =~ /^push_\d+_\d+$/ name = snapshot break end end # If no snapshot was found, we never pushed if !name machine.ui.info(I18n.t("vagrant.commands.snapshot.no_push_snapshot")) return end # Restore the snapshot and tell the provider to delete it, if required opts[:snapshot_name] = name machine.action(:snapshot_restore, opts) end end end end end vagrant-2.0.2/plugins/commands/snapshot/command/restore.rb000066400000000000000000000031241323370221500237220ustar00rootroot00000000000000require 'optparse' require 'vagrant' require Vagrant.source_root.join("plugins/commands/up/start_mixins") module VagrantPlugins module CommandSnapshot module Command class Restore < Vagrant.plugin("2", :command) include VagrantPlugins::CommandUp::StartMixins def execute options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant snapshot restore [options] [vm-name] " o.separator "" build_start_options(o, options) o.separator "Restore a snapshot taken previously with snapshot save." end # Parse the options argv = parse_options(opts) return if !argv if argv.empty? || argv.length > 2 raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp end # Validate the provisioners validate_provisioner_flags!(options, argv) name = argv.pop options[:snapshot_name] = name with_target_vms(argv) do |vm| if !vm.provider.capability?(:snapshot_list) raise Vagrant::Errors::SnapshotNotSupported end snapshot_list = vm.provider.capability(:snapshot_list) if snapshot_list.include? name vm.action(:snapshot_restore, options) else raise Vagrant::Errors::SnapshotNotFound, snapshot_name: name, machine: vm.name.to_s end end # Success, exit status 0 0 end end end end end vagrant-2.0.2/plugins/commands/snapshot/command/root.rb000066400000000000000000000047621323370221500232330ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandSnapshot module Command class Root < Vagrant.plugin("2", :command) def self.synopsis "manages snapshots: saving, restoring, etc." end def initialize(argv, env) super @main_args, @sub_command, @sub_args = split_main_and_subcommand(argv) @subcommands = Vagrant::Registry.new @subcommands.register(:save) do require_relative "save" Save end @subcommands.register(:restore) do require_relative "restore" Restore end @subcommands.register(:delete) do require_relative "delete" Delete end @subcommands.register(:list) do require_relative "list" List end @subcommands.register(:push) do require_relative "push" Push end @subcommands.register(:pop) do require_relative "pop" Pop end end def execute if @main_args.include?("-h") || @main_args.include?("--help") # Print the help for all the commands. return help end # If we reached this far then we must have a subcommand. If not, # then we also just print the help and exit. command_class = @subcommands.get(@sub_command.to_sym) if @sub_command return help if !command_class || !@sub_command @logger.debug("Invoking command class: #{command_class} #{@sub_args.inspect}") # Initialize and execute the command class command_class.new(@sub_args, @env).execute end # Prints the help out for this command def help opts = OptionParser.new do |opts| opts.banner = "Usage: vagrant snapshot []" opts.separator "" opts.separator "Available subcommands:" # Add the available subcommands as separators in order to print them # out as well. keys = [] @subcommands.each { |key, value| keys << key.to_s } keys.sort.each do |key| opts.separator " #{key}" end opts.separator "" opts.separator "For help on any individual subcommand run `vagrant snapshot -h`" end @env.ui.info(opts.help, prefix: false) end end end end end vagrant-2.0.2/plugins/commands/snapshot/command/save.rb000066400000000000000000000036261323370221500232040ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandSnapshot module Command class Save < Vagrant.plugin("2", :command) def execute options = {} options[:force] = false opts = OptionParser.new do |o| o.banner = "Usage: vagrant snapshot save [options] [vm-name] " o.separator "" o.separator "Take a snapshot of the current state of the machine. The snapshot" o.separator "can be restored via `vagrant snapshot restore` at any point in the" o.separator "future to get back to this exact machine state." o.separator "" o.separator "Snapshots are useful for experimenting in a machine and being able" o.separator "to rollback quickly." o.on("-f", "--force", "Replace snapshot without confirmation") do |f| options[:force] = f end end # Parse the options argv = parse_options(opts) return if !argv if argv.empty? || argv.length > 2 raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp end name = argv.pop with_target_vms(argv) do |vm| if !vm.provider.capability?(:snapshot_list) raise Vagrant::Errors::SnapshotNotSupported end snapshot_list = vm.provider.capability(:snapshot_list) if !snapshot_list.include? name vm.action(:snapshot_save, snapshot_name: name) elsif options[:force] # not a unique snapshot name vm.action(:snapshot_delete, snapshot_name: name) vm.action(:snapshot_save, snapshot_name: name) else raise Vagrant::Errors::SnapshotConflictFailed end end # Success, exit status 0 0 end end end end end vagrant-2.0.2/plugins/commands/snapshot/plugin.rb000066400000000000000000000005171323370221500221220ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandSnapshot class Plugin < Vagrant.plugin("2") name "snapshot command" description "The `snapshot` command gives you a way to manage snapshots." command("snapshot") do require_relative "command/root" Command::Root end end end end vagrant-2.0.2/plugins/commands/ssh/000077500000000000000000000000001323370221500172325ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/ssh/command.rb000066400000000000000000000046501323370221500212020ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandSSH class Command < Vagrant.plugin("2", :command) def self.synopsis "connects to machine via SSH" end def execute options = {} options[:tty] = true opts = OptionParser.new do |o| o.banner = "Usage: vagrant ssh [options] [name|id] [-- extra ssh args]" o.separator "" o.separator "Options:" o.separator "" o.on("-c", "--command COMMAND", "Execute an SSH command directly") do |c| options[:command] = c end o.on("-p", "--plain", "Plain mode, leaves authentication up to user") do |p| options[:plain_mode] = p end o.on("-t", "--[no-]tty", "Enables tty when executing an ssh command (defaults to true)") do |t| options[:tty] = t end end # Parse out the extra args to send to SSH, which is everything # after the "--" split_index = @argv.index("--") if split_index options[:ssh_args] = @argv.drop(split_index + 1) @argv = @argv.take(split_index) end # Parse the options and return if we don't have any target. argv = parse_options(opts) return if !argv # Execute the actual SSH with_target_vms(argv, single_target: true) do |vm| ssh_opts = { plain_mode: options[:plain_mode], extra_args: options[:ssh_args] } if options[:command] @logger.debug("Executing single command on remote machine: #{options[:command]}") env = vm.action(:ssh_run, ssh_opts: ssh_opts, ssh_run_command: options[:command], tty: options[:tty],) # Exit with the exit status of the command or a 0 if we didn't # get one. exit_status = env[:ssh_run_exit_status] || 0 return exit_status else Vagrant::Bundler.instance.deinit @logger.debug("Invoking `ssh` action on machine") vm.action(:ssh, ssh_opts: ssh_opts) # We should never reach this point, since the point of `ssh` # is to exec into the proper SSH shell, but we'll just return # an exit status of 0 anyways. return 0 end end end end end end vagrant-2.0.2/plugins/commands/ssh/plugin.rb000066400000000000000000000005561323370221500210630ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandSSH class Plugin < Vagrant.plugin("2") name "ssh command" description <<-DESC The `ssh` command allows you to SSH in to your running virtual machine. DESC command("ssh") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-2.0.2/plugins/commands/ssh_config/000077500000000000000000000000001323370221500205575ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/ssh_config/command.rb000066400000000000000000000043111323370221500225210ustar00rootroot00000000000000require 'optparse' require "vagrant/util/safe_puts" require "vagrant/util/platform" module VagrantPlugins module CommandSSHConfig class Command < Vagrant.plugin("2", :command) include Vagrant::Util::SafePuts def self.synopsis "outputs OpenSSH valid configuration to connect to the machine" end def convert_win_paths(paths) paths.map! { |path| Vagrant::Util::Platform.format_windows_path(path, :disable_unc) } end def execute options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant ssh-config [options] [name|id]" o.separator "" o.separator "Options:" o.separator "" o.on("--host NAME", "Name the host for the config") do |h| options[:host] = h end end argv = parse_options(opts) return if !argv with_target_vms(argv) do |machine| ssh_info = machine.ssh_info raise Vagrant::Errors::SSHNotReady if ssh_info.nil? if Vagrant::Util::Platform.windows? ssh_info[:private_key_path] = convert_win_paths(ssh_info[:private_key_path]) end variables = { host_key: options[:host] || machine.name || "vagrant", ssh_host: ssh_info[:host], ssh_port: ssh_info[:port], ssh_user: ssh_info[:username], keys_only: ssh_info[:keys_only], verify_host_key: ssh_info[:verify_host_key], private_key_path: ssh_info[:private_key_path], log_level: ssh_info[:log_level], forward_agent: ssh_info[:forward_agent], forward_x11: ssh_info[:forward_x11], proxy_command: ssh_info[:proxy_command], ssh_command: ssh_info[:ssh_command], forward_env: ssh_info[:forward_env], } # Render the template and output directly to STDOUT template = "commands/ssh_config/config" config = Vagrant::Util::TemplateRenderer.render(template, variables) machine.ui.machine("ssh-config", config) safe_puts(config) safe_puts end # Success, exit status 0 0 end end end end vagrant-2.0.2/plugins/commands/ssh_config/plugin.rb000066400000000000000000000006761323370221500224130ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandSSHConfig class Plugin < Vagrant.plugin("2") name "ssh-config command" description <<-DESC The `ssh-config` command dumps an OpenSSH compatible configuration that can be used to quickly SSH into your virtual machine. DESC command("ssh-config") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-2.0.2/plugins/commands/status/000077500000000000000000000000001323370221500177605ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/status/command.rb000066400000000000000000000032641323370221500217300ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandStatus class Command < Vagrant.plugin("2", :command) def self.synopsis "outputs status of the vagrant machine" end def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant status [name|id]" end # Parse the options argv = parse_options(opts) return if !argv max_name_length = 25 with_target_vms(argv) do |machine| max_name_length = machine.name.length if machine.name.length > max_name_length end state = nil results = [] with_target_vms(argv) do |machine| state = machine.state if !state current_state = machine.state results << "#{machine.name.to_s.ljust(max_name_length)} " + "#{current_state.short_description} (#{machine.provider_name})" opts = { target: machine.name.to_s } @env.ui.machine("provider-name", machine.provider_name, opts) @env.ui.machine("state", current_state.id, opts) @env.ui.machine("state-human-short", current_state.short_description, opts) @env.ui.machine("state-human-long", current_state.long_description, opts) end message = nil if results.length == 1 message = state.long_description else message = I18n.t("vagrant.commands.status.listing") end @env.ui.info(I18n.t("vagrant.commands.status.output", states: results.join("\n"), message: message), prefix: false) # Success, exit status 0 0 end end end end vagrant-2.0.2/plugins/commands/status/plugin.rb000066400000000000000000000006571323370221500216130ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandStatus class Plugin < Vagrant.plugin("2") name "status command" description <<-DESC The `status` command shows what the running state (running/saved/..) is of all your virtual machines in this environment. DESC command("status") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-2.0.2/plugins/commands/suspend/000077500000000000000000000000001323370221500201165ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/suspend/command.rb000066400000000000000000000011311323370221500220550ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandSuspend class Command < Vagrant.plugin("2", :command) def self.synopsis "suspends the machine" end def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant suspend [name|id]" end # Parse the options argv = parse_options(opts) return if !argv @logger.debug("'suspend' each target VM...") with_target_vms(argv) do |vm| vm.action(:suspend) end # Success, exit status 0 0 end end end end vagrant-2.0.2/plugins/commands/suspend/plugin.rb000066400000000000000000000006521323370221500217440ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandSuspend class Plugin < Vagrant.plugin("2") name "suspend command" description <<-DESC The `suspend` command suspends execution and puts it to sleep. The command `resume` returns it to running status. DESC command("suspend") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-2.0.2/plugins/commands/up/000077500000000000000000000000001323370221500170615ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/up/command.rb000066400000000000000000000146231323370221500210320ustar00rootroot00000000000000require 'optparse' require 'set' require "vagrant" require File.expand_path("../start_mixins", __FILE__) module VagrantPlugins module CommandUp class Command < Vagrant.plugin("2", :command) include StartMixins def self.synopsis "starts and provisions the vagrant environment" end def execute options = {} options[:destroy_on_error] = true options[:install_provider] = true options[:parallel] = true options[:provision_ignore_sentinel] = false opts = OptionParser.new do |o| o.banner = "Usage: vagrant up [options] [name|id]" o.separator "" o.separator "Options:" o.separator "" build_start_options(o, options) o.on("--[no-]destroy-on-error", "Destroy machine if any fatal error happens (default to true)") do |destroy| options[:destroy_on_error] = destroy end o.on("--[no-]parallel", "Enable or disable parallelism if provider supports it") do |parallel| options[:parallel] = parallel end o.on("--provider PROVIDER", String, "Back the machine with a specific provider") do |provider| options[:provider] = provider end o.on("--[no-]install-provider", "If possible, install the provider if it isn't installed") do |p| options[:install_provider] = p end end # Parse the options argv = parse_options(opts) return if !argv # Validate the provisioners validate_provisioner_flags!(options, argv) # Go over each VM and bring it up @logger.debug("'Up' each target VM...") # Get the names of the machines we want to bring up names = argv if names.empty? autostart = false @env.vagrantfile.machine_names_and_options.each do |n, o| autostart = true if o.key?(:autostart) o[:autostart] = true if !o.key?(:autostart) names << n.to_s if o[:autostart] end # If we have an autostart key but no names, it means that # all machines are autostart: false and we don't start anything. names = nil if autostart && names.empty? end # Build up the batch job of what we'll do machines = [] if names # To prevent vagrant from attempting to validate a global vms config # (which doesn't exist within the local dir) when attempting to # install a machines provider, this check below will disable the # install_providers function if a user gives us a machine id instead # of the machines name. machine_names = [] with_target_vms(names, provider: options[:provider]){|m| machine_names << m.name } options[:install_provider] = false if !(machine_names - names).empty? # If we're installing providers, then do that. We don't # parallelize this step because it is likely the same provider # anyways. if options[:install_provider] install_providers(names, provider: options[:provider]) end @env.batch(options[:parallel]) do |batch| with_target_vms(names, provider: options[:provider]) do |machine| @env.ui.info(I18n.t( "vagrant.commands.up.upping", name: machine.name, provider: machine.provider_name)) machines << machine batch.action(machine, :up, options) end end end if machines.empty? @env.ui.info(I18n.t("vagrant.up_no_machines")) return 0 end # Output the post-up messages that we have, if any machines.each do |m| next if !m.config.vm.post_up_message next if m.config.vm.post_up_message == "" # Add a newline to separate things. @env.ui.info("", prefix: false) m.ui.success(I18n.t( "vagrant.post_up_message", name: m.name.to_s, message: m.config.vm.post_up_message)) end # Success, exit status 0 0 end protected def install_providers(names, provider: nil) # First create a set of all the providers we need to check for. # Most likely this will be a set of one. providers = Set.new with_target_vms(names, provider: provider) do |machine| # Check if we have this machine in the index entry = @env.machine_index.get(machine.name.to_s) # Get the provider for this machine. This logic isn't completely # straightforward. If we have a forced provider, we always use # that no matter what. If we have an entry in the index (meaning # the machine may be created), we use that provider no matter # what since that will be used by the core. If we have none, then # we ask the Vagrant env what the default provider would be and use # that. # # Note that this logic is a bit redundant if we have "provider" # set but I think its probably cleaner to put this logic in one # place. p = provider p = entry.provider.to_sym if !p && entry p = @env.default_provider( machine: machine.name.to_sym, check_usable: false) if !p # Add it to the set providers.add(p) end # Go through and determine if we can install the providers providers.delete_if do |name| !@env.can_install_provider?(name) end # Install the providers if we have to providers.each do |name| # Find the provider. Ignore if we can't find it, this error # will pop up later in the process. parts = Vagrant.plugin("2").manager.providers[name] next if !parts # If the provider is already installed, then our work here is done cls = parts[0] next if cls.installed? # Some human-friendly output ui = Vagrant::UI::Prefixed.new(@env.ui, "") ui.output(I18n.t( "vagrant.installing_provider", provider: name.to_s)) ui.detail(I18n.t("vagrant.installing_provider_detail")) # Install the provider @env.install_provider(name) end end end end end vagrant-2.0.2/plugins/commands/up/plugin.rb000066400000000000000000000005431323370221500207060ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandUp class Plugin < Vagrant.plugin("2") name "up command" description <<-DESC The `up` command brings the virtual environment up and running. DESC command("up") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-2.0.2/plugins/commands/up/start_mixins.rb000066400000000000000000000033641323370221500221400ustar00rootroot00000000000000require "set" module VagrantPlugins module CommandUp module StartMixins # This adds the standard `start` command line flags to the given # OptionParser, storing the result in the `options` dictionary. # # @param [OptionParser] parser # @param [Hash] options def build_start_options(parser, options) # Setup the defaults options[:provision_types] = nil # Add the options parser.on("--[no-]provision", "Enable or disable provisioning") do |p| options[:provision_enabled] = p options[:provision_ignore_sentinel] = true end parser.on("--provision-with x,y,z", Array, "Enable only certain provisioners, by type or by name.") do |list| options[:provision_types] = list.map { |type| type.to_sym } options[:provision_enabled] = true options[:provision_ignore_sentinel] = true end end # This validates the provisioner flags and raises an exception # if there are invalid ones. def validate_provisioner_flags!(options, argv) if options[:provision_types].nil? return end provisioner_names = Set.new with_target_vms(argv) do |machine| machine.config.vm.provisioners.map(&:name).each do |name| provisioner_names.add(name) end end if (provisioner_names & options[:provision_types]).empty? (options[:provision_types] || []).each do |type| klass = Vagrant.plugin("2").manager.provisioners[type] if !klass raise Vagrant::Errors::ProvisionerFlagInvalid, name: type.to_s end end end end end end end vagrant-2.0.2/plugins/commands/validate/000077500000000000000000000000001323370221500202265ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/validate/command.rb000066400000000000000000000013251323370221500221720ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandValidate class Command < Vagrant.plugin("2", :command) def self.synopsis "validates the Vagrantfile" end def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant validate" end # Parse the options argv = parse_options(opts) return if !argv # Validate the configuration of all machines with_target_vms() do |machine| machine.action_raw(:config_validate, Vagrant::Action::Builtin::ConfigValidate) end @env.ui.info(I18n.t("vagrant.commands.validate.success")) # Success, exit status 0 0 end end end end vagrant-2.0.2/plugins/commands/validate/plugin.rb000066400000000000000000000005471323370221500220570ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandValidate class Plugin < Vagrant.plugin("2") name "validate command" description <<-DESC The `validate` command validates the Vagrantfile. DESC command("validate") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-2.0.2/plugins/commands/version/000077500000000000000000000000001323370221500201225ustar00rootroot00000000000000vagrant-2.0.2/plugins/commands/version/command.rb000066400000000000000000000031071323370221500220660ustar00rootroot00000000000000require "optparse" module VagrantPlugins module CommandVersion class Command < Vagrant.plugin("2", :command) def self.synopsis "prints current and latest Vagrant version" end def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant version" end # Parse the options argv = parse_options(opts) return if !argv # Output the currently installed version instantly. @env.ui.output(I18n.t( "vagrant.version_current", version: Vagrant::VERSION)) @env.ui.machine("version-installed", Vagrant::VERSION) # Load the latest information cp = Vagrant::Util::CheckpointClient.instance.result if !cp @env.ui.output("\n"+I18n.t( "vagrant.version_no_checkpoint")) return 0 end latest = cp["current_version"] # Output latest version @env.ui.output(I18n.t( "vagrant.version_latest", version: latest)) @env.ui.machine("version-latest", latest) # Determine if its a new version, and if so, output some more # information. current = Gem::Version.new(Vagrant::VERSION) latest = Gem::Version.new(latest) if current >= latest @env.ui.success(" \n" + I18n.t( "vagrant.version_latest_installed")) return 0 end # Out of date! Let the user know how to upgrade. @env.ui.output(" \n" + I18n.t( "vagrant.version_upgrade_howto", version: latest.to_s)) 0 end end end end vagrant-2.0.2/plugins/commands/version/plugin.rb000066400000000000000000000006361323370221500217520ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandVersion class Plugin < Vagrant.plugin("2") name "version command" description <<-DESC The `version` command prints the currently installed version as well as the latest available version. DESC command("version") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-2.0.2/plugins/communicators/000077500000000000000000000000001323370221500175175ustar00rootroot00000000000000vagrant-2.0.2/plugins/communicators/ssh/000077500000000000000000000000001323370221500203145ustar00rootroot00000000000000vagrant-2.0.2/plugins/communicators/ssh/communicator.rb000066400000000000000000000631241323370221500233470ustar00rootroot00000000000000require 'logger' require 'pathname' require 'stringio' require 'thread' require 'timeout' require 'log4r' require 'net/ssh' require 'net/ssh/proxy/command' require 'net/scp' require 'vagrant/util/ansi_escape_code_remover' require 'vagrant/util/file_mode' require 'vagrant/util/keypair' require 'vagrant/util/platform' require 'vagrant/util/retryable' module VagrantPlugins module CommunicatorSSH # This class provides communication with the VM via SSH. class Communicator < Vagrant.plugin("2", :communicator) # Marker for start of PTY enabled command output PTY_DELIM_START = "bccbb768c119429488cfd109aacea6b5-pty" # Marker for end of PTY enabled command output PTY_DELIM_END = "bccbb768c119429488cfd109aacea6b5-pty" # Marker for start of regular command output CMD_GARBAGE_MARKER = "41e57d38-b4f7-4e46-9c38-13873d338b86-vagrant-ssh" # These are the exceptions that we retry because they represent # errors that are generally fixed from a retry and don't # necessarily represent immediate failure cases. SSH_RETRY_EXCEPTIONS = [ Errno::EACCES, Errno::EADDRINUSE, Errno::ECONNABORTED, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ENETUNREACH, Errno::EHOSTUNREACH, Errno::EPIPE, Net::SSH::Disconnect, Timeout::Error ] include Vagrant::Util::ANSIEscapeCodeRemover include Vagrant::Util::Retryable def self.match?(machine) # All machines are currently expected to have SSH. true end def initialize(machine) @lock = Mutex.new @machine = machine @logger = Log4r::Logger.new("vagrant::communication::ssh") @connection = nil @inserted_key = false end def wait_for_ready(timeout) Timeout.timeout(timeout) do # Wait for ssh_info to be ready ssh_info = nil while true ssh_info = @machine.ssh_info break if ssh_info sleep 0.5 end # Got it! Let the user know what we're connecting to. @machine.ui.detail("SSH address: #{ssh_info[:host]}:#{ssh_info[:port]}") @machine.ui.detail("SSH username: #{ssh_info[:username]}") ssh_auth_type = "private key" ssh_auth_type = "password" if ssh_info[:password] @machine.ui.detail("SSH auth method: #{ssh_auth_type}") last_message = nil last_message_repeat_at = 0 while true message = nil begin begin connect(retries: 1) return true if ready? rescue Vagrant::Errors::VagrantError => e @logger.info("SSH not ready: #{e.inspect}") raise end rescue Vagrant::Errors::SSHConnectionTimeout message = "Connection timeout." rescue Vagrant::Errors::SSHAuthenticationFailed message = "Authentication failure." rescue Vagrant::Errors::SSHDisconnected message = "Remote connection disconnect." rescue Vagrant::Errors::SSHConnectionRefused message = "Connection refused." rescue Vagrant::Errors::SSHConnectionReset message = "Connection reset." rescue Vagrant::Errors::SSHConnectionAborted message = "Connection aborted." rescue Vagrant::Errors::SSHHostDown message = "Host appears down." rescue Vagrant::Errors::SSHNoRoute message = "Host unreachable." rescue Vagrant::Errors::SSHInvalidShell raise rescue Vagrant::Errors::SSHKeyTypeNotSupported raise rescue Vagrant::Errors::SSHKeyBadOwner raise rescue Vagrant::Errors::SSHKeyBadPermissions raise rescue Vagrant::Errors::SSHInsertKeyUnsupported raise rescue Vagrant::Errors::VagrantError => e # Ignore it, SSH is not ready, some other error. end # If we have a message to show, then show it. We don't show # repeated messages unless they've been repeating longer than # 10 seconds. if message message_at = Time.now.to_f show_message = true if last_message == message show_message = (message_at - last_message_repeat_at) > 10.0 end if show_message @machine.ui.detail("Warning: #{message} Retrying...") last_message = message last_message_repeat_at = message_at end end end end rescue Timeout::Error return false end def ready? @logger.debug("Checking whether SSH is ready...") # Attempt to connect. This will raise an exception if it fails. begin connect @logger.info("SSH is ready!") rescue Vagrant::Errors::VagrantError => e # We catch a `VagrantError` which would signal that something went # wrong expectedly in the `connect`, which means we didn't connect. @logger.info("SSH not up: #{e.inspect}") return false end # Verify the shell is valid if execute("", error_check: false) != 0 raise Vagrant::Errors::SSHInvalidShell end # If we're already attempting to switch out the SSH key, then # just return that we're ready (for Machine#guest). @lock.synchronize do return true if @inserted_key || !machine_config_ssh.insert_key @inserted_key = true end # If we used a password, then insert the insecure key ssh_info = @machine.ssh_info insert = ssh_info[:password] && ssh_info[:private_key_path].empty? ssh_info[:private_key_path].each do |pk| if insecure_key?(pk) insert = true @machine.ui.detail("\n"+I18n.t("vagrant.inserting_insecure_detected")) break end end if insert # If we don't have the power to insert/remove keys, then its an error cap = @machine.guest.capability?(:insert_public_key) && @machine.guest.capability?(:remove_public_key) raise Vagrant::Errors::SSHInsertKeyUnsupported if !cap _pub, priv, openssh = Vagrant::Util::Keypair.create @logger.info("Inserting key to avoid password: #{openssh}") @machine.ui.detail("\n"+I18n.t("vagrant.inserting_random_key")) @machine.guest.capability(:insert_public_key, openssh) # Write out the private key in the data dir so that the # machine automatically picks it up. @machine.data_dir.join("private_key").open("w+") do |f| f.write(priv) end # Remove the old key if it exists @machine.ui.detail(I18n.t("vagrant.inserting_remove_key")) @machine.guest.capability( :remove_public_key, Vagrant.source_root.join("keys", "vagrant.pub").read.chomp) # Done, restart. @machine.ui.detail(I18n.t("vagrant.inserted_key")) @connection.close @connection = nil return ready? end # If we reached this point then we successfully connected true end def execute(command, opts=nil, &block) opts = { error_check: true, error_class: Vagrant::Errors::VagrantError, error_key: :ssh_bad_exit_status, good_exit: 0, command: command, shell: nil, sudo: false, }.merge(opts || {}) opts[:good_exit] = Array(opts[:good_exit]) # Connect via SSH and execute the command in the shell. stdout = "" stderr = "" exit_status = connect do |connection| shell_opts = { sudo: opts[:sudo], shell: opts[:shell], } shell_execute(connection, command, **shell_opts) do |type, data| if type == :stdout stdout += data elsif type == :stderr stderr += data end block.call(type, data) if block end end # Check for any errors if opts[:error_check] && !opts[:good_exit].include?(exit_status) # The error classes expect the translation key to be _key, # but that makes for an ugly configuration parameter, so we # set it here from `error_key` error_opts = opts.merge( _key: opts[:error_key], stdout: stdout, stderr: stderr ) raise opts[:error_class], error_opts end # Return the exit status exit_status end def sudo(command, opts=nil, &block) # Run `execute` but with the `sudo` option. opts = { sudo: true }.merge(opts || {}) execute(command, opts, &block) end def download(from, to=nil) @logger.debug("Downloading: #{from} to #{to}") scp_connect do |scp| scp.download!(from, to) end end def test(command, opts=nil) opts = { error_check: false }.merge(opts || {}) execute(command, opts) == 0 end def upload(from, to) @logger.debug("Uploading: #{from} to #{to}") scp_connect do |scp| if File.directory?(from) # Recurisvely upload directories scp.upload!(from, to, recursive: true) else # Open file read only to fix issue [GH-1036] scp.upload!(File.open(from, "r"), to) end end rescue RuntimeError => e # Net::SCP raises a runtime error for this so the only way we have # to really catch this exception is to check the message to see if # it is something we care about. If it isn't, we re-raise. raise if e.message !~ /Permission denied/ # Otherwise, it is a permission denied, so let's raise a proper # exception raise Vagrant::Errors::SCPPermissionDenied, from: from.to_s, to: to.to_s end protected # Opens an SSH connection and yields it to a block. def connect(**opts) if @connection && !@connection.closed? # There is a chance that the socket is closed despite us checking # 'closed?' above. To test this we need to send data through the # socket. # # We wrap the check itself in a 5 second timeout because there # are some cases where this will just hang. begin Timeout.timeout(5) do @connection.exec!("") end rescue Exception => e @logger.info("Connection errored, not re-using. Will reconnect.") @logger.debug(e.inspect) @connection = nil end # If the @connection is still around, then it is valid, # and we use it. if @connection @logger.debug("Re-using SSH connection.") return yield @connection if block_given? return end end # Get the SSH info for the machine, raise an exception if the # provider is saying that SSH is not ready. ssh_info = @machine.ssh_info raise Vagrant::Errors::SSHNotReady if ssh_info.nil? # Default some options opts[:retries] = 5 if !opts.key?(:retries) # Set some valid auth methods. We disable the auth methods that # we're not using if we don't have the right auth info. auth_methods = ["none", "hostbased"] auth_methods << "publickey" if ssh_info[:private_key_path] auth_methods << "password" if ssh_info[:password] # yanked directly from ruby's Net::SSH, but with `none` last # TODO: Remove this once Vagrant has updated its dependency on Net:SSH # to be > 4.1.0, which should include this fix. cipher_array = Net::SSH::Transport::Algorithms::ALGORITHMS[:encryption].dup if cipher_array.delete("none") cipher_array.push("none") end # Build the options we'll use to initiate the connection via Net::SSH common_connect_opts = { auth_methods: auth_methods, config: false, forward_agent: ssh_info[:forward_agent], send_env: ssh_info[:forward_env], keys_only: ssh_info[:keys_only], verify_host_key: ssh_info[:verify_host_key], password: ssh_info[:password], port: ssh_info[:port], timeout: 15, user_known_hosts_file: [], verbose: :debug, encryption: cipher_array, } # Connect to SSH, giving it a few tries connection = nil begin timeout = 60 @logger.info("Attempting SSH connection...") connection = retryable(tries: opts[:retries], on: SSH_RETRY_EXCEPTIONS) do Timeout.timeout(timeout) do begin # This logger will get the Net-SSH log data for us. ssh_logger_io = StringIO.new ssh_logger = Logger.new(ssh_logger_io) # Setup logging for connections connect_opts = common_connect_opts.dup connect_opts[:logger] = ssh_logger if ssh_info[:private_key_path] connect_opts[:keys] = ssh_info[:private_key_path] end if ssh_info[:proxy_command] connect_opts[:proxy] = Net::SSH::Proxy::Command.new(ssh_info[:proxy_command]) end @logger.info("Attempting to connect to SSH...") @logger.info(" - Host: #{ssh_info[:host]}") @logger.info(" - Port: #{ssh_info[:port]}") @logger.info(" - Username: #{ssh_info[:username]}") @logger.info(" - Password? #{!!ssh_info[:password]}") @logger.info(" - Key Path: #{ssh_info[:private_key_path]}") @logger.debug(" - connect_opts: #{connect_opts}") Net::SSH.start(ssh_info[:host], ssh_info[:username], connect_opts) ensure # Make sure we output the connection log @logger.debug("== Net-SSH connection debug-level log START ==") @logger.debug(ssh_logger_io.string) @logger.debug("== Net-SSH connection debug-level log END ==") end end end rescue Errno::EACCES # This happens on connect() for unknown reasons yet... raise Vagrant::Errors::SSHConnectEACCES rescue Errno::ETIMEDOUT, Timeout::Error # This happens if we continued to timeout when attempting to connect. raise Vagrant::Errors::SSHConnectionTimeout rescue Net::SSH::AuthenticationFailed # This happens if authentication failed. We wrap the error in our # own exception. raise Vagrant::Errors::SSHAuthenticationFailed rescue Net::SSH::Disconnect # This happens if the remote server unexpectedly closes the # connection. This is usually raised when SSH is running on the # other side but can't properly setup a connection. This is # usually a server-side issue. raise Vagrant::Errors::SSHDisconnected rescue Errno::ECONNREFUSED # This is raised if we failed to connect the max amount of times raise Vagrant::Errors::SSHConnectionRefused rescue Errno::ECONNRESET # This is raised if we failed to connect the max number of times # due to an ECONNRESET. raise Vagrant::Errors::SSHConnectionReset rescue Errno::ECONNABORTED # This is raised if we failed to connect the max number of times # due to an ECONNABORTED raise Vagrant::Errors::SSHConnectionAborted rescue Errno::EHOSTDOWN # This is raised if we get an ICMP DestinationUnknown error. raise Vagrant::Errors::SSHHostDown rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH # This is raised if we can't work out how to route traffic. raise Vagrant::Errors::SSHNoRoute rescue Net::SSH::Exception => e # This is an internal error in Net::SSH raise Vagrant::Errors::NetSSHException, message: e.message rescue NotImplementedError # This is raised if a private key type that Net-SSH doesn't support # is used. Show a nicer error. raise Vagrant::Errors::SSHKeyTypeNotSupported end @connection = connection @connection_ssh_info = ssh_info # Yield the connection that is ready to be used and # return the value of the block return yield connection if block_given? end # The shell wrapper command used in shell_execute defined by # the sudo and shell options. def shell_cmd(opts) sudo = opts[:sudo] shell = opts[:shell] # Determine the shell to execute. Prefer the explicitly passed in shell # over the default configured shell. If we are using `sudo` then we # need to wrap the shell in a `sudo` call. cmd = machine_config_ssh.shell cmd = shell if shell cmd = machine_config_ssh.sudo_command.gsub("%c", cmd) if sudo cmd end # Executes the command on an SSH connection within a login shell. def shell_execute(connection, command, **opts) opts = { sudo: false, shell: nil }.merge(opts) sudo = opts[:sudo] @logger.info("Execute: #{command} (sudo=#{sudo.inspect})") exit_status = nil # These variables are used to scrub PTY output if we're in a PTY pty = false pty_stdout = "" # Open the channel so we can execute or command channel = connection.open_channel do |ch| if machine_config_ssh.pty ch.request_pty do |ch2, success| pty = success && command != "" if success @logger.debug("pty obtained for connection") else @logger.warn("failed to obtain pty, will try to continue anyways") end end end marker_found = false data_buffer = '' stderr_marker_found = false stderr_data_buffer = '' ch.exec(shell_cmd(opts)) do |ch2, _| # Setup the channel callbacks so we can get data and exit status ch2.on_data do |ch3, data| # Filter out the clear screen command data = remove_ansi_escape_codes(data) if pty pty_stdout << data else if !marker_found data_buffer << data marker_index = data_buffer.index(CMD_GARBAGE_MARKER) if marker_index marker_found = true data_buffer.slice!(0, marker_index + CMD_GARBAGE_MARKER.size) data.replace(data_buffer) data_buffer = nil end end if block_given? && marker_found && !data.empty? yield :stdout, data end end end ch2.on_extended_data do |ch3, type, data| # Filter out the clear screen command data = remove_ansi_escape_codes(data) @logger.debug("stderr: #{data}") if !stderr_marker_found stderr_data_buffer << data marker_index = stderr_data_buffer.index(CMD_GARBAGE_MARKER) if marker_index marker_found = true stderr_data_buffer.slice!(0, marker_index + CMD_GARBAGE_MARKER.size) data.replace(stderr_data_buffer) data_buffer = nil end end if block_given? && marker_found && !data.empty? yield :stderr, data end end ch2.on_request("exit-status") do |ch3, data| exit_status = data.read_long @logger.debug("Exit status: #{exit_status}") # Close the channel, since after the exit status we're # probably done. This fixes up issues with hanging. ch.close end # Set the terminal ch2.send_data generate_environment_export("TERM", "vt100") # Set SSH_AUTH_SOCK if we are in sudo and forwarding agent. # This is to work around often misconfigured boxes where # the SSH_AUTH_SOCK env var is not preserved. if @connection_ssh_info[:forward_agent] && sudo auth_socket = "" execute("echo; printf $SSH_AUTH_SOCK") do |type, data| if type == :stdout auth_socket += data end end if auth_socket != "" # Make sure we only read the last line which should be # the $SSH_AUTH_SOCK env var we printed. auth_socket = auth_socket.split("\n").last.chomp end if auth_socket == "" @logger.warn("No SSH_AUTH_SOCK found despite forward_agent being set.") else @logger.info("Setting SSH_AUTH_SOCK remotely: #{auth_socket}") ch2.send_data generate_environment_export("SSH_AUTH_SOCK", auth_socket) end end # Output the command. If we're using a pty we have to do # a little dance to make sure we get all the output properly # without the cruft added from pty mode. if pty data = "stty raw -echo\n" data += generate_environment_export("PS1", "") data += generate_environment_export("PS2", "") data += generate_environment_export("PROMPT_COMMAND", "") data += "printf #{PTY_DELIM_START}\n" data += "#{command}\n" data += "exitcode=$?\n" data += "printf #{PTY_DELIM_END}\n" data += "exit $exitcode\n" data = data.force_encoding('ASCII-8BIT') ch2.send_data data else ch2.send_data "printf '#{CMD_GARBAGE_MARKER}'\n(>&2 printf '#{CMD_GARBAGE_MARKER}')\n#{command}\n".force_encoding('ASCII-8BIT') # Remember to exit or this channel will hang open ch2.send_data "exit\n" end # Send eof to let server know we're done ch2.eof! end end begin keep_alive = nil if machine_config_ssh.keep_alive # Begin sending keep-alive packets while we wait for the script # to complete. This avoids connections closing on long-running # scripts. keep_alive = Thread.new do loop do sleep 5 @logger.debug("Sending SSH keep-alive...") connection.send_global_request("keep-alive@openssh.com") end end end # Wait for the channel to complete begin channel.wait rescue Errno::ECONNRESET, IOError @logger.info( "SSH connection unexpected closed. Assuming reboot or something.") exit_status = 0 pty = false rescue Net::SSH::ChannelOpenFailed raise Vagrant::Errors::SSHChannelOpenFail rescue Net::SSH::Disconnect raise Vagrant::Errors::SSHDisconnected end ensure # Kill the keep-alive thread keep_alive.kill if keep_alive end # If we're in a PTY, we now finally parse the output if pty @logger.debug("PTY stdout: #{pty_stdout}") if !pty_stdout.include?(PTY_DELIM_START) || !pty_stdout.include?(PTY_DELIM_END) @logger.error("PTY stdout doesn't include delims") raise Vagrant::Errors::SSHInvalidShell.new end data = pty_stdout[/.*#{PTY_DELIM_START}(.*?)#{PTY_DELIM_END}/m, 1] data ||= "" @logger.debug("PTY stdout parsed: #{data}") yield :stdout, data if block_given? end # Return the final exit status return exit_status end # Opens an SCP connection and yields it so that you can download # and upload files. def scp_connect # Connect to SCP and yield the SCP object connect do |connection| scp = Net::SCP.new(connection) return yield scp end rescue Net::SCP::Error => e # If we get the exit code of 127, then this means SCP is unavailable. raise Vagrant::Errors::SCPUnavailable if e.message =~ /\(127\)/ # Otherwise, just raise the error up raise end # This will test whether path is the Vagrant insecure private key. # # @param [String] path def insecure_key?(path) return false if !path return false if !File.file?(path) source_path = Vagrant.source_root.join("keys", "vagrant") return File.read(path).chomp == source_path.read.chomp end def generate_environment_export(env_key, env_value) template = machine_config_ssh.export_command_template template.sub("%ENV_KEY%", env_key).sub("%ENV_VALUE%", env_value) + "\n" end def machine_config_ssh @machine.config.ssh end end end end vagrant-2.0.2/plugins/communicators/ssh/plugin.rb000066400000000000000000000007361323370221500221450ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommunicatorSSH class Plugin < Vagrant.plugin("2") name "ssh communicator" description <<-DESC This plugin allows Vagrant to communicate with remote machines using SSH as the underlying protocol, powered internally by Ruby's net-ssh library. DESC communicator("ssh") do require File.expand_path("../communicator", __FILE__) Communicator end end end end vagrant-2.0.2/plugins/communicators/winrm/000077500000000000000000000000001323370221500206535ustar00rootroot00000000000000vagrant-2.0.2/plugins/communicators/winrm/command_filter.rb000066400000000000000000000023371323370221500241700ustar00rootroot00000000000000module VagrantPlugins module CommunicatorWinRM # Handles loading and applying all available WinRM command filters class CommandFilter @@cmd_filters = [ "cat", "chmod", "chown", "grep", "rm", "test", "uname", "which", "mkdir", ] # Filter the given Vagrant command to ensure compatibility with Windows # # @param [String] The Vagrant shell command # @returns [String] Windows runnable command or empty string def filter(command) command_filters.each { |c| command = c.filter(command) if c.accept?(command) } command end # All the available Linux command filters # # @returns [Array] All Linux command filter instances def command_filters @command_filters ||= create_command_filters() end private def create_command_filters [].tap do |filters| @@cmd_filters.each do |cmd| require_relative "command_filters/#{cmd}" class_name = "VagrantPlugins::CommunicatorWinRM::CommandFilters::#{cmd.capitalize}" filters << Module.const_get(class_name).new end end end end end end vagrant-2.0.2/plugins/communicators/winrm/command_filters/000077500000000000000000000000001323370221500240215ustar00rootroot00000000000000vagrant-2.0.2/plugins/communicators/winrm/command_filters/cat.rb000066400000000000000000000012471323370221500251210ustar00rootroot00000000000000module VagrantPlugins module CommunicatorWinRM module CommandFilters # Handles the special case of determining the guest OS using cat class Cat def filter(command) # cat /etc/release | grep -i OmniOS # cat /etc/redhat-release # cat /etc/issue | grep 'Core Linux' # cat /etc/release | grep -i SmartOS '' end def accept?(command) # cat works in PowerShell, however we don't want to run Guest # OS detection as this will fail on Windows because the lack of the # grep command command.start_with?('cat /etc/') end end end end end vagrant-2.0.2/plugins/communicators/winrm/command_filters/chmod.rb000066400000000000000000000006321323370221500254410ustar00rootroot00000000000000module VagrantPlugins module CommunicatorWinRM module CommandFilters # Converts a *nix 'chmod' command to a PowerShell equivalent (none) class Chmod def filter(command) # Not supported on Windows, the communicator should skip this command '' end def accept?(command) command.start_with?('chmod ') end end end end end vagrant-2.0.2/plugins/communicators/winrm/command_filters/chown.rb000066400000000000000000000006321323370221500254650ustar00rootroot00000000000000module VagrantPlugins module CommunicatorWinRM module CommandFilters # Converts a *nix 'chown' command to a PowerShell equivalent (none) class Chown def filter(command) # Not supported on Windows, the communicator should skip this command '' end def accept?(command) command.start_with?('chown ') end end end end end vagrant-2.0.2/plugins/communicators/winrm/command_filters/grep.rb000066400000000000000000000011431323370221500253020ustar00rootroot00000000000000module VagrantPlugins module CommunicatorWinRM module CommandFilters # Converts a *nix 'grep' command to a PowerShell equivalent (none) class Grep def filter(command) # grep 'Fedora release [12][67890]' /etc/redhat-release # grep Funtoo /etc/gentoo-release # grep Gentoo /etc/gentoo-release # grep is often used to detect the guest type in Vagrant, so don't bother running # to speed up OS detection '' end def accept?(command) command.start_with?('grep ') end end end end endvagrant-2.0.2/plugins/communicators/winrm/command_filters/mkdir.rb000066400000000000000000000013031323370221500254510ustar00rootroot00000000000000module VagrantPlugins module CommunicatorWinRM module CommandFilters # Converts a *nix 'mkdir' command to a PowerShell equivalent class Mkdir def filter(command) # mkdir -p /some/dir # mkdir /some/dir cmd_parts = command.strip.split(/\s+/) dir = cmd_parts.pop while !dir.nil? && dir.start_with?('-') dir = cmd_parts.pop end # This will ignore any -p switches, which are redundant in PowerShell, # and ambiguous in PowerShell 4+ return "mkdir #{dir} -force" end def accept?(command) command.start_with?('mkdir ') end end end end end vagrant-2.0.2/plugins/communicators/winrm/command_filters/rm.rb000066400000000000000000000022111323370221500247600ustar00rootroot00000000000000module VagrantPlugins module CommunicatorWinRM module CommandFilters # Converts a *nix 'rm' command to a PowerShell equivalent class Rm def filter(command) # rm -Rf /some/dir # rm -R /some/dir # rm -R -f /some/dir # rm -f /some/dir # rm /some/dir cmd_parts = command.strip.split(/\s+/) # Figure out if we need to do this recursively recurse = false cmd_parts.each do |k| argument = k.downcase if argument == '-r' || argument == '-rf' || argument == '-fr' recurse = true break end end # Figure out which argument is the path dir = cmd_parts.pop while !dir.nil? && dir.start_with?('-') dir = cmd_parts.pop end ret_cmd = '' if recurse ret_cmd = "rm #{dir} -recurse -force" else ret_cmd = "rm #{dir} -force" end return ret_cmd end def accept?(command) command.start_with?('rm ') end end end end end vagrant-2.0.2/plugins/communicators/winrm/command_filters/test.rb000066400000000000000000000025771323370221500253400ustar00rootroot00000000000000module VagrantPlugins module CommunicatorWinRM module CommandFilters # Converts a *nix 'test' command to a PowerShell equivalent class Test def filter(command) # test -d /tmp/dir # test -f /tmp/afile # test -L /somelink # test -x /tmp/some.exe cmd_parts = command.strip.split(/\s+/) flag = cmd_parts[1] path = cmd_parts[2] if flag == '-d' check_for_directory(path) elsif flag == '-f' || flag == '-x' check_for_file(path) else check_exists(path) end end def accept?(command) command.start_with?("test ") end private def check_for_directory(path) <<-EOH $p = "#{path}" if ((Test-Path $p) -and (get-item $p).PSIsContainer) { exit 0 } exit 1 EOH end def check_for_file(path) <<-EOH $p = "#{path}" if ((Test-Path $p) -and (!(get-item $p).PSIsContainer)) { exit 0 } exit 1 EOH end def check_exists(path) <<-EOH $p = "#{path}" if (Test-Path $p) { exit 0 } exit 1 EOH end end end end end vagrant-2.0.2/plugins/communicators/winrm/command_filters/uname.rb000066400000000000000000000014001323370221500254460ustar00rootroot00000000000000module VagrantPlugins module CommunicatorWinRM module CommandFilters # Converts a *nix 'uname' command to a PowerShell equivalent (none) class Uname def filter(command) # uname -s | grep 'Darwin' # uname -s | grep VMkernel # uname -s | grep 'FreeBSD' # uname -s | grep 'Linux' # uname -s | grep NetBSD # uname -s | grep 'OpenBSD' # uname -sr | grep SunOS | grep -v 5.11 # uname -sr | grep 'SunOS 5.11' # uname is used to detect the guest type in Vagrant, so don't bother running # to speed up OS detection '' end def accept?(command) command.start_with?('uname ') end end end end end vagrant-2.0.2/plugins/communicators/winrm/command_filters/which.rb000066400000000000000000000011311323370221500254440ustar00rootroot00000000000000module VagrantPlugins module CommunicatorWinRM module CommandFilters # Converts a *nix 'which' command to a PowerShell equivalent class Which def filter(command) executable = command.strip.split(/\s+/)[1] return <<-EOH $command = [Array](Get-Command #{executable} -errorAction SilentlyContinue) if ($null -eq $command) { exit 1 } write-host $command[0].Definition exit 0 EOH end def accept?(command) command.start_with?('which ') end end end end end vagrant-2.0.2/plugins/communicators/winrm/communicator.rb000066400000000000000000000162631323370221500237100ustar00rootroot00000000000000require "log4r" require "tempfile" require "timeout" require_relative "helper" require_relative "shell" require_relative "command_filter" module VagrantPlugins module CommunicatorWinRM # Provides communication channel for Vagrant commands via WinRM. class Communicator < Vagrant.plugin("2", :communicator) include Vagrant::Util def self.match?(machine) # This is useless, and will likely be removed in the future (this # whole method). true end def initialize(machine) @cmd_filter = CommandFilter.new() @logger = Log4r::Logger.new("vagrant::communication::winrm") @machine = machine @shell = nil @logger.info("Initializing WinRMCommunicator") end def wait_for_ready(timeout) Timeout.timeout(timeout) do # Wait for winrm_info to be ready winrm_info = nil while true winrm_info = nil begin winrm_info = Helper.winrm_info(@machine) rescue Errors::WinRMNotReady @logger.debug("WinRM not ready yet; retrying until boot_timeout is reached.") end break if winrm_info sleep 0.5 end # Got it! Let the user know what we're connecting to. @machine.ui.detail("WinRM address: #{shell.host}:#{shell.port}") @machine.ui.detail("WinRM username: #{shell.username}") @machine.ui.detail("WinRM execution_time_limit: #{shell.execution_time_limit}") @machine.ui.detail("WinRM transport: #{shell.config.transport}") last_message = nil last_message_repeat_at = 0 while true message = nil begin begin return true if ready? rescue Vagrant::Errors::VagrantError => e @logger.info("WinRM not ready: #{e.inspect}") raise end rescue Errors::ConnectionTimeout message = "Connection timeout." rescue Errors::AuthenticationFailed message = "Authentication failure." rescue Errors::Disconnected message = "Remote connection disconnect." rescue Errors::ConnectionRefused message = "Connection refused." rescue Errors::ConnectionReset message = "Connection reset." rescue Errors::HostDown message = "Host appears down." rescue Errors::NoRoute message = "Host unreachable." rescue Errors::TransientError => e # Any other retriable errors message = e.message end # If we have a message to show, then show it. We don't show # repeated messages unless they've been repeating longer than # 10 seconds. if message message_at = Time.now.to_f show_message = true if last_message == message show_message = (message_at - last_message_repeat_at) > 10.0 end if show_message @machine.ui.detail("Warning: #{message} Retrying...") last_message = message last_message_repeat_at = message_at end end end end rescue Timeout::Error return false end def ready? @logger.info("Checking whether WinRM is ready...") result = Timeout.timeout(@machine.config.winrm.timeout) do shell(true).cmd("hostname") end @logger.info("WinRM is ready!") return true rescue Errors::TransientError => e # We catch a `TransientError` which would signal that something went # that might work if we wait and retry. @logger.info("WinRM not up: #{e.inspect}") # We reset the shell to trigger calling of winrm_finder again. # This resolves a problem when using vSphere where the winrm_info was not refreshing # thus never getting the correct hostname. @shell = nil return false end def shell(reload=false) @shell = nil if reload @shell ||= create_shell end def execute(command, opts={}, &block) # If this is a *nix command with no Windows equivilant, don't run it command = @cmd_filter.filter(command) return 0 if command.empty? opts = { command: command, elevated: false, error_check: true, error_class: Errors::WinRMBadExitStatus, error_key: nil, # use the error_class message key good_exit: 0, shell: :powershell, interactive: false, }.merge(opts || {}) opts[:shell] = :elevated if opts[:elevated] opts[:good_exit] = Array(opts[:good_exit]) @logger.debug("#{opts[:shell]} executing:\n#{command}") output = shell.send(opts[:shell], command, opts, &block) execution_output(output, opts) end alias_method :sudo, :execute def test(command, opts=nil) # If this is a *nix command (which we know about) with no Windows # equivilant, assume failure command = @cmd_filter.filter(command) return false if command.empty? opts = { command: command, elevated: false, error_check: false, }.merge(opts || {}) # If we're passed a *nix command which PS can't parse we get exit code # 0, but output in stderr. We need to check both exit code and stderr. output = shell.send(:powershell, command) return output.exitcode == 0 && output.stderr.length == 0 end def upload(from, to) @logger.info("Uploading: #{from} to #{to}") shell.upload(from, to) end def download(from, to) @logger.info("Downloading: #{from} to #{to}") shell.download(from, to) end protected # This creates a new WinRMShell based on the information we know # about this machine. def create_shell winrm_info = Helper.winrm_info(@machine) WinRMShell.new( winrm_info[:host], winrm_info[:port], @machine.config.winrm ) end # Handles the raw WinRM shell result and converts it to a # standard Vagrant communicator result def execution_output(output, opts) if opts[:shell] == :wql return output elsif opts[:error_check] && \ !opts[:good_exit].include?(output.exitcode) raise_execution_error(output, opts) end output.exitcode end def raise_execution_error(output, opts) # WinRM can return multiple stderr and stdout entries error_opts = opts.merge( stdout: output.stdout, stderr: output.stderr ) # Use a different error message key if the caller gave us one, # otherwise use the error's default message error_opts[:_key] = opts[:error_key] if opts[:error_key] # Raise the error, use the type the caller gave us or the comm default raise opts[:error_class], error_opts end end #WinRM class end end vagrant-2.0.2/plugins/communicators/winrm/config.rb000066400000000000000000000057761323370221500224640ustar00rootroot00000000000000module VagrantPlugins module CommunicatorWinRM class Config < Vagrant.plugin("2", :config) attr_accessor :username attr_accessor :password attr_accessor :host attr_accessor :port attr_accessor :guest_port attr_accessor :max_tries attr_accessor :retry_delay attr_accessor :timeout attr_accessor :transport attr_accessor :ssl_peer_verification attr_accessor :execution_time_limit attr_accessor :basic_auth_only attr_accessor :codepage def initialize @username = UNSET_VALUE @password = UNSET_VALUE @host = UNSET_VALUE @port = UNSET_VALUE @guest_port = UNSET_VALUE @max_tries = UNSET_VALUE @retry_delay = UNSET_VALUE @timeout = UNSET_VALUE @transport = UNSET_VALUE @ssl_peer_verification = UNSET_VALUE @execution_time_limit = UNSET_VALUE @basic_auth_only = UNSET_VALUE @codepage = UNSET_VALUE end def finalize! @username = "vagrant" if @username == UNSET_VALUE @password = "vagrant" if @password == UNSET_VALUE @transport = :negotiate if @transport == UNSET_VALUE @host = nil if @host == UNSET_VALUE is_ssl = @transport == :ssl @port = (is_ssl ? 5986 : 5985) if @port == UNSET_VALUE @guest_port = (is_ssl ? 5986 : 5985) if @guest_port == UNSET_VALUE @max_tries = 20 if @max_tries == UNSET_VALUE @retry_delay = 2 if @retry_delay == UNSET_VALUE @timeout = 1800 if @timeout == UNSET_VALUE @ssl_peer_verification = true if @ssl_peer_verification == UNSET_VALUE @execution_time_limit = "PT2H" if @execution_time_limit == UNSET_VALUE @basic_auth_only = false if @basic_auth_only == UNSET_VALUE @codepage = nil if @codepage == UNSET_VALUE end def validate(machine) errors = [] errors << "winrm.username cannot be nil." if @username.nil? errors << "winrm.password cannot be nil." if @password.nil? errors << "winrm.port cannot be nil." if @port.nil? errors << "winrm.guest_port cannot be nil." if @guest_port.nil? errors << "winrm.max_tries cannot be nil." if @max_tries.nil? errors << "winrm.retry_delay cannot be nil." if @max_tries.nil? errors << "winrm.timeout cannot be nil." if @timeout.nil? errors << "winrm.execution_time_limit cannot be nil." if @execution_time_limit.nil? unless @ssl_peer_verification == true || @ssl_peer_verification == false errors << "winrm.ssl_peer_verification must be a boolean." end unless @basic_auth_only == true || @basic_auth_only == false errors << "winrm.basic_auth_only must be a boolean." end { "WinRM" => errors } end end end end vagrant-2.0.2/plugins/communicators/winrm/errors.rb000066400000000000000000000030211323370221500225100ustar00rootroot00000000000000module VagrantPlugins module CommunicatorWinRM module Errors # A convenient superclass for all our errors. class WinRMError < Vagrant::Errors::VagrantError error_namespace("vagrant_winrm.errors") end class TransientError < WinRMError end class AuthenticationFailed < WinRMError error_key(:authentication_failed) end class ExecutionError < WinRMError error_key(:execution_error) end class InvalidShell < WinRMError error_key(:invalid_shell) end class WinRMBadExitStatus < WinRMError error_key(:winrm_bad_exit_status) end class WinRMNotReady < WinRMError error_key(:winrm_not_ready) end class WinRMFileTransferError < WinRMError error_key(:winrm_file_transfer_error) end class InvalidTransport < WinRMError error_key(:invalid_transport) end class SSLError < WinRMError error_key(:ssl_error) end class ConnectionTimeout < TransientError error_key(:connection_timeout) end class Disconnected < TransientError error_key(:disconnected) end class ConnectionRefused < TransientError error_key(:connection_refused) end class ConnectionReset < TransientError error_key(:connection_reset) end class HostDown < TransientError error_key(:host_down) end class NoRoute < TransientError error_key(:no_route) end end end end vagrant-2.0.2/plugins/communicators/winrm/helper.rb000066400000000000000000000054221323370221500224620ustar00rootroot00000000000000module VagrantPlugins module CommunicatorWinRM # This is a helper module that provides some functions to the # communicator. This is extracted into a module so that we can # easily unit test these methods. module Helper # Returns the host and port to access WinRM. # # This asks the provider via the `winrm_info` capability if it # exists, otherwise defaulting to its own heuristics. # # @param [Vagrant::Machine] machine # @return [Hash] def self.winrm_info(machine) info = {} if machine.provider.capability?(:winrm_info) info = machine.provider.capability(:winrm_info) raise Errors::WinRMNotReady if !info end info[:host] ||= winrm_address(machine) info[:port] ||= winrm_port(machine, info[:host] == "127.0.0.1") return info end # Returns the address to access WinRM. This does not contain # the port. # # @param [Vagrant::Machine] machine # @return [String] def self.winrm_address(machine) addr = machine.config.winrm.host return addr if addr ssh_info = machine.ssh_info raise Errors::WinRMNotReady if winrm_info_invalid?(ssh_info) return ssh_info[:host] end def self.winrm_info_invalid?(ssh_info) invalid = (!ssh_info || ssh_info[:host].to_s.empty? || ssh_info[:host].start_with?("169.254")) return invalid end # Returns the port to access WinRM. # # @param [Vagrant::Machine] machine # @return [Integer] def self.winrm_port(machine, local=true) host_port = machine.config.winrm.port if machine.config.winrm.guest_port # If we're not requesting a local port, return # the guest port directly. return machine.config.winrm.guest_port if !local # Search by guest port if we can. We use a provider capability # if we have it. Otherwise, we just scan the Vagrantfile defined # ports. port = nil if machine.provider.capability?(:forwarded_ports) machine.provider.capability(:forwarded_ports).each do |host, guest| if guest == machine.config.winrm.guest_port port = host break end end end if !port machine.config.vm.networks.each do |type, netopts| next if type != :forwarded_port next if !netopts[:host] if netopts[:guest] == machine.config.winrm.guest_port port = netopts[:host] break end end end # Set it if we found it host_port = port if port end host_port end end end end vagrant-2.0.2/plugins/communicators/winrm/plugin.rb000066400000000000000000000017501323370221500225010ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommunicatorWinRM autoload :Errors, File.expand_path("../errors", __FILE__) class Plugin < Vagrant.plugin("2") name "winrm communicator" description <<-DESC This plugin allows Vagrant to communicate with remote machines using WinRM. DESC communicator("winrm") do require File.expand_path("../communicator", __FILE__) init! Communicator end config("winrm") do require_relative "config" Config end protected def self.init! return if defined?(@_init) @_init = true # Setup the I18n I18n.load_path << File.expand_path( "templates/locales/comm_winrm.yml", Vagrant.source_root) I18n.reload! # Load the WinRM gem require "vagrant/util/silence_warnings" Vagrant::Util::SilenceWarnings.silence! do require "winrm" end end end end end vagrant-2.0.2/plugins/communicators/winrm/shell.rb000066400000000000000000000157601323370221500223200ustar00rootroot00000000000000require "timeout" require "log4r" require "vagrant/util/retryable" require "vagrant/util/silence_warnings" Vagrant::Util::SilenceWarnings.silence! do require "winrm" end require "winrm-elevated" require "winrm-fs" module VagrantPlugins module CommunicatorWinRM class WinRMShell include Vagrant::Util::Retryable # These are the exceptions that we retry because they represent # errors that are generally fixed from a retry and don't # necessarily represent immediate failure cases. @@exceptions_to_retry_on = [ HTTPClient::KeepAliveDisconnected, WinRM::WinRMHTTPTransportError, WinRM::WinRMAuthorizationError, WinRM::WinRMWSManFault, Errno::EACCES, Errno::EADDRINUSE, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ENETUNREACH, Errno::EHOSTUNREACH, Timeout::Error ] attr_reader :logger attr_reader :host attr_reader :port attr_reader :username attr_reader :password attr_reader :execution_time_limit attr_reader :config def initialize(host, port, config) @logger = Log4r::Logger.new("vagrant::communication::winrmshell") @logger.debug("initializing WinRMShell") @host = host @port = port @username = config.username @password = config.password @execution_time_limit = config.execution_time_limit @config = config end def powershell(command, opts = {}, &block) connection.shell(:powershell) do |shell| execute_with_rescue(shell, command, &block) end end def cmd(command, opts = {}, &block) shell_opts = {} shell_opts[:codepage] = @config.codepage if @config.codepage connection.shell(:cmd, shell_opts) do |shell| execute_with_rescue(shell, command, &block) end end def elevated(command, opts = {}, &block) connection.shell(:elevated) do |shell| shell.interactive_logon = opts[:interactive] || false execute_with_rescue(shell, command, &block) end end def wql(query, opts = {}, &block) retryable(tries: @config.max_tries, on: @@exceptions_to_retry_on, sleep: @config.retry_delay) do connection.run_wql(query) end rescue => e raise_winrm_exception(e, "run_wql", query) end # @param from [Array, String] a single path or folder, or an # array of paths and folders to upload to the guest # @param to [String] a path or folder on the guest to upload to # @return [FixNum] Total size transfered from host to guest def upload(from, to) file_manager = WinRM::FS::FileManager.new(connection) if from.is_a?(Array) # Preserve return FixNum of bytes transfered return_bytes = 0 from.each do |file| return_bytes += file_manager.upload(file, to) end return return_bytes else file_manager.upload(from, to) end end def download(from, to) file_manager = WinRM::FS::FileManager.new(connection) file_manager.download(from, to) end protected def execute_with_rescue(shell, command, &block) handle_output(shell, command, &block) rescue => e raise_winrm_exception(e, shell.class.name.split("::").last, command) end def handle_output(shell, command, &block) output = shell.run(command) do |out, err| block.call(:stdout, out) if block_given? && out block.call(:stderr, err) if block_given? && err end @logger.debug("Output: #{output.inspect}") # Verify that we didn't get a parser error, and if so we should # set the exit code to 1. Parse errors return exit code 0 so we # need to do this. if output.exitcode == 0 if output.stderr.include?("ParserError") @logger.warn("Detected ParserError, setting exit code to 1") output.exitcode = 1 end end return output end def raise_winrm_exception(exception, shell = nil, command = nil) case exception when WinRM::WinRMAuthorizationError raise Errors::AuthenticationFailed, user: @config.username, password: @config.password, endpoint: endpoint, message: exception.message when WinRM::WinRMHTTPTransportError raise Errors::ExecutionError, shell: shell, command: command, message: exception.message when OpenSSL::SSL::SSLError raise Errors::SSLError, message: exception.message when HTTPClient::TimeoutError raise Errors::ConnectionTimeout, message: exception.message when Errno::ETIMEDOUT raise Errors::ConnectionTimeout # This is raised if the connection timed out when Errno::ECONNREFUSED # This is raised if we failed to connect the max amount of times raise Errors::ConnectionRefused when Errno::ECONNRESET # This is raised if we failed to connect the max number of times # due to an ECONNRESET. raise Errors::ConnectionReset when Errno::EHOSTDOWN # This is raised if we get an ICMP DestinationUnknown error. raise Errors::HostDown when Errno::EHOSTUNREACH # This is raised if we can't work out how to route traffic. raise Errors::NoRoute else raise Errors::ExecutionError, shell: shell, command: command, message: exception.message end end def new_connection @logger.info("Attempting to connect to WinRM...") @logger.info(" - Host: #{@host}") @logger.info(" - Port: #{@port}") @logger.info(" - Username: #{@config.username}") @logger.info(" - Transport: #{@config.transport}") client = ::WinRM::Connection.new(endpoint_options) client.logger = @logger client end def connection @connection ||= new_connection end def endpoint case @config.transport.to_sym when :ssl "https://#{@host}:#{@port}/wsman" when :plaintext, :negotiate "http://#{@host}:#{@port}/wsman" else raise Errors::WinRMInvalidTransport, transport: @config.transport end end def endpoint_options { endpoint: endpoint, transport: @config.transport, operation_timeout: @config.timeout, user: @username, password: @password, host: @host, port: @port, basic_auth_only: @config.basic_auth_only, no_ssl_peer_verification: !@config.ssl_peer_verification, retry_delay: @config.retry_delay, retry_limit: @config.max_tries } end end #WinShell class end end vagrant-2.0.2/plugins/communicators/winssh/000077500000000000000000000000001323370221500210325ustar00rootroot00000000000000vagrant-2.0.2/plugins/communicators/winssh/communicator.rb000066400000000000000000000120721323370221500240610ustar00rootroot00000000000000require File.expand_path("../../ssh/communicator", __FILE__) module VagrantPlugins module CommunicatorWinSSH # This class provides communication with a Windows VM running # the Windows native port of OpenSSH class Communicator < VagrantPlugins::CommunicatorSSH::Communicator def initialize(machine) super @logger = Log4r::Logger.new("vagrant::communication::winssh") end # Executes the command on an SSH connection within a login shell. def shell_execute(connection, command, **opts) opts = { sudo: false, shell: nil }.merge(opts) sudo = opts[:sudo] shell = (opts[:shell] || machine_config_ssh.shell).to_s @logger.info("Execute: #{command} (sudo=#{sudo.inspect})") exit_status = nil # Open the channel so we can execute or command channel = connection.open_channel do |ch| marker_found = false data_buffer = '' stderr_marker_found = false stderr_data_buffer = '' tfile = Tempfile.new('vagrant-ssh') remote_ext = shell == "powershell" ? "ps1" : "bat" remote_name = "#{machine_config_ssh.upload_directory}\\#{File.basename(tfile.path)}.#{remote_ext}" if shell == "powershell" base_cmd = "powershell -File #{remote_name}" tfile.puts <<-SCRIPT.force_encoding('ASCII-8BIT') Remove-Item #{remote_name} Write-Host #{CMD_GARBAGE_MARKER} [Console]::Error.WriteLine("#{CMD_GARBAGE_MARKER}") #{command} SCRIPT else base_cmd = remote_name tfile.puts <<-SCRIPT.force_encoding('ASCII-8BIT') ECHO OFF ECHO #{CMD_GARBAGE_MARKER} ECHO #{CMD_GARBAGE_MARKER} 1>&2 #{command} SCRIPT end tfile.close upload(tfile.path, remote_name) tfile.delete base_cmd = shell_cmd(opts.merge(shell: base_cmd)) @logger.debug("Base SSH exec command: #{base_cmd}") ch.exec(base_cmd) do |ch2, _| # Setup the channel callbacks so we can get data and exit status ch2.on_data do |ch3, data| # Filter out the clear screen command data = remove_ansi_escape_codes(data) if !marker_found data_buffer << data marker_index = data_buffer.index(CMD_GARBAGE_MARKER) if marker_index marker_found = true data_buffer.slice!(0, marker_index + CMD_GARBAGE_MARKER.size) data.replace(data_buffer) data_buffer = nil end end if block_given? && marker_found yield :stdout, data end end ch2.on_extended_data do |ch3, type, data| # Filter out the clear screen command data = remove_ansi_escape_codes(data) @logger.debug("stderr: #{data}") if !stderr_marker_found stderr_data_buffer << data marker_index = stderr_data_buffer.index(CMD_GARBAGE_MARKER) if marker_index marker_found = true stderr_data_buffer.slice!(0, marker_index + CMD_GARBAGE_MARKER.size) data.replace(stderr_data_buffer.lstrip) data_buffer = nil end end if block_given? && marker_found yield :stderr, data end end ch2.on_request("exit-status") do |ch3, data| exit_status = data.read_long @logger.debug("Exit status: #{exit_status}") # Close the channel, since after the exit status we're # probably done. This fixes up issues with hanging. ch.close end end end begin keep_alive = nil if @machine.config.ssh.keep_alive # Begin sending keep-alive packets while we wait for the script # to complete. This avoids connections closing on long-running # scripts. keep_alive = Thread.new do loop do sleep 5 @logger.debug("Sending SSH keep-alive...") connection.send_global_request("keep-alive@openssh.com") end end end # Wait for the channel to complete begin channel.wait rescue Errno::ECONNRESET, IOError @logger.info( "SSH connection unexpected closed. Assuming reboot or something.") exit_status = 0 pty = false rescue Net::SSH::ChannelOpenFailed raise Vagrant::Errors::SSHChannelOpenFail rescue Net::SSH::Disconnect raise Vagrant::Errors::SSHDisconnected end ensure # Kill the keep-alive thread keep_alive.kill if keep_alive end # Return the final exit status return exit_status end def machine_config_ssh @machine.config.winssh end end end end vagrant-2.0.2/plugins/communicators/winssh/config.rb000066400000000000000000000017571323370221500226360ustar00rootroot00000000000000require File.expand_path("../../../kernel_v2/config/ssh", __FILE__) module VagrantPlugins module CommunicatorWinSSH class Config < VagrantPlugins::Kernel_V2::SSHConfig attr_accessor :upload_directory def initialize super @upload_directory = UNSET_VALUE end def finalize! @shell = "cmd" if @shell == UNSET_VALUE @sudo_command = "%c" if @sudo_command == UNSET_VALUE @upload_directory = "C:\\Windows\\Temp" if @upload_directory == UNSET_VALUE if @export_command_template == UNSET_VALUE if @shell == "cmd" @export_command_template = 'set %ENV_KEY%="%ENV_VALUE%"' else @export_command_template = '$env:%ENV_KEY%="%ENV_VALUE%"' end end super end def to_s "WINSSH" end # Remove configuration options from regular SSH that are # not used within this communicator undef :forward_x11 undef :pty end end end vagrant-2.0.2/plugins/communicators/winssh/plugin.rb000066400000000000000000000006351323370221500226610ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommunicatorWinSSH class Plugin < Vagrant.plugin("2") name "windows ssh communicator" description <<-DESC DESC communicator("winssh") do require File.expand_path("../communicator", __FILE__) Communicator end config("winssh") do require_relative "config" Config end end end end vagrant-2.0.2/plugins/guests/000077500000000000000000000000001323370221500161465ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/alt/000077500000000000000000000000001323370221500167265ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/alt/cap/000077500000000000000000000000001323370221500174715ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/alt/cap/change_host_name.rb000066400000000000000000000033351323370221500233040ustar00rootroot00000000000000module VagrantPlugins module GuestALT module Cap class ChangeHostName 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 <<-EOH.gsub(/^ {14}/, '') # Save current hostname saved in /etc/hosts CURRENT_HOSTNAME_FULL="$(hostname -f)" CURRENT_HOSTNAME_SHORT="$(hostname -s)" # New hostname to be saved in /etc/hosts 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}' hostnamectl set-hostname --transient '#{name}' else hostname '#{name}' fi # Update ourselves in /etc/hosts if grep -w "$CURRENT_HOSTNAME_FULL" /etc/hosts; then sed -i -e "s/\(\s\)$CURRENT_HOSTNAME_FULL\(\s\)/\1$NEW_HOSTNAME_FULL\2/g" -e "s/\(\s\)$CURRENT_HOSTNAME_FULL$/\1$NEW_HOSTNAME_FULL/g" /etc/hosts fi if grep -w "$CURRENT_HOSTNAME_SHORT" /etc/hosts; then 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 EOH end end end end end end vagrant-2.0.2/plugins/guests/alt/cap/configure_networks.rb000066400000000000000000000125351323370221500237410ustar00rootroot00000000000000require "tempfile" require_relative "../../../../lib/vagrant/util/template_renderer" module VagrantPlugins module GuestALT module Cap class ConfigureNetworks include Vagrant::Util extend Vagrant::Util::GuestInspection::Linux def self.configure_networks(machine, networks) comm = machine.communicate network_scripts_dir = machine.guest.capability(:network_scripts_dir) commands = {:start => [], :middle => [], :end => []} interfaces = machine.guest.capability(:network_interfaces) # Check if NetworkManager is installed on the system nmcli_installed = nmcli?(comm) networks.each.with_index do |network, i| network[:device] = interfaces[network[:interface]] extra_opts = machine.config.vm.networks[i].last.dup if nmcli_installed # Now check if the device is actively being managed by NetworkManager nm_controlled = nm_controlled?(comm, network[:device]) end if !extra_opts.key?(:nm_controlled) extra_opts[:nm_controlled] = !!nm_controlled end extra_opts[:nm_controlled] = case extra_opts[:nm_controlled] when true "yes" when false, nil "no" else extra_opts[:nm_controlled].to_s end if extra_opts[:nm_controlled] == "yes" && !nmcli_installed raise Vagrant::Errors::NetworkManagerNotInstalled, device: network[:device] end # Render a new configuration template_options = network.merge(extra_opts) # ALT expects netmasks to be in the CIDR notation, but users may # specify IPV4 netmasks like "255.255.255.0". This magic converts # the netmask to the proper value. if template_options[:netmask] && template_options[:netmask].to_s.include?(".") template_options[:netmask] = (32-Math.log2((IPAddr.new(template_options[:netmask], Socket::AF_INET).to_i^0xffffffff)+1)).to_i end options_entry = TemplateRenderer.render("guests/alt/network_#{network[:type]}", options: template_options) # Upload the new configuration options_remote_path = "/tmp/vagrant-network-entry-#{network[:device]}-#{Time.now.to_i}-#{i}" ipv4_address_remote_path = "/tmp/vagrant-network-ipv4-address-entry-#{network[:device]}-#{Time.now.to_i}-#{i}" ipv4_route_remote_path = "/tmp/vagrant-network-ipv4-route-entry-#{network[:device]}-#{Time.now.to_i}-#{i}" Tempfile.open("vagrant-alt-configure-networks") do |f| f.binmode f.write(options_entry) f.fsync f.close machine.communicate.upload(f.path, options_remote_path) end # Add the new interface and bring it back up iface_path = "#{network_scripts_dir}/ifaces/#{network[:device]}" if network[:type].to_sym == :static ipv4_address_entry = TemplateRenderer.render("guests/alt/network_ipv4address", options: template_options) # Upload the new ipv4address configuration Tempfile.open("vagrant-alt-configure-ipv4-address") do |f| f.binmode f.write(ipv4_address_entry) f.fsync f.close machine.communicate.upload(f.path, ipv4_address_remote_path) end ipv4_route_entry = TemplateRenderer.render("guests/alt/network_ipv4route", options: template_options) # Upload the new ipv4route configuration Tempfile.open("vagrant-alt-configure-ipv4-route") do |f| f.binmode f.write(ipv4_route_entry) f.fsync f.close machine.communicate.upload(f.path, ipv4_route_remote_path) end end if nm_controlled and extra_opts[:nm_controlled] == "yes" commands[:start] << "nmcli d disconnect iface '#{network[:device]}'" else commands[:start] << "/sbin/ifdown '#{network[:device]}'" end commands[:middle] << "mkdir -p '#{iface_path}'" commands[:middle] << "mv -f '#{options_remote_path}' '#{iface_path}/options'" if network[:type].to_sym == :static commands[:middle] << "mv -f '#{ipv4_address_remote_path}' '#{iface_path}/ipv4address'" commands[:middle] << "mv -f '#{ipv4_route_remote_path}' '#{iface_path}/ipv4route'" end if extra_opts[:nm_controlled] == "no" commands[:end] << "/sbin/ifup '#{network[:device]}'" end end if nmcli_installed commands[:middle] << "((systemctl | grep NetworkManager.service) && systemctl restart NetworkManager) || " \ "(test -f /etc/init.d/NetworkManager && /etc/init.d/NetworkManager restart)" end commands = commands[:start] + commands[:middle] + commands[:end] comm.sudo(commands.join("\n")) comm.wait_for_ready(5) end end end end end vagrant-2.0.2/plugins/guests/alt/cap/flavor.rb000066400000000000000000000034761323370221500213210ustar00rootroot00000000000000module VagrantPlugins module GuestALT module Cap class Flavor def self.flavor(machine) comm = machine.communicate # Read the version file if comm.test("test -f /etc/os-release") name = nil comm.sudo("grep NAME /etc/os-release") do |type, data| if type == :stdout name = data.split("=")[1].gsub!(/\A"|"\Z/, '') end end if !name.nil? and name == "Sisyphus" return :alt end version = nil comm.sudo("grep VERSION_ID /etc/os-release") do |type, data| if type == :stdout verstr = data.split("=")[1] if verstr == "p8" version = 8 elsif verstr =~ /^[[\d]]/ version = verstr.chomp.to_i subversion = verstr.chomp.split(".")[1].to_i if subversion > 90 version += 1 end end end end if version.nil? or version == 0 return :alt else return :"alt_#{version}" end else output = "" comm.sudo("cat /etc/altlinux-release") do |_, data| output = data end # Detect various flavors we care about if output =~ /(ALT SP|ALT Education|ALT Workstation|ALT Workstation K|ALT Linux starter kit)\s*8(\.[1-9])?( .+)?/i return :alt_8 elsif output =~ /ALT\s+8(\.[1-9])?( .+)?\s.+/i return :alt_8 elsif output =~ /ALT Linux p8( .+)?/i return :alt_8 else return :alt end end end end end end end vagrant-2.0.2/plugins/guests/alt/cap/network_scripts_dir.rb000066400000000000000000000003001323370221500241050ustar00rootroot00000000000000module VagrantPlugins module GuestALT module Cap class NetworkScriptsDir def self.network_scripts_dir(machine) "/etc/net" end end end end end vagrant-2.0.2/plugins/guests/alt/cap/rsync.rb000066400000000000000000000004251323370221500211550ustar00rootroot00000000000000module VagrantPlugins module GuestALT module Cap class RSync def self.rsync_install(machine) machine.communicate.sudo <<-EOH.gsub(/^ {12}/, '') apt-get install -y -qq install rsync EOH end end end end end vagrant-2.0.2/plugins/guests/alt/guest.rb000066400000000000000000000003131323370221500203770ustar00rootroot00000000000000module VagrantPlugins module GuestALT class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("cat /etc/altlinux-release") end end end end vagrant-2.0.2/plugins/guests/alt/plugin.rb000066400000000000000000000016531323370221500205560ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestALT class Plugin < Vagrant.plugin("2") name "ALT Platform guest" description "ALT Platform guest support." guest(:alt, :redhat) do require_relative "guest" Guest end guest_capability(:alt, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:alt, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability(:alt, :flavor) do require_relative "cap/flavor" Cap::Flavor end guest_capability(:alt, :network_scripts_dir) do require_relative "cap/network_scripts_dir" Cap::NetworkScriptsDir end guest_capability(:alt, :rsync_install) do require_relative "cap/rsync" Cap::RSync end end end end vagrant-2.0.2/plugins/guests/amazon/000077500000000000000000000000001323370221500174335ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/amazon/cap/000077500000000000000000000000001323370221500201765ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/amazon/cap/flavor.rb000066400000000000000000000005701323370221500220160ustar00rootroot00000000000000module VagrantPlugins module GuestAmazon module Cap class Flavor def self.flavor(machine) # Amazon AMI is a frankenstien RHEL, mainly based on 6 # Maybe in the future if they incoporate RHEL 7 elements # this should be extended to read /etc/os-release or similar return :rhel end end end end end vagrant-2.0.2/plugins/guests/amazon/guest.rb000066400000000000000000000003301323370221500211030ustar00rootroot00000000000000module VagrantPlugins module GuestAmazon class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("grep 'Amazon Linux' /etc/os-release") end end end end vagrant-2.0.2/plugins/guests/amazon/plugin.rb000066400000000000000000000006231323370221500212570ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestAmazon class Plugin < Vagrant.plugin("2") name "Amazon Linux guest" description "Amazon linux guest support." guest(:amazon, :redhat) do require_relative "guest" Guest end guest_capability(:amazon, :flavor) do require_relative "cap/flavor" Cap::Flavor end end end end vagrant-2.0.2/plugins/guests/arch/000077500000000000000000000000001323370221500170635ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/arch/cap/000077500000000000000000000000001323370221500176265ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/arch/cap/change_host_name.rb000066400000000000000000000012651323370221500234410ustar00rootroot00000000000000module VagrantPlugins module GuestArch module Cap class ChangeHostName 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 <<-EOH.gsub(/^ {14}/, "") # Set hostname hostnamectl set-hostname '#{basename}' # Prepend ourselves to /etc/hosts test $? -eq 0 && (grep -w '#{name}' /etc/hosts || { sed -i'' '1i 127.0.0.1\\t#{name}\\t#{basename}' /etc/hosts }) EOH end end end end end end vagrant-2.0.2/plugins/guests/arch/cap/configure_networks.rb000066400000000000000000000036201323370221500240710ustar00rootroot00000000000000require "ipaddr" require "socket" require "tempfile" require_relative "../../../../lib/vagrant/util/template_renderer" module VagrantPlugins module GuestArch module Cap class ConfigureNetworks include Vagrant::Util def self.configure_networks(machine, networks) comm = machine.communicate commands = [] interfaces = machine.guest.capability(:network_interfaces) networks.each.with_index do |network, i| network[:device] = interfaces[network[:interface]] # Arch expects netmasks to be in the "24" or "64", but users may # specify IPV4 netmasks like "255.255.255.0". This magic converts # the netmask to the proper value. if network[:netmask] && network[:netmask].to_s.include?(".") network[:netmask] = (32-Math.log2((IPAddr.new(network[:netmask], Socket::AF_INET).to_i^0xffffffff)+1)).to_i end entry = TemplateRenderer.render("guests/arch/network_#{network[:type]}", options: network, ) remote_path = "/tmp/vagrant-network-#{network[:device]}-#{Time.now.to_i}-#{i}" Tempfile.open("vagrant-arch-configure-networks") do |f| f.binmode f.write(entry) f.fsync f.close comm.upload(f.path, remote_path) end commands << <<-EOH.gsub(/^ {14}/, '').rstrip # Configure #{network[:device]} mv '#{remote_path}' '/etc/netctl/#{network[:device]}' && ip link set '#{network[:device]}' down && netctl restart '#{network[:device]}' && netctl enable '#{network[:device]}' EOH end # Run all the network modification commands in one communicator call. comm.sudo(commands.join(" && \n")) end end end end end vagrant-2.0.2/plugins/guests/arch/cap/nfs.rb000066400000000000000000000016251323370221500207450ustar00rootroot00000000000000module VagrantPlugins module GuestArch module Cap class NFS def self.nfs_client_installed(machine) machine.communicate.test("pacman -Q nfs-utils") end def self.nfs_pre(machine) comm = machine.communicate # There is a bug in NFS where the rpcbind functionality is not started # and it's not a dependency of nfs-utils. Read more here: # # https://bbs.archlinux.org/viewtopic.php?id=193410 # comm.sudo <<-EOH.gsub(/^ {12}/, "") systemctl enable rpcbind && systemctl start rpcbind EOH end def self.nfs_client_install(machine) comm = machine.communicate comm.sudo <<-EOH.gsub(/^ {12}/, "") pacman --noconfirm -Syy && pacman --noconfirm -S nfs-utils ntp EOH end end end end end vagrant-2.0.2/plugins/guests/arch/cap/rsync.rb000066400000000000000000000005321323370221500213110ustar00rootroot00000000000000module VagrantPlugins module GuestArch module Cap class RSync def self.rsync_install(machine) comm = machine.communicate comm.sudo <<-EOH.gsub(/^ {12}/, '') pacman -Sy --noconfirm pacman -S --noconfirm rsync exit $? EOH end end end end end vagrant-2.0.2/plugins/guests/arch/cap/smb.rb000066400000000000000000000006241323370221500207360ustar00rootroot00000000000000module VagrantPlugins module GuestArch module Cap class SMB def self.smb_install(machine) comm = machine.communicate if !comm.test("test -f /usr/bin/mount.cifs") comm.sudo <<-EOH.gsub(/^ {14}/, '') pacman -Sy --noconfirm pacman -S --noconfirm cifs-utils EOH end end end end end end vagrant-2.0.2/plugins/guests/arch/guest.rb000066400000000000000000000003331323370221500205360ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestArch class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("cat /etc/arch-release") end end end end vagrant-2.0.2/plugins/guests/arch/plugin.rb000066400000000000000000000021421323370221500207050ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestArch class Plugin < Vagrant.plugin("2") name "Arch guest" description "Arch guest support." guest(:arch, :linux) do require_relative "guest" Guest end guest_capability(:arch, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:arch, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability(:arch, :nfs_client_install) do require_relative "cap/nfs" Cap::NFS end guest_capability(:arch, :nfs_client_installed) do require_relative "cap/nfs" Cap::NFS end guest_capability(:arch, :nfs_pre) do require_relative "cap/nfs" Cap::NFS end guest_capability(:arch, :rsync_install) do require_relative "cap/rsync" Cap::RSync end guest_capability(:arch, :smb_install) do require_relative "cap/smb" Cap::SMB end end end end vagrant-2.0.2/plugins/guests/atomic/000077500000000000000000000000001323370221500174225ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/atomic/cap/000077500000000000000000000000001323370221500201655ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/atomic/cap/change_host_name.rb000066400000000000000000000012671323370221500240020ustar00rootroot00000000000000module VagrantPlugins module GuestAtomic module Cap class ChangeHostName 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 <<-EOH.gsub(/^ {14}/, "") # Set hostname hostnamectl set-hostname '#{basename}' # Prepend ourselves to /etc/hosts test $? -eq 0 && (grep -w '#{name}' /etc/hosts || { sed -i'' '1i 127.0.0.1\\t#{name}\\t#{basename}' /etc/hosts }) EOH end end end end end end vagrant-2.0.2/plugins/guests/atomic/cap/docker.rb000066400000000000000000000003451323370221500217630ustar00rootroot00000000000000module VagrantPlugins module GuestAtomic module Cap module Docker def self.docker_daemon_running(machine) machine.communicate.test("test -S /run/docker.sock") end end end end end vagrant-2.0.2/plugins/guests/atomic/guest.rb000066400000000000000000000003441323370221500210770ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestAtomic class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("grep 'ostree=' /proc/cmdline") end end end end vagrant-2.0.2/plugins/guests/atomic/plugin.rb000066400000000000000000000010551323370221500212460ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestAtomic class Plugin < Vagrant.plugin("2") name "Atomic Host guest" description "Atomic Host guest support." guest(:atomic, :fedora) do require_relative "guest" Guest end guest_capability(:atomic, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:atomic, :docker_daemon_running) do require_relative "cap/docker" Cap::Docker end end end end vagrant-2.0.2/plugins/guests/bsd/000077500000000000000000000000001323370221500167165ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/bsd/cap/000077500000000000000000000000001323370221500174615ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/bsd/cap/halt.rb000066400000000000000000000006501323370221500207370ustar00rootroot00000000000000module VagrantPlugins module GuestBSD module Cap class Halt def self.halt(machine) begin machine.communicate.sudo("/sbin/shutdown -p now", shell: "sh") rescue IOError, Vagrant::Errors::SSHDisconnected # Do nothing, because it probably means the machine shut down # and SSH connection was lost. end end end end end end vagrant-2.0.2/plugins/guests/bsd/cap/nfs.rb000066400000000000000000000030561323370221500206000ustar00rootroot00000000000000require "shellwords" require "vagrant/util/retryable" module VagrantPlugins module GuestBSD module Cap class NFS extend Vagrant::Util::Retryable # Mount the given NFS folder. def self.mount_nfs_folder(machine, ip, folders) comm = machine.communicate # Mount each folder separately so we can retry. folders.each do |name, opts| # Shellescape the paths in case they do not have special characters. guest_path = Shellwords.escape(opts[:guestpath]) host_path = Shellwords.escape(opts[:hostpath]) # Build the list of mount options. mount_opts = [] mount_opts << "nfsv#{opts[:nfs_version]}" if opts[:nfs_version] mount_opts << "mntudp" if opts[:nfs_udp] if opts[:mount_options] mount_opts = mount_opts + opts[:mount_options].dup end mount_opts = mount_opts.join(",") # Make the directory on the guest. machine.communicate.sudo("mkdir -p #{guest_path}") # Perform the mount operation. command = "/sbin/mount -t nfs -o '#{mount_opts}' #{ip}:#{host_path} #{guest_path}" # Run the command, raising a specific error. retryable(on: Vagrant::Errors::NFSMountFailed, tries: 3, sleep: 5) do machine.communicate.sudo(command, error_class: Vagrant::Errors::NFSMountFailed, shell: "sh", ) end end end end end end end vagrant-2.0.2/plugins/guests/bsd/cap/public_key.rb000066400000000000000000000040151323370221500221340ustar00rootroot00000000000000require "tempfile" require "vagrant/util/shell_quote" module VagrantPlugins module GuestBSD module Cap class PublicKey def self.insert_public_key(machine, contents) comm = machine.communicate contents = contents.strip << "\n" remote_path = "/tmp/vagrant-insert-pubkey-#{Time.now.to_i}" Tempfile.open("vagrant-bsd-insert-public-key") do |f| f.binmode f.write(contents) f.fsync f.close comm.upload(f.path, remote_path) end # Use execute (not sudo) because we want to execute this as the SSH # user (which is "vagrant" by default). comm.execute <<-EOH.gsub(/^ {12}/, "") mkdir -p ~/.ssh chmod 0700 ~/.ssh && cat '#{remote_path}' >> ~/.ssh/authorized_keys && chmod 0600 ~/.ssh/authorized_keys result=$? rm -f '#{remote_path}' exit $result EOH end def self.remove_public_key(machine, contents) comm = machine.communicate contents = contents.strip << "\n" remote_path = "/tmp/vagrant-remove-pubkey-#{Time.now.to_i}" Tempfile.open("vagrant-bsd-remove-public-key") do |f| f.binmode f.write(contents) f.fsync f.close comm.upload(f.path, remote_path) end # Use execute (not sudo) because we want to execute this as the SSH # user (which is "vagrant" by default). comm.execute <<-EOH.sub(/^ {12}/, "") result=0 if test -f ~/.ssh/authorized_keys; then grep -v -x -f '#{remote_path}' ~/.ssh/authorized_keys > ~/.ssh/authorized_keys.tmp && mv ~/.ssh/authorized_keys.tmp ~/.ssh/authorized_keys && chmod 0600 ~/.ssh/authorized_keys result=$? fi rm -f '#{remote_path}' exit $result EOH end end end end end vagrant-2.0.2/plugins/guests/bsd/cap/virtualbox.rb000066400000000000000000000011171323370221500222050ustar00rootroot00000000000000module 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 vagrant-2.0.2/plugins/guests/bsd/guest.rb000066400000000000000000000003121323370221500203660ustar00rootroot00000000000000module VagrantPlugins module GuestBSD class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("uname -s | grep -i 'BSD'") end end end end vagrant-2.0.2/plugins/guests/bsd/plugin.rb000066400000000000000000000015651323370221500205500ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestBSD class Plugin < Vagrant.plugin("2") name "BSD-based guest" description "BSD-based guest support." guest(:bsd) do require_relative "guest" Guest end guest_capability(:bsd, :halt) do require_relative "cap/halt" Cap::Halt end guest_capability(:bsd, :insert_public_key) do require_relative "cap/public_key" Cap::PublicKey end guest_capability(:bsd, :mount_nfs_folder) do require_relative "cap/nfs" Cap::NFS end guest_capability(:bsd, :mount_virtualbox_shared_folder) do require_relative "cap/virtualbox" Cap::VirtualBox end guest_capability(:bsd, :remove_public_key) do require_relative "cap/public_key" Cap::PublicKey end end end end vagrant-2.0.2/plugins/guests/coreos/000077500000000000000000000000001323370221500174405ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/coreos/cap/000077500000000000000000000000001323370221500202035ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/coreos/cap/change_host_name.rb000066400000000000000000000012471323370221500240160ustar00rootroot00000000000000module VagrantPlugins module GuestCoreOS module Cap class ChangeHostName 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}'") # 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 end end vagrant-2.0.2/plugins/guests/coreos/cap/configure_networks.rb000066400000000000000000000052371323370221500244540ustar00rootroot00000000000000require "tempfile" require_relative "../../../../lib/vagrant/util/template_renderer" module VagrantPlugins module GuestCoreOS module Cap class ConfigureNetworks include Vagrant::Util def self.configure_networks(machine, networks) machine.communicate.tap do |comm| # Read network interface names interfaces = [] comm.sudo("ifconfig | grep '(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) 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") end # Build a list of commands commands = [] # Stop default systemd commands << "systemctl stop etcd" # Configure interfaces # FIXME: fix matching of interfaces with IP adresses networks.each do |network| iface = interfaces[network[:interface].to_i] commands << "ifconfig #{iface} #{network[:ip]} netmask #{network[:netmask]}".squeeze(" ") end commands << <<-EOH.gsub(/^ {14}/, '') mv /tmp/etcd-cluster.service /media/state/units/ systemctl restart local-enable.service # Restart default etcd systemctl start etcd EOH # Run all network configuration commands in one communicator session. comm.sudo(commands.join("\n")) end end private def self.get_ip(machine) ip = nil machine.config.vm.networks.each do |type, opts| if type == :private_network && opts[:ip] ip = opts[:ip] break end end ip end end end end end vagrant-2.0.2/plugins/guests/coreos/cap/docker.rb000066400000000000000000000003451323370221500220010ustar00rootroot00000000000000module VagrantPlugins module GuestCoreOS module Cap module Docker def self.docker_daemon_running(machine) machine.communicate.test("test -S /run/docker.sock") end end end end end vagrant-2.0.2/plugins/guests/coreos/guest.rb000066400000000000000000000003541323370221500211160ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestCoreOS class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("cat /etc/os-release | grep ID=coreos") end end end end vagrant-2.0.2/plugins/guests/coreos/plugin.rb000066400000000000000000000012661323370221500212700ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestCoreOS class Plugin < Vagrant.plugin("2") name "CoreOS guest" description "CoreOS guest support." guest(:coreos, :linux) do require_relative "guest" Guest end guest_capability(:coreos, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:coreos, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability(:coreos, :docker_daemon_running) do require_relative "cap/docker" Cap::Docker end end end end vagrant-2.0.2/plugins/guests/darwin/000077500000000000000000000000001323370221500174325ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/darwin/cap/000077500000000000000000000000001323370221500201755ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/darwin/cap/change_host_name.rb000066400000000000000000000021771323370221500240130ustar00rootroot00000000000000module VagrantPlugins module GuestDarwin module Cap class ChangeHostName def self.change_host_name(machine, name) comm = machine.communicate if !comm.test("hostname -f | grep '^#{name}$'", sudo: false) basename = name.split(".", 2)[0] # LocalHostName should not contain dots - it is used by Bonjour and # visible through file sharing services. comm.sudo <<-EOH.gsub(/^ */, '') # Set hostname scutil --set ComputerName '#{name}' && scutil --set HostName '#{name}' && scutil --set LocalHostName '#{basename}' result=$? if [ $result -ne 0 ]; then exit $result fi hostname '#{name}' # Prepend ourselves to /etc/hosts - sed on bsd is sad grep -w '#{name}' /etc/hosts || { echo -e '127.0.0.1\\t#{name}\\t#{basename}' | cat - /etc/hosts > /tmp/tmp-hosts && mv /tmp/tmp-hosts /etc/hosts } EOH end end end end end end vagrant-2.0.2/plugins/guests/darwin/cap/choose_addressable_ip_addr.rb000066400000000000000000000007041323370221500260160ustar00rootroot00000000000000module VagrantPlugins module GuestDarwin module Cap module ChooseAddressableIPAddr def self.choose_addressable_ip_addr(machine, possible) comm = machine.communicate possible.each do |ip| if comm.test("ping -c1 -t1 #{ip}") return ip end end # If we got this far, there are no addressable IPs return nil end end end end end vagrant-2.0.2/plugins/guests/darwin/cap/configure_networks.rb000066400000000000000000000112621323370221500244410ustar00rootroot00000000000000require "tempfile" require "vagrant/util/template_renderer" module VagrantPlugins module GuestDarwin module Cap class ConfigureNetworks @@logger = Log4r::Logger.new("vagrant::guest::darwin::configure_networks") include Vagrant::Util def self.configure_networks(machine, networks) if !machine.provider.capability?(:nic_mac_addresses) raise Vagrant::Errors::CantReadMACAddresses, provider: machine.provider_name.to_s end nic_mac_addresses = machine.provider.capability(:nic_mac_addresses) @@logger.debug("mac addresses: #{nic_mac_addresses.inspect}") mac_service_map = create_mac_service_map(machine) networks.each do |network| mac_address = nic_mac_addresses[network[:interface]+1] if mac_address.nil? @@logger.warn("Could not find mac address for network #{network.inspect}") next end service_name = mac_service_map[mac_address] if service_name.nil? @@logger.warn("Could not find network service for mac address #{mac_address}") next end network_type = network[:type].to_sym case network_type.to_sym when :static command = "networksetup -setmanual \"#{service_name}\" #{network[:ip]} #{network[:netmask]} #{network[:router]}" when :static6 command = "networksetup -setv6manual \"#{service_name}\" #{network[:ip]} #{network[:netmask]} #{network[:router]}" when :dhcp command = "networksetup -setdhcp \"#{service_name}\"" when :dhcp6 # This is not actually possible yet in Vagrant, but when we do # enable IPv6 across the board, Darwin will already have support. command = "networksetup -setv6automatic \"#{service_name}\"" else raise Vagrant::Errors::NetworkTypeNotSupported, type: network_type end machine.communicate.sudo(command) end end # Creates a hash mapping MAC addresses to network service name # Example: { "00C100A1B2C3" => "Thunderbolt Ethernet" } def self.create_mac_service_map(machine) tmp_ints = File.join(Dir.tmpdir, File.basename("#{machine.id}.interfaces")) tmp_hw = File.join(Dir.tmpdir, File.basename("#{machine.id}.hardware")) machine.communicate.tap do |comm| comm.sudo("networksetup -detectnewhardware") comm.sudo("networksetup -listnetworkserviceorder > /tmp/vagrant.interfaces") comm.sudo("networksetup -listallhardwareports > /tmp/vagrant.hardware") comm.download("/tmp/vagrant.interfaces", tmp_ints) comm.download("/tmp/vagrant.hardware", tmp_hw) end interface_map = {} ints = ::IO.read(tmp_ints) ints.split(/\n\n/m).each do |i| if i.match(/Hardware/) && i.match(/Ethernet/) # Ethernet, should be 2 lines, # (3) Thunderbolt Ethernet # (Hardware Port: Thunderbolt Ethernet, Device: en1) # multiline, should match "Thunderbolt Ethernet", "en1" devicearry = i.match(/\([0-9]+\) (.+)\n.*Device: (.+)\)/m) service = devicearry[1] interface = devicearry[2] # Should map interface to service { "en1" => "Thunderbolt Ethernet" } interface_map[interface] = service end end File.delete(tmp_ints) mac_service_map = {} macs = ::IO.read(tmp_hw) macs.split(/\n\n/m).each do |i| if i.match(/Hardware/) && i.match(/Ethernet/) # Ethernet, should be 3 lines, # Hardware Port: Thunderbolt 1 # Device: en1 # Ethernet Address: a1:b2:c3:d4:e5:f6 # multiline, should match "en1", "00:c1:00:a1:b2:c3" devicearry = i.match(/Device: (.+)\nEthernet Address: (.+)/m) interface = devicearry[1] naked_mac = devicearry[2].gsub(':','').upcase # Skip hardware ports without MAC (bridges, bluetooth, etc.) next if naked_mac == "N/A" if !interface_map[interface] @@logger.warn("Could not find network service for interface #{interface}") next end # Should map MAC to service, { "00C100A1B2C3" => "Thunderbolt Ethernet" } mac_service_map[naked_mac] = interface_map[interface] end end File.delete(tmp_hw) mac_service_map end end end end end vagrant-2.0.2/plugins/guests/darwin/cap/halt.rb000066400000000000000000000010531323370221500214510ustar00rootroot00000000000000module VagrantPlugins module GuestDarwin module Cap class Halt def self.halt(machine) begin # Darwin does not support the `-p` option like the rest of the # BSD-based guests, so it needs its own cap. machine.communicate.sudo("/sbin/shutdown -h now") rescue IOError, Vagrant::Errors::SSHDisconnected # Do nothing because SSH connection closed and it probably # means the VM just shut down really fast. end end end end end end vagrant-2.0.2/plugins/guests/darwin/cap/mount_smb_shared_folder.rb000066400000000000000000000030341323370221500254060ustar00rootroot00000000000000require "vagrant/util/retryable" require "shellwords" module VagrantPlugins module GuestDarwin module Cap class MountSMBSharedFolder extend Vagrant::Util::Retryable def self.mount_smb_shared_folder(machine, name, guestpath, options) expanded_guest_path = machine.guest.capability(:shell_expand_guest_path, guestpath) mount_point_owner = options[:owner]; if mount_point_owner machine.communicate.sudo("mkdir -p #{expanded_guest_path}") machine.communicate.sudo("chown #{mount_point_owner} #{expanded_guest_path}") else # fallback to assumption that user has permission # to create the specified mountpoint machine.communicate.execute("mkdir -p #{expanded_guest_path}") end smb_password = Shellwords.shellescape(options[:smb_password]) # Ensure password is scrubbed Vagrant::Util::CredentialScrubber.sensitive(smb_password) mount_options = options[:mount_options]; mount_command = "mount -t smbfs " + (mount_options ? "-o '#{mount_options.join(",")}' " : "") + "'//#{options[:smb_username]}:#{smb_password}@#{options[:smb_host]}/#{name}' " + "#{expanded_guest_path}" retryable(on: Vagrant::Errors::DarwinMountFailed, tries: 10, sleep: 2) do machine.communicate.execute( mount_command, error_class: Vagrant::Errors::DarwinMountFailed) end end end end end end vagrant-2.0.2/plugins/guests/darwin/cap/mount_vmware_shared_folder.rb000066400000000000000000000023661323370221500261350ustar00rootroot00000000000000module VagrantPlugins module GuestDarwin module Cap class MountVmwareSharedFolder # we seem to be unable to ask 'mount -t vmhgfs' to mount the roots # of specific shares, so instead we symlink from what is already # mounted by the guest tools # (ie. the behaviour of the VMware_fusion provider prior to 0.8.x) def self.mount_vmware_shared_folder(machine, name, guestpath, options) machine.communicate.tap do |comm| # clear prior symlink if comm.test("test -L \"#{guestpath}\"", sudo: true) comm.sudo("rm -f \"#{guestpath}\"") end # clear prior directory if exists if comm.test("test -d \"#{guestpath}\"", sudo: true) comm.sudo("rm -Rf \"#{guestpath}\"") end # create intermediate directories if needed intermediate_dir = File.dirname(guestpath) if !comm.test("test -d \"#{intermediate_dir}\"", sudo: true) comm.sudo("mkdir -p \"#{intermediate_dir}\"") end # finally make the symlink comm.sudo("ln -s \"/Volumes/VMware Shared Folders/#{name}\" \"#{guestpath}\"") end end end end end end vagrant-2.0.2/plugins/guests/darwin/cap/rsync.rb000066400000000000000000000003541323370221500216620ustar00rootroot00000000000000require_relative "../../../synced_folders/rsync/default_unix_cap" module VagrantPlugins module GuestDarwin module Cap class RSync extend VagrantPlugins::SyncedFolderRSync::DefaultUnixCap end end end end vagrant-2.0.2/plugins/guests/darwin/cap/shell_expand_guest_path.rb000066400000000000000000000014131323370221500254120ustar00rootroot00000000000000module VagrantPlugins module GuestDarwin module Cap class ShellExpandGuestPath def self.shell_expand_guest_path(machine, path) real_path = nil path = path.gsub(/ /, '\ ') machine.communicate.execute("printf #{path}") do |type, data| if type == :stdout real_path ||= "" real_path += data end end if !real_path # If no real guest path was detected, this is really strange # and we raise an exception because this is a bug. raise Vagrant::Errors::ShellExpandFailed end # Chomp the string so that any trailing newlines are killed return real_path.chomp end end end end end vagrant-2.0.2/plugins/guests/darwin/cap/verify_vmware_hgfs.rb000066400000000000000000000005001323370221500244110ustar00rootroot00000000000000module VagrantPlugins module GuestDarwin module Cap class VerifyVmwareHgfs def self.verify_vmware_hgfs(machine) kext_bundle_id = "com.vmware.kext.vmhgfs" machine.communicate.test("kextstat -b #{kext_bundle_id} -l | grep #{kext_bundle_id}") end end end end end vagrant-2.0.2/plugins/guests/darwin/guest.rb000066400000000000000000000006351323370221500211120ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestDarwin # A general Vagrant system implementation for OS X (ie. "Darwin"). # # Contributed by: - Brian Johnson # - Tim Sutton class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("uname -s | grep 'Darwin'") end end end end vagrant-2.0.2/plugins/guests/darwin/plugin.rb000066400000000000000000000036341323370221500212630ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestDarwin class Plugin < Vagrant.plugin("2") name "Darwin guest" description "Darwin guest support." guest(:darwin, :bsd) do require_relative "guest" Guest end guest_capability(:darwin, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:darwin, :choose_addressable_ip_addr) do require_relative "cap/choose_addressable_ip_addr" Cap::ChooseAddressableIPAddr end guest_capability(:darwin, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability(:darwin, :halt) do require_relative "cap/halt" Cap::Halt end guest_capability(:darwin, :mount_smb_shared_folder) do require_relative "cap/mount_smb_shared_folder" Cap::MountSMBSharedFolder end guest_capability(:darwin, :mount_vmware_shared_folder) do require_relative "cap/mount_vmware_shared_folder" Cap::MountVmwareSharedFolder end guest_capability(:darwin, :rsync_installed) do require_relative "cap/rsync" Cap::RSync end guest_capability(:darwin, :rsync_command) do require_relative "cap/rsync" Cap::RSync end guest_capability(:darwin, :rsync_post) do require_relative "cap/rsync" Cap::RSync end guest_capability(:darwin, :rsync_pre) do require_relative "cap/rsync" Cap::RSync end guest_capability(:darwin, :shell_expand_guest_path) do require_relative "cap/shell_expand_guest_path" Cap::ShellExpandGuestPath end guest_capability(:darwin, :verify_vmware_hgfs) do require_relative "cap/verify_vmware_hgfs" Cap::VerifyVmwareHgfs end end end end vagrant-2.0.2/plugins/guests/debian/000077500000000000000000000000001323370221500173705ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/debian/cap/000077500000000000000000000000001323370221500201335ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/debian/cap/change_host_name.rb000066400000000000000000000022631323370221500237450ustar00rootroot00000000000000module VagrantPlugins module GuestDebian module Cap class ChangeHostName 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 <<-EOH.gsub(/^ {14}/, '') # Set the hostname echo '#{basename}' > /etc/hostname hostname -F /etc/hostname if command -v hostnamectl; then hostnamectl set-hostname '#{basename}' fi # Prepend ourselves to /etc/hosts grep -w '#{name}' /etc/hosts || { sed -i'' '1i 127.0.0.1\\t#{name}\\t#{basename}' /etc/hosts } # Update mailname echo '#{name}' > /etc/mailname # Restart hostname services if test -f /etc/init.d/hostname; then /etc/init.d/hostname start || true fi if test -f /etc/init.d/hostname.sh; then /etc/init.d/hostname.sh start || true fi EOH end end end end end end vagrant-2.0.2/plugins/guests/debian/cap/configure_networks.rb000066400000000000000000000152101323370221500243740ustar00rootroot00000000000000require "tempfile" require_relative "../../../../lib/vagrant/util/template_renderer" module VagrantPlugins module GuestDebian module Cap class ConfigureNetworks include Vagrant::Util extend Vagrant::Util::GuestInspection::Linux NETPLAN_DEFAULT_VERSION = 2 NETPLAN_DEFAULT_RENDERER = "networkd".freeze NETPLAN_DIRECTORY = "/etc/netplan".freeze NETWORKD_DIRECTORY = "/etc/systemd/network".freeze def self.configure_networks(machine, networks) comm = machine.communicate interfaces = machine.guest.capability(:network_interfaces) if systemd?(comm) if netplan?(comm) configure_netplan(machine, interfaces, comm, networks) elsif systemd_networkd?(comm) configure_networkd(machine, interfaces, comm, networks) else configure_nettools(machine, interfaces, comm, networks) end else configure_nettools(machine, interfaces, comm, networks) end end # Configure networking using netplan def self.configure_netplan(machine, interfaces, comm, networks) ethernets = {}.tap do |e_nets| networks.each do |network| e_config = {}.tap do |entry| if network[:ip] mask = network[:netmask] if mask && IPAddr.new(network[:ip]).ipv4? begin mask = IPAddr.new(mask).to_i.to_s(2).count("1") rescue IPAddr::Error # ignore and use given value end end entry["addresses"] = [[network[:ip], mask].compact.join("/")] else entry["dhcp4"] = true end if network[:gateway] entry["gateway4"] = network[:gateway] end end e_nets[interfaces[network[:interface]]] = e_config end end np_config = {"network" => {"version" => NETPLAN_DEFAULT_VERSION, "renderer" => NETPLAN_DEFAULT_RENDERER, "ethernets" => ethernets}} remote_path = upload_tmp_file(comm, np_config.to_yaml) dest_path = "#{NETPLAN_DIRECTORY}/99-vagrant.yaml" comm.sudo(["mv -f '#{remote_path}' '#{dest_path}'", "chown root:root '#{dest_path}'", "chmod 0644 '#{dest_path}'", "netplan apply"].join("\n")) end # Configure guest networking using networkd def self.configure_networkd(machine, interfaces, comm, networks) net_conf = [] root_device = interfaces.first networks.each do |network| dev_name = interfaces[network[:interface]] net_conf << "[Match]" net_conf << "Name=#{dev_name}" net_conf << "[Network]" if network[:ip] mask = network[:netmask] if mask && IPAddr.new(network[:ip]).ipv4? begin mask = IPAddr.new(mask).to_i.to_s(2).count("1") rescue IPAddr::Error # ignore and use given value end end address = [network[:ip], mask].compact.join("/") net_conf << "DHCP=no" net_conf << "Address=#{address}" net_conf << "Gateway=#{network[:gateway]}" if network[:gateway] else net_conf << "DHCP=yes" end end remote_path = upload_tmp_file(comm, net_conf.join("\n")) dest_path = "#{NETWORKD_DIRECTORY}/99-vagrant.network" comm.sudo(["mkdir -p #{NETWORKD_DIRECTORY}", "mv -f '#{remote_path}' '#{dest_path}'", "chown root:root '#{dest_path}'", "chmod 0644 '#{dest_path}'", "systemctl restart systemd-networkd.service"].join("\n")) end # Configure guest networking using net-tools def self.configure_nettools(machine, interfaces, comm, networks) commands = [] entries = [] root_device = interfaces.first networks.each do |network| network[:device] = interfaces[network[:interface]] entry = TemplateRenderer.render("guests/debian/network_#{network[:type]}", options: network.merge(:root_device => root_device), ) entries << entry end content = entries.join("\n") remote_path = "/tmp/vagrant-network-entry" upload_tmp_file(comm, content, remote_path) networks.each do |network| # Ubuntu 16.04+ returns an error when downing an interface that # does not exist. The `|| true` preserves the behavior that older # Ubuntu versions exhibit and Vagrant expects (GH-7155) commands << "/sbin/ifdown '#{network[:device]}' || true" commands << "/sbin/ip addr flush dev '#{network[:device]}'" end # Reconfigure /etc/network/interfaces. commands << <<-EOH.gsub(/^ {12}/, "") # Remove any previous network modifications from the interfaces file sed -e '/^#VAGRANT-BEGIN/,$ d' /etc/network/interfaces > /tmp/vagrant-network-interfaces.pre sed -ne '/^#VAGRANT-END/,$ p' /etc/network/interfaces | tac | sed -e '/^#VAGRANT-END/,$ d' | tac > /tmp/vagrant-network-interfaces.post cat \\ /tmp/vagrant-network-interfaces.pre \\ /tmp/vagrant-network-entry \\ /tmp/vagrant-network-interfaces.post \\ > /etc/network/interfaces rm -f /tmp/vagrant-network-interfaces.pre rm -f /tmp/vagrant-network-entry rm -f /tmp/vagrant-network-interfaces.post EOH # Bring back up each network interface, reconfigured. networks.each do |network| commands << "/sbin/ifup '#{network[:device]}'" end comm.sudo(commands.join("\n")) end # Simple helper to upload content to guest temporary file # # @param [Vagrant::Plugin::Communicator] comm # @param [String] content # @return [String] remote path def self.upload_tmp_file(comm, content, remote_path=nil) if remote_path.nil? remote_path = "/tmp/vagrant-network-entry-#{Time.now.to_i}" end Tempfile.open("vagrant-debian-configure-networks") do |f| f.binmode f.write(content) f.fsync f.close comm.upload(f.path, remote_path) end remote_path end end end end end vagrant-2.0.2/plugins/guests/debian/cap/nfs.rb000066400000000000000000000005501323370221500212460ustar00rootroot00000000000000module VagrantPlugins module GuestDebian module Cap class NFS def self.nfs_client_install(machine) comm = machine.communicate comm.sudo <<-EOH.gsub(/^ {12}/, '') apt-get -yqq update apt-get -yqq install nfs-common portmap exit $? EOH end end end end end vagrant-2.0.2/plugins/guests/debian/cap/rsync.rb000066400000000000000000000005041323370221500216150ustar00rootroot00000000000000module VagrantPlugins module GuestDebian module Cap class RSync def self.rsync_install(machine) comm = machine.communicate comm.sudo <<-EOH.gsub(/^ {14}/, '') apt-get -yqq update apt-get -yqq install rsync EOH end end end end end vagrant-2.0.2/plugins/guests/debian/cap/smb.rb000066400000000000000000000006171323370221500212450ustar00rootroot00000000000000module VagrantPlugins module GuestDebian module Cap class SMB def self.smb_install(machine) comm = machine.communicate if !comm.test("test -f /sbin/mount.cifs") comm.sudo <<-EOH.gsub(/^ {14}/, '') apt-get -yqq update apt-get -yqq install cifs-utils EOH end end end end end end vagrant-2.0.2/plugins/guests/debian/guest.rb000066400000000000000000000003471323370221500210500ustar00rootroot00000000000000require_relative '../linux/guest' module VagrantPlugins module GuestDebian class Guest < VagrantPlugins::GuestLinux::Guest # Name used for guest detection GUEST_DETECTION_NAME = "debian".freeze end end end vagrant-2.0.2/plugins/guests/debian/plugin.rb000066400000000000000000000016231323370221500212150ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestDebian class Plugin < Vagrant.plugin("2") name "Debian guest" description "Debian guest support." guest(:debian, :linux) do require_relative "guest" Guest end guest_capability(:debian, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability(:debian, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:debian, :nfs_client_install) do require_relative "cap/nfs" Cap::NFS end guest_capability(:debian, :rsync_install) do require_relative "cap/rsync" Cap::RSync end guest_capability(:debian, :smb_install) do require_relative "cap/smb" Cap::SMB end end end end vagrant-2.0.2/plugins/guests/dragonflybsd/000077500000000000000000000000001323370221500206245ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/dragonflybsd/guest.rb000066400000000000000000000003311323370221500222750ustar00rootroot00000000000000module VagrantPlugins module GuestDragonFlyBSD class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("uname -s | grep -i 'DragonFly'") end end end end vagrant-2.0.2/plugins/guests/dragonflybsd/plugin.rb000066400000000000000000000004611323370221500224500ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestDragonFlyBSD class Plugin < Vagrant.plugin("2") name "DragonFly BSD guest" description "DragonFly BSD guest support." guest(:dragonflybsd, :freebsd) do require_relative "guest" Guest end end end end vagrant-2.0.2/plugins/guests/elementary/000077500000000000000000000000001323370221500203135ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/elementary/guest.rb000066400000000000000000000003571323370221500217740ustar00rootroot00000000000000require_relative '../linux/guest' module VagrantPlugins module GuestElementary class Guest < VagrantPlugins::GuestLinux::Guest # Name used for guest detection GUEST_DETECTION_NAME = "elementary".freeze end end end vagrant-2.0.2/plugins/guests/elementary/plugin.rb000066400000000000000000000004461323370221500221420ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestElementary class Plugin < Vagrant.plugin("2") name "Elementary guest" description "Elementary guest support." guest(:elementary, :ubuntu) do require_relative "guest" Guest end end end end vagrant-2.0.2/plugins/guests/esxi/000077500000000000000000000000001323370221500171165ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/esxi/cap/000077500000000000000000000000001323370221500176615ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/esxi/cap/change_host_name.rb000066400000000000000000000005471323370221500234760ustar00rootroot00000000000000module VagrantPlugins module GuestEsxi module Cap class ChangeHostName def self.change_host_name(machine, name) if !machine.communicate.test("localcli system hostname get | grep '#{name}'") machine.communicate.execute("localcli system hostname set -H '#{name}'") end end end end end end vagrant-2.0.2/plugins/guests/esxi/cap/configure_networks.rb000066400000000000000000000031311323370221500241210ustar00rootroot00000000000000module VagrantPlugins module GuestEsxi module Cap class ConfigureNetworks def self.configure_networks(machine, networks) networks.each do |network| ix = network[:interface] switch = "vSwitch#{ix}" pg = "VagrantNetwork#{ix}" vmnic = "vmnic#{ix}" device = "vmk#{ix}" if machine.communicate.test("localcli network ip interface ipv4 get -i #{device}") machine.communicate.execute("localcli network ip interface remove -i #{device}") end if machine.communicate.test("localcli network vswitch standard list -v #{switch}") machine.communicate.execute("localcli network vswitch standard remove -v #{switch}") end machine.communicate.execute("localcli network vswitch standard add -v #{switch}") machine.communicate.execute("localcli network vswitch standard uplink add -v #{switch} -u #{vmnic}") machine.communicate.execute("localcli network vswitch standard portgroup add -v #{switch} -p #{pg}") machine.communicate.execute("localcli network ip interface add -i #{device} -p #{pg}") ifconfig = "localcli network ip interface ipv4 set -i #{device}" if network[:type].to_sym == :static machine.communicate.execute("#{ifconfig} -t static --ipv4 #{network[:ip]} --netmask #{network[:netmask]}") elsif network[:type].to_sym == :dhcp machine.communicate.execute("#{ifconfig} -t dhcp") end end end end end end end vagrant-2.0.2/plugins/guests/esxi/cap/halt.rb000066400000000000000000000006031323370221500211350ustar00rootroot00000000000000module VagrantPlugins module GuestEsxi module Cap class Halt def self.halt(machine) begin machine.communicate.execute("/bin/halt -d 0") rescue IOError, Vagrant::Errors::SSHDisconnected # Ignore, this probably means connection closed because it # shut down. end end end end end end vagrant-2.0.2/plugins/guests/esxi/cap/mount_nfs_folder.rb000066400000000000000000000026541323370221500235600ustar00rootroot00000000000000module VagrantPlugins module GuestEsxi module Cap class MountNFSFolder extend Vagrant::Util::Retryable def self.mount_nfs_folder(machine, ip, folders) folders.each do |name, opts| guestpath = opts[:guestpath] volume = guestpath.gsub("/", "_") machine.communicate.tap do |comm| if comm.test("localcli storage nfs list | grep '^#{volume}'") comm.execute("localcli storage nfs remove -v #{volume}") end mount_command = "localcli storage nfs add -H #{ip} -s '#{opts[:hostpath]}' -v '#{volume}'" retryable(on: Vagrant::Errors::NFSMountFailed, tries: 5, sleep: 2) do comm.execute(mount_command, error_class: Vagrant::Errors::NFSMountFailed) end # symlink vmfs volume to :guestpath if comm.test("test -L '#{guestpath}'") comm.execute("rm -f '#{guestpath}'") end if comm.test("test -d '#{guestpath}'") comm.execute("rmdir '#{guestpath}'") end dir = File.dirname(guestpath) if !comm.test("test -d '#{dir}'") comm.execute("mkdir -p '#{dir}'") end comm.execute("ln -s '/vmfs/volumes/#{volume}' '#{guestpath}'") end end end end end end end vagrant-2.0.2/plugins/guests/esxi/cap/public_key.rb000066400000000000000000000042751323370221500223440ustar00rootroot00000000000000require "tempfile" require "vagrant/util/shell_quote" module VagrantPlugins module GuestEsxi module Cap class PublicKey def self.insert_public_key(machine, contents) comm = machine.communicate contents = contents.strip << "\n" remote_path = "/tmp/vagrant-insert-pubkey-#{Time.now.to_i}" Tempfile.open("vagrant-esxi-insert-public-key") do |f| f.binmode f.write(contents) f.fsync f.close comm.upload(f.path, remote_path) end comm.execute <<-EOH.gsub(/^ {12}/, "") set -e SSH_DIR="$(grep -q '^AuthorizedKeysFile\s*\/etc\/ssh\/keys-%u\/authorized_keys$' /etc/ssh/sshd_config && echo -n /etc/ssh/keys-${USER} || echo -n ~/.ssh)" mkdir -p "${SSH_DIR}" chmod 0700 "${SSH_DIR}" cat '#{remote_path}' >> "${SSH_DIR}/authorized_keys" chmod 0600 "${SSH_DIR}/authorized_keys" rm -f '#{remote_path}' EOH end def self.remove_public_key(machine, contents) comm = machine.communicate contents = contents.strip << "\n" remote_path = "/tmp/vagrant-remove-pubkey-#{Time.now.to_i}" Tempfile.open("vagrant-esxi-remove-public-key") do |f| f.binmode f.write(contents) f.fsync f.close comm.upload(f.path, remote_path) end # Use execute (not sudo) because we want to execute this as the SSH # user (which is "vagrant" by default). comm.execute <<-EOH.sub(/^ {12}/, "") set -e SSH_DIR="$(grep -q '^AuthorizedKeysFile\s*\/etc\/ssh\/keys-%u\/authorized_keys$' /etc/ssh/sshd_config && echo -n /etc/ssh/keys-${USER} || echo -n ~/.ssh)" if test -f "${SSH_DIR}/authorized_keys"; then grep -v -x -f '#{remote_path}' "${SSH_DIR}/authorized_keys" > "${SSH_DIR}/authorized_keys.tmp" mv "${SSH_DIR}/authorized_keys.tmp" "${SSH_DIR}/authorized_keys" chmod 0600 "${SSH_DIR}/authorized_keys" fi rm -f '#{remote_path}' EOH end end end end end vagrant-2.0.2/plugins/guests/esxi/guest.rb000066400000000000000000000003361323370221500205740ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestEsxi class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("uname -s | grep VMkernel") end end end end vagrant-2.0.2/plugins/guests/esxi/plugin.rb000066400000000000000000000020551323370221500207430ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestEsxi class Plugin < Vagrant.plugin("2") name "ESXi guest." description "ESXi guest support." guest(:esxi) do require_relative "guest" Guest end guest_capability(:esxi, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:esxi, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability(:esxi, :mount_nfs_folder) do require_relative "cap/mount_nfs_folder" Cap::MountNFSFolder end guest_capability(:esxi, :halt) do require_relative "cap/halt" Cap::Halt end guest_capability(:esxi, :remove_public_key) do require_relative "cap/public_key" Cap::PublicKey end guest_capability(:esxi, :insert_public_key) do require_relative "cap/public_key" Cap::PublicKey end end end end vagrant-2.0.2/plugins/guests/fedora/000077500000000000000000000000001323370221500174065ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/fedora/cap/000077500000000000000000000000001323370221500201515ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/fedora/cap/flavor.rb000066400000000000000000000010241323370221500217640ustar00rootroot00000000000000module VagrantPlugins module GuestFedora module Cap class Flavor def self.flavor(machine) # Read the version file version = nil machine.communicate.sudo("grep VERSION_ID /etc/os-release") do |type, data| if type == :stdout version = data.split("=")[1].chomp.to_i end end if version.nil? return :fedora else return :"fedora_#{version}" end end end end end end vagrant-2.0.2/plugins/guests/fedora/guest.rb000066400000000000000000000003611323370221500210620ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestFedora class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("grep 'Fedora release' /etc/redhat-release") end end end end vagrant-2.0.2/plugins/guests/fedora/plugin.rb000066400000000000000000000006071323370221500212340ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestFedora class Plugin < Vagrant.plugin("2") name "Fedora guest" description "Fedora guest support." guest(:fedora, :redhat) do require_relative "guest" Guest end guest_capability(:fedora, :flavor) do require_relative "cap/flavor" Cap::Flavor end end end end vagrant-2.0.2/plugins/guests/freebsd/000077500000000000000000000000001323370221500175605ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/freebsd/cap/000077500000000000000000000000001323370221500203235ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/freebsd/cap/change_host_name.rb000066400000000000000000000015351323370221500241360ustar00rootroot00000000000000module VagrantPlugins module GuestFreeBSD module Cap class ChangeHostName def self.change_host_name(machine, name) comm = machine.communicate if !comm.test("hostname -f | grep '^#{name}$'", sudo: false, shell: "sh") basename = name.split(".", 2)[0] command = <<-EOH.gsub(/^ {14}/, '') # Set the hostname hostname '#{name}' sed -i '' 's/^hostname=.*$/hostname=\"#{name}\"/' /etc/rc.conf # Prepend ourselves to /etc/hosts grep -w '#{name}' /etc/hosts || { echo -e '127.0.0.1\\t#{name}\\t#{basename}' | cat - /etc/hosts > /tmp/tmp-hosts mv /tmp/tmp-hosts /etc/hosts } EOH comm.sudo(command, shell: "sh") end end end end end end vagrant-2.0.2/plugins/guests/freebsd/cap/configure_networks.rb000066400000000000000000000042101323370221500245620ustar00rootroot00000000000000require "tempfile" require_relative "../../../../lib/vagrant/util/template_renderer" module VagrantPlugins module GuestFreeBSD module Cap class ConfigureNetworks include Vagrant::Util def self.configure_networks(machine, networks) options = { shell: "sh" } comm = machine.communicate commands = [] interfaces = [] # Remove any previous network additions to the configuration file. 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") end networks.each.with_index do |network, i| network[:device] = interfaces[network[:interface]] entry = TemplateRenderer.render("guests/freebsd/network_#{network[:type]}", options: network, ) remote_path = "/tmp/vagrant-network-#{network[:device]}-#{Time.now.to_i}-#{i}" Tempfile.open("vagrant-freebsd-configure-networks") do |f| f.binmode f.write(entry) f.fsync f.close comm.upload(f.path, remote_path) end commands << <<-EOH.gsub(/^ {14}/, '') cat '#{remote_path}' >> /etc/rc.conf rm -f '#{remote_path}' EOH # If the network is DHCP, then we have to start the dhclient, unless # it is already running. See GH-5852 for more information if network[:type].to_sym == :dhcp file = "/var/run/dhclient.#{network[:device]}.pid" commands << <<-EOH.gsub(/^ {16}/, '') if ! test -f '#{file}' || ! kill -0 $(cat '#{file}'); then dhclient '#{network[:device]}' fi EOH end # For some reason, this returns status 1... every time commands << "/etc/rc.d/netif restart '#{network[:device]}' || true" end comm.sudo(commands.join("\n"), options) end end end end end vagrant-2.0.2/plugins/guests/freebsd/cap/rsync.rb000066400000000000000000000005351323370221500220110ustar00rootroot00000000000000require_relative "../../../synced_folders/rsync/default_unix_cap" module VagrantPlugins module GuestFreeBSD module Cap class RSync extend VagrantPlugins::SyncedFolderRSync::DefaultUnixCap def self.rsync_install(machine) machine.communicate.sudo("pkg install -y rsync") end end end end end vagrant-2.0.2/plugins/guests/freebsd/cap/shell_expand_guest_path.rb000066400000000000000000000014771323370221500255520ustar00rootroot00000000000000module VagrantPlugins module GuestFreeBSD module Cap class ShellExpandGuestPath def self.shell_expand_guest_path(machine, path) real_path = nil path = path.gsub(/ /, '\ ') machine.communicate.execute("printf #{path}", shell: "sh") do |type, data| if type == :stdout real_path ||= "" real_path += data end end if !real_path # If no real guest path was detected, this is really strange # and we raise an exception because this is a bug. raise Vagrant::Errors::ShellExpandFailed end # Chomp the string so that any trailing newlines are killed return real_path.chomp end end end end end vagrant-2.0.2/plugins/guests/freebsd/guest.rb000066400000000000000000000006151323370221500212360ustar00rootroot00000000000000require 'vagrant/util/template_renderer' module VagrantPlugins module GuestFreeBSD # A general Vagrant system implementation for "freebsd". # # Contributed by Kenneth Vestergaard class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("uname -s | grep 'FreeBSD'", {shell: "sh"}) end end end end vagrant-2.0.2/plugins/guests/freebsd/plugin.rb000066400000000000000000000024521323370221500214060ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestFreeBSD class Plugin < Vagrant.plugin("2") name "FreeBSD guest" description "FreeBSD guest support." guest(:freebsd, :bsd) do require_relative "guest" Guest end guest_capability(:freebsd, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:freebsd, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability(:freebsd, :rsync_install) do require_relative "cap/rsync" Cap::RSync end guest_capability(:freebsd, :rsync_installed) do require_relative "cap/rsync" Cap::RSync end guest_capability(:freebsd, :rsync_command) do require_relative "cap/rsync" Cap::RSync end guest_capability(:freebsd, :rsync_post) do require_relative "cap/rsync" Cap::RSync end guest_capability(:freebsd, :rsync_pre) do require_relative "cap/rsync" Cap::RSync end guest_capability(:freebsd, :shell_expand_guest_path) do require_relative "cap/shell_expand_guest_path" Cap::ShellExpandGuestPath end end end end vagrant-2.0.2/plugins/guests/funtoo/000077500000000000000000000000001323370221500174605ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/funtoo/cap/000077500000000000000000000000001323370221500202235ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/funtoo/cap/configure_networks.rb000066400000000000000000000034731323370221500244740ustar00rootroot00000000000000require "tempfile" require_relative "../../../../lib/vagrant/util/template_renderer" module VagrantPlugins module GuestFuntoo module Cap class ConfigureNetworks include Vagrant::Util def self.configure_networks(machine, networks) machine.communicate.tap do |comm| # Configure each network interface networks.each do |network| # http://www.funtoo.org/Funtoo_Linux_Networking # dhcpcd generally runs on all interfaces by default # in the future we can change this, dhcpcd has lots of features # it would be nice to expose more of its capabilities... if not /dhcp/i.match(network[:type]) line = "denyinterfaces eth#{network[:interface]}" cmd = "grep '#{line}' /etc/dhcpcd.conf; if [ $? -ne 0 ]; then echo '#{line}' >> /etc/dhcpcd.conf ; fi" comm.sudo(cmd) ifFile = "netif.eth#{network[:interface]}" entry = TemplateRenderer.render("guests/funtoo/network_#{network[:type]}", options: network) # Upload the entry to a temporary location Tempfile.open("vagrant-funtoo-configure-networks") do |f| f.binmode f.write(entry) f.fsync f.close comm.upload(f.path, "/tmp/vagrant-#{ifFile}") end comm.sudo("cp /tmp/vagrant-#{ifFile} /etc/conf.d/#{ifFile}") comm.sudo("chmod 0644 /etc/conf.d/#{ifFile}") comm.sudo("ln -fs /etc/init.d/netif.tmpl /etc/init.d/#{ifFile}") comm.sudo("/etc/init.d/#{ifFile} start") end end end end end end end end vagrant-2.0.2/plugins/guests/funtoo/guest.rb000066400000000000000000000003241323370221500211330ustar00rootroot00000000000000module VagrantPlugins module GuestFuntoo class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("grep Funtoo /etc/gentoo-release") end end end end vagrant-2.0.2/plugins/guests/funtoo/plugin.rb000066400000000000000000000006521323370221500213060ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestFuntoo class Plugin < Vagrant.plugin("2") name "Funtoo guest" description "Funtoo guest support." guest(:funtoo, :gentoo) do require_relative "guest" Guest end guest_capability(:funtoo, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end end end end vagrant-2.0.2/plugins/guests/gentoo/000077500000000000000000000000001323370221500174415ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/gentoo/cap/000077500000000000000000000000001323370221500202045ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/gentoo/cap/change_host_name.rb000066400000000000000000000017321323370221500240160ustar00rootroot00000000000000module VagrantPlugins module GuestGentoo module Cap class ChangeHostName 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 <<-EOH.gsub(/^ {14}/, "") # Set the hostname # Use hostnamectl on systemd if [[ `systemctl` =~ -\.mount ]]; then systemctl set-hostname '#{name}' else hostname '#{basename}' echo "hostname=#{basename}" > /etc/conf.d/hostname fi # Prepend ourselves to /etc/hosts grep -w '#{name}' /etc/hosts || { echo -e '127.0.0.1\\t#{name}\\t#{basename}' | cat - /etc/hosts > /tmp/tmp-hosts && mv /tmp/tmp-hosts /etc/hosts } EOH end end end end end end vagrant-2.0.2/plugins/guests/gentoo/cap/configure_networks.rb000066400000000000000000000061571323370221500244570ustar00rootroot00000000000000require "tempfile" require "ipaddr" require_relative "../../../../lib/vagrant/util/template_renderer" module VagrantPlugins module GuestGentoo module Cap class ConfigureNetworks include Vagrant::Util def self.configure_networks(machine, networks) comm = machine.communicate commands = [] interfaces = machine.guest.capability(:network_interfaces, "/bin/ip") networks.map! { |n| n[:device] = interfaces[n[:interface]]; n } if comm.test('[[ `systemctl` =~ -\.mount ]]') # Configure networking for Systemd # convert netmasks to CIDR by converting to a binary string and counting the '1's networks.map! { |n| n[:netmask] = IPAddr.new(n[:netmask]).to_i.to_s(2).count("1"); n } # glob networks by device, so that we can write one file per device # (result is hash[devicename] = [net, net, net...]) networks = networks.map { |n| [n[:device], n] }.reduce({}) { |h, (k, v)| (h[k] ||= []) << v; h } # Write one .network file out for each device networks.each_pair do |device_name, device_networks| entry = TemplateRenderer.render('guests/gentoo/network_systemd', networks: device_networks) filename = "50_vagrant_#{device_name}.network" tmpfile = "/tmp/#{filename}" destfile = "/etc/systemd/network/#{filename}" Tempfile.open('vagrant-gentoo-configure-networks') do |f| f.binmode f.write(entry) f.fsync f.close comm.upload(f.path, tmpfile) end commands << "mv #{tmpfile} #{destfile} && chmod 644 #{destfile}" end # tell systemd to reload the networking config commands << 'systemctl daemon-reload && systemctl restart systemd-networkd.service' else # Configure networking for OpenRC # Remove any previous network additions to the configuration file. commands << "sed -i'' -e '/^#VAGRANT-BEGIN/,/^#VAGRANT-END/ d' /etc/conf.d/net" networks.each_with_index do |network, i| entry = TemplateRenderer.render("guests/gentoo/network_#{network[:type]}", options: network, ) remote_path = "/tmp/vagrant-network-#{network[:device]}-#{Time.now.to_i}-#{i}" Tempfile.open("vagrant-gentoo-configure-networks") do |f| f.binmode f.write(entry) f.fsync f.close comm.upload(f.path, remote_path) end commands << <<-EOH.gsub(/^ {14}/, '') ln -sf /etc/init.d/net.lo /etc/init.d/net.#{network[:device]} /etc/init.d/net.#{network[:device]} stop || true cat '#{remote_path}' >> /etc/conf.d/net rm -f '#{remote_path}' /etc/init.d/net.#{network[:device]} start EOH end end comm.sudo(commands.join("\n")) end end end end end vagrant-2.0.2/plugins/guests/gentoo/guest.rb000066400000000000000000000003241323370221500211140ustar00rootroot00000000000000module VagrantPlugins module GuestGentoo class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("grep Gentoo /etc/gentoo-release") end end end end vagrant-2.0.2/plugins/guests/gentoo/plugin.rb000066400000000000000000000010661323370221500212670ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestGentoo class Plugin < Vagrant.plugin("2") name "Gentoo guest" description "Gentoo guest support." guest(:gentoo, :linux) do require_relative "guest" Guest end guest_capability(:gentoo, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:gentoo, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end end end end vagrant-2.0.2/plugins/guests/haiku/000077500000000000000000000000001323370221500172475ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/haiku/cap/000077500000000000000000000000001323370221500200125ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/haiku/cap/change_host_name.rb000066400000000000000000000020761323370221500236260ustar00rootroot00000000000000module VagrantPlugins module GuestHaiku module Cap class ChangeHostName def self.change_host_name(machine, name) comm = machine.communicate if !comm.test("hostname | grep '^#{name}$'", sudo: false) basename = name.split(".", 2)[0] comm.execute <<-EOH.gsub(/^ {14}/, '') # Ensure exit on command error set -e export SYS_SETTINGS_DIR=$(finddir B_SYSTEM_SETTINGS_DIRECTORY) # Set the hostname echo '#{basename}' > $SYS_SETTINGS_DIR/network/hostname hostname '#{basename}' # Remove comments and blank lines from /etc/hosts sed -i'' -e 's/#.*$//' -e '/^$/d' $SYS_SETTINGS_DIR/network/hosts # Prepend ourselves to $SYS_SETTINGS_DIR/network/hosts grep -w '#{name}' $SYS_SETTINGS_DIR/network/hosts || { sed -i'' '1i 127.0.0.1\\t#{name}\\t#{basename}' $SYS_SETTINGS_DIR/network/hosts } EOH end end end end end end vagrant-2.0.2/plugins/guests/haiku/cap/halt.rb000066400000000000000000000006031323370221500212660ustar00rootroot00000000000000module VagrantPlugins module GuestHaiku module Cap class Halt def self.halt(machine) begin machine.communicate.execute("/bin/shutdown") rescue IOError, Vagrant::Errors::SSHDisconnected # Ignore, this probably means connection closed because it # shut down. end end end end end end vagrant-2.0.2/plugins/guests/haiku/cap/insert_public_key.rb000066400000000000000000000013511323370221500240510ustar00rootroot00000000000000require "vagrant/util/shell_quote" module VagrantPlugins module GuestHaiku module Cap class InsertPublicKey def self.insert_public_key(machine, contents) contents = contents.chomp contents = Vagrant::Util::ShellQuote.escape(contents, "'") machine.communicate.tap do |comm| comm.execute("mkdir -p $(finddir B_USER_SETTINGS_DIRECTORY)/ssh") comm.execute("chmod 0700 $(finddir B_USER_SETTINGS_DIRECTORY)/ssh") comm.execute("printf '#{contents}\\n' >> $(finddir B_USER_SETTINGS_DIRECTORY)/ssh/authorized_keys") comm.execute("chmod 0600 $(finddir B_USER_SETTINGS_DIRECTORY)/ssh/authorized_keys") end end end end end end vagrant-2.0.2/plugins/guests/haiku/cap/remove_public_key.rb000066400000000000000000000011561323370221500240450ustar00rootroot00000000000000require "vagrant/util/shell_quote" module VagrantPlugins module GuestHaiku module Cap class RemovePublicKey def self.remove_public_key(machine, contents) contents = contents.chomp contents = Vagrant::Util::ShellQuote.escape(contents, "'") machine.communicate.tap do |comm| if comm.test("test -f $(finddir B_USER_SETTINGS_DIRECTORY)/ssh/authorized_keys") comm.execute( "sed -i '/^.*#{contents}.*$/d' $(finddir B_USER_SETTINGS_DIRECTORY)/ssh/authorized_keys") end end end end end end end vagrant-2.0.2/plugins/guests/haiku/guest.rb000066400000000000000000000003361323370221500207250ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestHaiku class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("uname -o | grep 'Haiku'") end end end end vagrant-2.0.2/plugins/guests/haiku/plugin.rb000066400000000000000000000014351323370221500210750ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestHaiku class Plugin < Vagrant.plugin("2") name "Haiku guest" description "Haiku guest support." guest(:haiku) do require_relative "guest" Guest end guest_capability(:haiku, :halt) do require_relative "cap/halt" Cap::Halt end guest_capability(:haiku, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:haiku, :insert_public_key) do require_relative "cap/insert_public_key" Cap::InsertPublicKey end guest_capability(:haiku, :remove_public_key) do require_relative "cap/remove_public_key" Cap::RemovePublicKey end end end end vagrant-2.0.2/plugins/guests/kali/000077500000000000000000000000001323370221500170665ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/kali/guest.rb000066400000000000000000000003431323370221500205420ustar00rootroot00000000000000require_relative '../linux/guest' module VagrantPlugins module GuestKali class Guest < VagrantPlugins::GuestLinux::Guest # Name used for guest detection GUEST_DETECTION_NAME = "kali".freeze end end end vagrant-2.0.2/plugins/guests/kali/plugin.rb000066400000000000000000000004161323370221500207120ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestKali class Plugin < Vagrant.plugin("2") name "Kali guest" description "Kali guest support." guest(:kali, :debian) do require_relative "guest" Guest end end end end vagrant-2.0.2/plugins/guests/linux/000077500000000000000000000000001323370221500173055ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/linux/cap/000077500000000000000000000000001323370221500200505ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/linux/cap/choose_addressable_ip_addr.rb000066400000000000000000000006121323370221500256670ustar00rootroot00000000000000module VagrantPlugins module GuestLinux module Cap module ChooseAddressableIPAddr def self.choose_addressable_ip_addr(machine, possible) comm = machine.communicate possible.each do |ip| if comm.test("ping -c1 -w1 -W1 #{ip}") return ip end end return nil end end end end end vagrant-2.0.2/plugins/guests/linux/cap/halt.rb000066400000000000000000000006271323370221500213320ustar00rootroot00000000000000module VagrantPlugins module GuestLinux module Cap class Halt def self.halt(machine) begin machine.communicate.sudo("shutdown -h now") rescue IOError, Vagrant::Errors::SSHDisconnected # Do nothing, because it probably means the machine shut down # and SSH connection was lost. end end end end end end vagrant-2.0.2/plugins/guests/linux/cap/mount_smb_shared_folder.rb000066400000000000000000000070231323370221500252630ustar00rootroot00000000000000require "shellwords" require_relative "../../../synced_folders/unix_mount_helpers" module VagrantPlugins module GuestLinux module Cap class MountSMBSharedFolder extend SyncedFolder::UnixMountHelpers def self.mount_smb_shared_folder(machine, name, guestpath, options) expanded_guest_path = machine.guest.capability( :shell_expand_guest_path, guestpath) mount_device = "//#{options[:smb_host]}/#{name}" mount_options = options.fetch(:mount_options, []) detected_ids = detect_owner_group_ids(machine, guestpath, mount_options, options) mount_uid = detected_ids[:uid] mount_gid = detected_ids[:gid] # If a domain is provided in the username, separate it username, domain = (options[:smb_username] || '').split('@', 2) smb_password = options[:smb_password] # Ensure password is scrubbed Vagrant::Util::CredentialScrubber.sensitive(smb_password) mnt_opts = [] if machine.env.host.capability?(:smb_mount_options) mnt_opts += machine.env.host.capability(:smb_mount_options) else mnt_opts << "sec=ntlmssp" end mnt_opts << "credentials=/etc/smb_creds_#{name}" mnt_opts << "uid=#{mount_uid}" mnt_opts << "gid=#{mount_gid}" mnt_opts = merge_mount_options(mnt_opts, options[:mount_options] || []) mount_options = "-o #{mnt_opts.join(",")}" mount_command = "mount -t cifs #{mount_options} #{mount_device} #{expanded_guest_path}" # Create the guest path if it doesn't exist machine.communicate.sudo("mkdir -p #{expanded_guest_path}") # Write the credentials file machine.communicate.sudo(<<-SCRIPT) cat <<"EOF" >/etc/smb_creds_#{name} username=#{username} password=#{smb_password} #{domain ? "domain=#{domain}" : ""} EOF chmod 0600 /etc/smb_creds_#{name} SCRIPT # Attempt to mount the folder. We retry here a few times because # it can fail early on. begin retryable(on: Vagrant::Errors::LinuxMountFailed, tries: 10, sleep: 2) do no_such_device = false stderr = "" status = machine.communicate.sudo(mount_command, error_check: false) do |type, data| if type == :stderr no_such_device = true if data =~ /No such device/i stderr += data.to_s end end if status != 0 || no_such_device raise Vagrant::Errors::LinuxMountFailed, command: mount_command, output: stderr end end ensure # Always remove credentials file after mounting attempts # have been completed machine.communicate.sudo("rm /etc/smb_creds_#{name}") end emit_upstart_notification(machine, expanded_guest_path) end def self.merge_mount_options(base, overrides) base = base.join(",").split(",") overrides = overrides.join(",").split(",") b_kv = Hash[base.map{|item| item.split("=", 2) }] o_kv = Hash[overrides.map{|item| item.split("=", 2) }] merged = {}.tap do |opts| (b_kv.keys + o_kv.keys).uniq.each do |key| opts[key] = o_kv.fetch(key, b_kv[key]) end end merged.map do |key, value| [key, value].compact.join("=") end end end end end end vagrant-2.0.2/plugins/guests/linux/cap/mount_virtualbox_shared_folder.rb000066400000000000000000000044771323370221500267130ustar00rootroot00000000000000require_relative "../../../synced_folders/unix_mount_helpers" module VagrantPlugins module GuestLinux 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})") 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 -t vboxsf -o #{mount_options} #{name} #{guest_path}" # Create the guest path if it doesn't exist machine.communicate.sudo("mkdir -p #{guest_path}") # 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 # 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 vagrant-2.0.2/plugins/guests/linux/cap/network_interfaces.rb000066400000000000000000000057331323370221500243010ustar00rootroot00000000000000module VagrantPlugins module GuestLinux module Cap class NetworkInterfaces # Valid ethernet device prefix values. # eth - classic prefix # en - predictable interface names prefix POSSIBLE_ETHERNET_PREFIXES = ["eth".freeze, "en".freeze].freeze @@logger = Log4r::Logger.new("vagrant::guest::linux::network_interfaces") # Get network interfaces as a list. The result will be something like: # # eth0\nenp0s8\nenp0s9 # # @return [Array] def self.network_interfaces(machine, path = "/sbin/ip") s = "" machine.communicate.sudo("#{path} -o -0 addr | grep -v LOOPBACK | awk '{print $2}' | sed 's/://'") do |type, data| s << data if type == :stdout end # In some cases net devices may be added to the guest and will not # properly show up when using `ip`. This pulls any device information # that can be found in /proc and adds it to the list of interfaces s << "\n" machine.communicate.sudo("cat /proc/net/dev | grep -E '^[a-z0-9 ]+:' | awk '{print $1}' | sed 's/://'", error_check: false) do |type, data| s << data if type == :stdout end ifaces = s.split("\n") @@logger.debug("Unsorted list: #{ifaces.inspect}") # Break out integers from strings and sort the arrays to provide # a natural sort for the interface names # NOTE: Devices named with a hex value suffix will _not_ be sorted # as expected. This is generally seen with veth* devices, and proper ordering # is currently not required ifaces = ifaces.map do |iface| iface.scan(/(.+?)(\d+)/).flatten.map do |iface_part| if iface_part.to_i.to_s == iface_part iface_part.to_i else iface_part end end end ifaces = ifaces.uniq.sort do |lhs, rhs| result = 0 slice_length = [rhs.size, lhs.size].min slice_length.times do |idx| if(lhs[idx].is_a?(rhs[idx].class)) result = lhs[idx] <=> rhs[idx] elsif(lhs[idx].is_a?(String)) result = 1 else result = -1 end break if result != 0 end result end.map(&:join) @@logger.debug("Sorted list: #{ifaces.inspect}") # Extract ethernet devices and place at start of list resorted_ifaces = [] resorted_ifaces += ifaces.find_all do |iface| POSSIBLE_ETHERNET_PREFIXES.any?{|prefix| iface.start_with?(prefix)} && iface.match(/^[a-zA-Z0-9]+$/) end resorted_ifaces += ifaces - resorted_ifaces ifaces = resorted_ifaces @@logger.debug("Ethernet preferred sorted list: #{ifaces.inspect}") ifaces end end end end end vagrant-2.0.2/plugins/guests/linux/cap/nfs.rb000066400000000000000000000031061323370221500211630ustar00rootroot00000000000000require_relative "../../../synced_folders/unix_mount_helpers" module VagrantPlugins module GuestLinux module Cap class NFS extend SyncedFolder::UnixMountHelpers def self.nfs_client_installed(machine) machine.communicate.test("test -x /sbin/mount.nfs") end def self.mount_nfs_folder(machine, ip, folders) comm = machine.communicate # Mount each folder separately so we can retry. folders.each do |name, opts| # Shellescape the paths in case they do not have special characters. guest_path = Shellwords.escape(opts[:guestpath]) host_path = Shellwords.escape(opts[:hostpath]) # Build the list of mount options. mount_opts = [] mount_opts << "vers=#{opts[:nfs_version]}" if opts[:nfs_version] mount_opts << "udp" if opts[:nfs_udp] if opts[:mount_options] mount_opts = mount_opts + opts[:mount_options].dup end mount_opts = mount_opts.join(",") machine.communicate.sudo("mkdir -p #{guest_path}") command = "mount -o #{mount_opts} #{ip}:#{host_path} #{guest_path}" # Run the command, raising a specific error. retryable(on: Vagrant::Errors::NFSMountFailed, tries: 3, sleep: 5) do machine.communicate.sudo(command, error_class: Vagrant::Errors::NFSMountFailed, ) end emit_upstart_notification(machine, guest_path) end end end end end end vagrant-2.0.2/plugins/guests/linux/cap/port.rb000066400000000000000000000003441323370221500213620ustar00rootroot00000000000000module VagrantPlugins module GuestLinux module Cap class Port def self.port_open_check(machine, port) machine.communicate.test("nc -z -w2 127.0.0.1 #{port}") end end end end end vagrant-2.0.2/plugins/guests/linux/cap/public_key.rb000066400000000000000000000037171323370221500225330ustar00rootroot00000000000000require "tempfile" require "vagrant/util/shell_quote" module VagrantPlugins module GuestLinux module Cap class PublicKey def self.insert_public_key(machine, contents) comm = machine.communicate contents = contents.strip << "\n" remote_path = "/tmp/vagrant-insert-pubkey-#{Time.now.to_i}" Tempfile.open("vagrant-linux-insert-public-key") do |f| f.binmode f.write(contents) f.fsync f.close comm.upload(f.path, remote_path) end # Use execute (not sudo) because we want to execute this as the SSH # user (which is "vagrant" by default). comm.execute <<-EOH.gsub(/^ */, "") mkdir -p ~/.ssh chmod 0700 ~/.ssh cat '#{remote_path}' >> ~/.ssh/authorized_keys && chmod 0600 ~/.ssh/authorized_keys result=$? rm -f '#{remote_path}' exit $result EOH end def self.remove_public_key(machine, contents) comm = machine.communicate contents = contents.strip << "\n" remote_path = "/tmp/vagrant-remove-pubkey-#{Time.now.to_i}" Tempfile.open("vagrant-linux-remove-public-key") do |f| f.binmode f.write(contents) f.fsync f.close comm.upload(f.path, remote_path) end # Use execute (not sudo) because we want to execute this as the SSH # user (which is "vagrant" by default). comm.execute <<-EOH.sub(/^ */, "") if test -f ~/.ssh/authorized_keys; then grep -v -x -f '#{remote_path}' ~/.ssh/authorized_keys > ~/.ssh/authorized_keys.tmp mv ~/.ssh/authorized_keys.tmp ~/.ssh/authorized_keys && chmod 0600 ~/.ssh/authorized_keys result=$? fi rm -f '#{remote_path}' exit $result EOH end end end end end vagrant-2.0.2/plugins/guests/linux/cap/read_ip_address.rb000066400000000000000000000012071323370221500235050ustar00rootroot00000000000000module VagrantPlugins module GuestLinux module Cap class ReadIPAddress def self.read_ip_address(machine) comm = machine.communicate if comm.test("which ip") command = "LANG=en ip addr | grep -Po 'inet \\K[\\d.]+' | grep -v 127.0.0.1" else command = "LANG=en ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' | cut -d: -f2 | awk '{ print $1 }'" end result = "" comm.sudo(command) do |type, data| result << data if type == :stdout end result.chomp.split("\n").first end end end end end vagrant-2.0.2/plugins/guests/linux/cap/rsync.rb000066400000000000000000000003531323370221500215340ustar00rootroot00000000000000require_relative "../../../synced_folders/rsync/default_unix_cap" module VagrantPlugins module GuestLinux module Cap class RSync extend VagrantPlugins::SyncedFolderRSync::DefaultUnixCap end end end end vagrant-2.0.2/plugins/guests/linux/cap/shell_expand_guest_path.rb000066400000000000000000000016451323370221500252740ustar00rootroot00000000000000module VagrantPlugins module GuestLinux module Cap class ShellExpandGuestPath def self.shell_expand_guest_path(machine, path) real_path = nil path = path.gsub(/ /, '\ ') machine.communicate.execute("echo; printf #{path}") do |type, data| if type == :stdout real_path ||= "" real_path += data end end if real_path # The last line is the path we care about real_path = real_path.split("\n").last.chomp end if !real_path # If no real guest path was detected, this is really strange # and we raise an exception because this is a bug. raise Vagrant::Errors::ShellExpandFailed end # Chomp the string so that any trailing newlines are killed return real_path.chomp end end end end end vagrant-2.0.2/plugins/guests/linux/guest.rb000066400000000000000000000014401323370221500207600ustar00rootroot00000000000000module VagrantPlugins module GuestLinux class Guest < Vagrant.plugin("2", :guest) # Name used for guest detection GUEST_DETECTION_NAME = "linux".freeze def detect?(machine) machine.communicate.test <<-EOH.gsub(/^ */, '') if test -r /etc/os-release; then source /etc/os-release && test 'x#{self.class.const_get(:GUEST_DETECTION_NAME)}' = "x$ID" && exit fi if test -x /usr/bin/lsb_release; then /usr/bin/lsb_release -i 2>/dev/null | grep -qi '#{self.class.const_get(:GUEST_DETECTION_NAME)}' && exit fi if test -r /etc/issue; then cat /etc/issue | grep -qi '#{self.class.const_get(:GUEST_DETECTION_NAME)}' && exit fi exit 1 EOH end end end end vagrant-2.0.2/plugins/guests/linux/plugin.rb000066400000000000000000000050711323370221500211330ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestLinux class Plugin < Vagrant.plugin("2") name "Linux guest." description "Linux guest support." guest(:linux) do require_relative "guest" Guest end guest_capability(:linux, :choose_addressable_ip_addr) do require_relative "cap/choose_addressable_ip_addr" Cap::ChooseAddressableIPAddr end guest_capability(:linux, :halt) do require_relative "cap/halt" Cap::Halt end guest_capability(:linux, :insert_public_key) do require_relative "cap/public_key" Cap::PublicKey end guest_capability(:linux, :shell_expand_guest_path) do require_relative "cap/shell_expand_guest_path" Cap::ShellExpandGuestPath end guest_capability(:linux, :mount_nfs_folder) do require_relative "cap/nfs" Cap::NFS end guest_capability(:linux, :mount_smb_shared_folder) do require_relative "cap/mount_smb_shared_folder" Cap::MountSMBSharedFolder end guest_capability(:linux, :mount_virtualbox_shared_folder) do require_relative "cap/mount_virtualbox_shared_folder" Cap::MountVirtualBoxSharedFolder end guest_capability(:linux, :network_interfaces) do require_relative "cap/network_interfaces" Cap::NetworkInterfaces end guest_capability(:linux, :nfs_client_installed) do require_relative "cap/nfs" Cap::NFS end # For the Docker provider guest_capability(:linux, :port_open_check) do require_relative "cap/port" Cap::Port end guest_capability(:linux, :read_ip_address) do require_relative "cap/read_ip_address" Cap::ReadIPAddress end guest_capability(:linux, :remove_public_key) do require_relative "cap/public_key" Cap::PublicKey end guest_capability(:linux, :rsync_installed) do require_relative "cap/rsync" Cap::RSync end guest_capability(:linux, :rsync_command) do require_relative "cap/rsync" Cap::RSync end guest_capability(:linux, :rsync_post) do require_relative "cap/rsync" Cap::RSync end guest_capability(:linux, :rsync_pre) do require_relative "cap/rsync" Cap::RSync end guest_capability(:linux, :unmount_virtualbox_shared_folder) do require_relative "cap/mount_virtualbox_shared_folder" Cap::MountVirtualBoxSharedFolder end end end end vagrant-2.0.2/plugins/guests/mint/000077500000000000000000000000001323370221500171155ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/mint/guest.rb000066400000000000000000000003511323370221500205700ustar00rootroot00000000000000require_relative '../linux/guest' module VagrantPlugins module GuestMint class Guest < VagrantPlugins::GuestLinux::Guest # Name used for guest detection GUEST_DETECTION_NAME = "Linux Mint".freeze end end end vagrant-2.0.2/plugins/guests/mint/plugin.rb000066400000000000000000000004161323370221500207410ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestMint class Plugin < Vagrant.plugin("2") name "Mint guest" description "Mint guest support." guest(:mint, :ubuntu) do require_relative "guest" Guest end end end end vagrant-2.0.2/plugins/guests/netbsd/000077500000000000000000000000001323370221500174255ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/netbsd/cap/000077500000000000000000000000001323370221500201705ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/netbsd/cap/change_host_name.rb000066400000000000000000000007771323370221500240120ustar00rootroot00000000000000module VagrantPlugins module GuestNetBSD module Cap class ChangeHostName def self.change_host_name(machine, name) if !machine.communicate.test("hostname -s | grep '^#{name}$'") machine.communicate.sudo(< /tmp/rc.conf.vagrant_changehostname_#{name} && mv /tmp/rc.conf.vagrant_changehostname_#{name} /etc/rc.conf && hostname #{name} CMDS end end end end end end vagrant-2.0.2/plugins/guests/netbsd/cap/configure_networks.rb000066400000000000000000000037051323370221500244370ustar00rootroot00000000000000require "tempfile" require_relative "../../../../lib/vagrant/util/template_renderer" module VagrantPlugins module GuestNetBSD module Cap class ConfigureNetworks include Vagrant::Util def self.configure_networks(machine, networks) # setup a new rc.conf file newrcconf = "/tmp/rc.conf.vagrant_configurenetworks" machine.communicate.sudo("sed -e '/^#VAGRANT-BEGIN/,/^#VAGRANT-END/ d' /etc/rc.conf > #{newrcconf}") networks.each do |network| # create an interface configuration file fragment entry = TemplateRenderer.render("guests/netbsd/network_#{network[:type]}", options: network) Tempfile.open("vagrant-netbsd-configure-networks") do |f| f.binmode f.write(entry) f.fsync f.close machine.communicate.upload(f.path, "/tmp/vagrant-network-entry") end machine.communicate.sudo("cat /tmp/vagrant-network-entry >> #{newrcconf}") machine.communicate.sudo("rm -f /tmp/vagrant-network-entry") ifname = "wm#{network[:interface]}" # remove old configuration machine.communicate.sudo("/sbin/dhcpcd -x #{ifname}", { error_check: false }) machine.communicate.sudo("/sbin/ifconfig #{ifname} inet delete", { error_check: false }) # live new configuration if network[:type].to_sym == :static machine.communicate.sudo("/sbin/ifconfig #{ifname} media autoselect up;/sbin/ifconfig #{ifname} inet #{network[:ip]} netmask #{network[:netmask]}") elsif network[:type].to_sym == :dhcp machine.communicate.sudo("/sbin/dhcpcd -n -q #{ifname}") end end # install new rc.conf machine.communicate.sudo("install -c -o 0 -g 0 -m 644 #{newrcconf} /etc/rc.conf") end end end end end vagrant-2.0.2/plugins/guests/netbsd/cap/rsync.rb000066400000000000000000000010401323370221500216460ustar00rootroot00000000000000require_relative "../../../synced_folders/rsync/default_unix_cap" module VagrantPlugins module GuestNetBSD module Cap class RSync extend VagrantPlugins::SyncedFolderRSync::DefaultUnixCap def self.rsync_install(machine) machine.communicate.sudo( 'PATH=$PATH:/usr/sbin '\ 'PKG_PATH="http://ftp.NetBSD.org/pub/pkgsrc/packages/NetBSD/' \ '`uname -m`/`uname -r | cut -d. -f1-2`/All" ' \ 'pkg_add rsync' ) end end end end end vagrant-2.0.2/plugins/guests/netbsd/cap/shell_expand_guest_path.rb000066400000000000000000000014131323370221500254050ustar00rootroot00000000000000module VagrantPlugins module GuestNetBSD module Cap class ShellExpandGuestPath def self.shell_expand_guest_path(machine, path) real_path = nil path = path.gsub(/ /, '\ ') machine.communicate.execute("printf #{path}") do |type, data| if type == :stdout real_path ||= "" real_path += data end end if !real_path # If no real guest path was detected, this is really strange # and we raise an exception because this is a bug. raise Vagrant::Errors::ShellExpandFailed end # Chomp the string so that any trailing newlines are killed return real_path.chomp end end end end end vagrant-2.0.2/plugins/guests/netbsd/guest.rb000066400000000000000000000003361323370221500211030ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestNetBSD class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("uname -s | grep NetBSD") end end end end vagrant-2.0.2/plugins/guests/netbsd/plugin.rb000066400000000000000000000024361323370221500212550ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestNetBSD class Plugin < Vagrant.plugin("2") name "NetBSD guest" description "NetBSD guest support." guest(:netbsd, :bsd) do require_relative "guest" Guest end guest_capability(:netbsd, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:netbsd, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability(:netbsd, :rsync_install) do require_relative "cap/rsync" Cap::RSync end guest_capability(:netbsd, :rsync_installed) do require_relative "cap/rsync" Cap::RSync end guest_capability(:netbsd, :rsync_command) do require_relative "cap/rsync" Cap::RSync end guest_capability(:netbsd, :rsync_post) do require_relative "cap/rsync" Cap::RSync end guest_capability(:netbsd, :rsync_pre) do require_relative "cap/rsync" Cap::RSync end guest_capability(:netbsd, :shell_expand_guest_path) do require_relative "cap/shell_expand_guest_path" Cap::ShellExpandGuestPath end end end end vagrant-2.0.2/plugins/guests/nixos/000077500000000000000000000000001323370221500173065ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/nixos/cap/000077500000000000000000000000001323370221500200515ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/nixos/cap/change_host_name.rb000066400000000000000000000022631323370221500236630ustar00rootroot00000000000000require "tempfile" require_relative "../../../../lib/vagrant/util/template_renderer" module VagrantPlugins module GuestNixos module Cap class ChangeHostName include Vagrant::Util def self.change_host_name(machine, name) # upload the config file hostname_module = TemplateRenderer.render("guests/nixos/hostname", name: name) upload(machine, hostname_module, "/etc/nixos/vagrant-hostname.nix") end # Upload a file. def self.upload(machine, content, remote_path) remote_temp = mktemp(machine) Tempfile.open("vagrant-nixos-change-host-name") do |f| f.binmode f.write(content) f.fsync f.close machine.communicate.upload(f.path, "#{remote_temp}") end machine.communicate.sudo("mv #{remote_temp} #{remote_path}") end # Create a temp file. def self.mktemp(machine) path = nil machine.communicate.execute("mktemp --suffix -vagrant-upload") do |type, result| path = result.chomp if type == :stdout end path end end end end end vagrant-2.0.2/plugins/guests/nixos/cap/configure_networks.rb000066400000000000000000000112161323370221500243140ustar00rootroot00000000000000require "ipaddr" require "tempfile" require_relative "../../../../lib/vagrant/util/template_renderer" module VagrantPlugins module GuestNixos module Cap class ConfigureNetworks include Vagrant::Util def self.configure_networks(machine, networks) # set the prefix length. networks.each do |network| network[:prefix_length] = (network[:netmask] && netmask_to_cidr(network[:netmask])) end # set the device names. assign_device_names(machine, networks) # upload the config file network_module = TemplateRenderer.render("guests/nixos/network", networks: networks) upload(machine, network_module, "/etc/nixos/vagrant-network.nix") end # Set :device on each network. # Attempts to use biosdevname when available to detect interface names, # and falls back to ifconfig otherwise. def self.assign_device_names(machine, networks) if machine.communicate.test("command -v biosdevname") # use biosdevname to get info about the interfaces interfaces = get_interfaces(machine) if machine.provider.capability?(:nic_mac_addresses) # find device name by MAC lookup. mac_addresses = machine.provider.capability(:nic_mac_addresses) networks.each do |network| mac_address = mac_addresses[network[:interface]+1] interface = interfaces.detect {|nic| nic[:mac_address].gsub(":","") == mac_address} if mac_address network[:device] = interface[:kernel] if interface end else # assume interface numbers correspond to (ethN+1). networks.each do |network| interface = interfaces.detect {|nic| nic[:ethn] == network[:interface]} network[:device] = interface[:kernel] if interface end end else # assume interface numbers correspond to the order of interfaces. interfaces = get_interface_names(machine) networks.each do |network| network[:device] = interfaces[network[:interface]] end end end def self.get_interface_names(machine) output = nil machine.communicate.execute("ifconfig -a") do |type, result| output = result.chomp if type == :stdout end names = output.scan(/^[^:\s]+/).reject {|name| name =~ /^lo/ } names end # Upload a file. def self.upload(machine, content, remote_path) remote_temp = mktemp(machine) Tempfile.open("vagrant-nixos-configure-networks") do |f| f.binmode f.write(content) f.fsync f.close machine.communicate.upload(f.path, "#{remote_temp}") end machine.communicate.sudo("mv #{remote_temp} #{remote_path}") end # Create a temp file. def self.mktemp(machine) path = nil machine.communicate.execute("mktemp --suffix -vagrant-upload") do |type, result| path = result.chomp if type == :stdout end path end # using biosdevname, get all interfaces as a list of hashes, where: # :kernel - the kernel's name for the device, # :ethn - the calculated ethN-style name converted to integer. # :mac_address - the permanent mac address. ethN-style name converted to integer. def self.get_interfaces(machine) interfaces = [] # get all adapters, as named by the kernel output = nil machine.communicate.sudo("biosdevname -d") do |type, result| output = result if type == :stdout end kernel_if_names = output.scan(/Kernel name: ([^\n]+)/).flatten mac_addresses = output.scan(/Permanent MAC: ([^\n]+)/).flatten # get ethN-style names ethns = [] kernel_if_names.each do |name| machine.communicate.sudo("biosdevname --policy=all_ethN -i #{name}") do |type, result| ethns << result.gsub(/[^\d]/,'').to_i if type == :stdout end end # populate the interface list kernel_if_names.each_index do |i| interfaces << { kernel: kernel_if_names[i], ethn: ethns[i], mac_address: mac_addresses[i] } end interfaces end def self.netmask_to_cidr(mask) IPAddr.new(mask).to_i.to_s(2).count("1") end end end end end vagrant-2.0.2/plugins/guests/nixos/cap/nfs_client.rb000066400000000000000000000003721323370221500225240ustar00rootroot00000000000000module VagrantPlugins module GuestNixos module Cap class NFSClient def self.nfs_client_installed(machine) machine.communicate.test("test -x /run/current-system/sw/sbin/mount.nfs") end end end end end vagrant-2.0.2/plugins/guests/nixos/guest.rb000066400000000000000000000006711323370221500207660ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestNixos class Guest < Vagrant.plugin("2", :guest) def detect?(machine) # For some reason our test passes on Windows, so just short # circuit because we're not Windows. if machine.config.vm.communicator == :winrm return false end machine.communicate.test("test -f /run/current-system/nixos-version") end end end end vagrant-2.0.2/plugins/guests/nixos/plugin.rb000066400000000000000000000012651323370221500211350ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestNixos class Plugin < Vagrant.plugin("2") name "NixOS guest" description "NixOS guest support." guest(:nixos, :linux) do require_relative "guest" Guest end guest_capability(:nixos, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability(:nixos, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:nixos, :nfs_client_installed) do require_relative "cap/nfs_client" Cap::NFSClient end end end end vagrant-2.0.2/plugins/guests/omnios/000077500000000000000000000000001323370221500174525ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/omnios/cap/000077500000000000000000000000001323370221500202155ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/omnios/cap/change_host_name.rb000066400000000000000000000013771323370221500240340ustar00rootroot00000000000000module VagrantPlugins module GuestOmniOS module Cap class ChangeHostName 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 <<-EOH.gsub(/^ {14}/, '') # Set hostname echo '#{name}' > /etc/nodename hostname '#{name}' # Prepend ourselves to /etc/hosts grep -w '#{name}' /etc/hosts || { echo -e '127.0.0.1\\t#{name}\\t#{basename}' | cat - /etc/hosts > /tmp/tmp-hosts mv /tmp/tmp-hosts /etc/hosts } EOH end end end end end end vagrant-2.0.2/plugins/guests/omnios/cap/mount_nfs_folder.rb000066400000000000000000000010131323370221500241000ustar00rootroot00000000000000module VagrantPlugins module GuestOmniOS module Cap class MountNFSFolder def self.mount_nfs_folder(machine, ip, folders) comm = machine.communicate commands = [] folders.each do |_, opts| commands << <<-EOH.gsub(/^ {14}/, '') mkdir -p '#{opts[:guestpath]}' /sbin/mount '#{ip}:#{opts[:hostpath]}' '#{opts[:guestpath]}' EOH end comm.sudo(commands.join("\n")) end end end end end vagrant-2.0.2/plugins/guests/omnios/cap/rsync.rb000066400000000000000000000003241323370221500216770ustar00rootroot00000000000000module VagrantPlugins module GuestOmniOS module Cap class RSync def self.rsync_install(machine) machine.communicate.sudo("pkg install rsync") end end end end end vagrant-2.0.2/plugins/guests/omnios/guest.rb000066400000000000000000000003261323370221500211270ustar00rootroot00000000000000module VagrantPlugins module GuestOmniOS class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("cat /etc/release | grep -i OmniOS") end end end end vagrant-2.0.2/plugins/guests/omnios/plugin.rb000066400000000000000000000012501323370221500212730ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestOmniOS class Plugin < Vagrant.plugin("2") name "OmniOS guest." description "OmniOS guest support." guest(:omnios, :solaris) do require_relative "guest" Guest end guest_capability(:omnios, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:omnios, :mount_nfs_folder) do require_relative "cap/mount_nfs_folder" Cap::MountNFSFolder end guest_capability(:omnios, :rsync_install) do require_relative "cap/rsync" Cap::RSync end end end end vagrant-2.0.2/plugins/guests/openbsd/000077500000000000000000000000001323370221500176005ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/openbsd/cap/000077500000000000000000000000001323370221500203435ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/openbsd/cap/change_host_name.rb000066400000000000000000000016071323370221500241560ustar00rootroot00000000000000module VagrantPlugins module GuestOpenBSD module Cap class ChangeHostName def self.change_host_name(machine, name) comm = machine.communicate if !comm.test("hostname -f | grep '^#{name}$'", sudo: false, shell: "sh") basename = name.split(".", 2)[0] command = <<-EOH.gsub(/^ {14}/, '') # Set the hostname hostname '#{name}' sed -i'' 's/^hostname=.*$/hostname=\"#{name}\"/' /etc/rc.conf echo '#{name}' > /etc/myname # Prepend ourselves to /etc/hosts grep -w '#{name}' /etc/hosts || { echo -e '127.0.0.1\\t#{name}\\t#{basename}' | cat - /etc/hosts > /tmp/tmp-hosts mv /tmp/tmp-hosts /etc/hosts } EOH comm.sudo(command, shell: "sh") end end end end end end vagrant-2.0.2/plugins/guests/openbsd/cap/configure_networks.rb000066400000000000000000000035171323370221500246130ustar00rootroot00000000000000require "tempfile" require_relative "../../../../lib/vagrant/util/template_renderer" module VagrantPlugins module GuestOpenBSD module Cap class ConfigureNetworks include Vagrant::Util def self.configure_networks(machine, networks) networks.each do |network| entry = TemplateRenderer.render("guests/openbsd/network_#{network[:type]}", options: network) Tempfile.open("vagrant-openbsd-configure-networks") do |f| f.binmode f.write(entry) f.fsync f.close machine.communicate.upload(f.path, "/tmp/vagrant-network-entry") end # Determine the interface prefix... command = "ifconfig -a | grep -o ^[0-9a-z]*" result = "" ifname = "" machine.communicate.execute(command) do |type, data| result << data if type == :stdout if result.split(/\n/).any?{|i| i.match(/vio*/)} ifname = "vio#{network[:interface]}" else ifname = "em#{network[:interface]}" end end machine.communicate.sudo("mv /tmp/vagrant-network-entry /etc/hostname.#{ifname}") # remove old configurations machine.communicate.sudo("sudo ifconfig #{ifname} inet delete", { error_check: false }) machine.communicate.sudo("pkill -f 'dhclient: #{ifname}'", { error_check: false }) if network[:type].to_sym == :static machine.communicate.sudo("ifconfig #{ifname} inet #{network[:ip]} netmask #{network[:netmask]}") elsif network[:type].to_sym == :dhcp machine.communicate.sudo("dhclient #{ifname}") end end end end end end end vagrant-2.0.2/plugins/guests/openbsd/cap/halt.rb000066400000000000000000000011361323370221500216210ustar00rootroot00000000000000module VagrantPlugins module GuestOpenBSD module Cap class Halt def self.halt(machine) begin # Versions of OpenBSD prior to 5.7 require the -h option to be # provided with the -p option. Later options allow the -h to # be optional. machine.communicate.sudo("/sbin/shutdown -p -h now", shell: "sh") rescue IOError, Vagrant::Errors::SSHDisconnected # Do nothing, because it probably means the machine shut down # and SSH connection was lost. end end end end end end vagrant-2.0.2/plugins/guests/openbsd/cap/rsync.rb000066400000000000000000000020171323370221500220260ustar00rootroot00000000000000require_relative "../../../synced_folders/rsync/default_unix_cap" module VagrantPlugins module GuestOpenBSD module Cap class RSync extend VagrantPlugins::SyncedFolderRSync::DefaultUnixCap def self.rsync_install(machine) install_output = {:stderr => '', :stdout => ''} command = 'PKG_PATH="http://ftp.openbsd.org/pub/OpenBSD/' \ '`uname -r`/packages/`arch -s`/" ' \ 'pkg_add -I rsync--' machine.communicate.sudo(command) do |type, data| install_output[type] << data if install_output.key?(type) end # pkg_add returns 0 even if package was not found, so # validate package is actually installed machine.communicate.sudo('pkg_info -cA | grep inst:rsync-[[:digit:]]', error_class: Vagrant::Errors::RSyncNotInstalledInGuest, command: command, stderr: install_output[:stderr], stdout: install_output[:stdout] ) end end end end end vagrant-2.0.2/plugins/guests/openbsd/cap/shell_expand_guest_path.rb000066400000000000000000000014141323370221500255610ustar00rootroot00000000000000module VagrantPlugins module GuestOpenBSD module Cap class ShellExpandGuestPath def self.shell_expand_guest_path(machine, path) real_path = nil path = path.gsub(/ /, '\ ') machine.communicate.execute("printf #{path}") do |type, data| if type == :stdout real_path ||= "" real_path += data end end if !real_path # If no real guest path was detected, this is really strange # and we raise an exception because this is a bug. raise Vagrant::Errors::ShellExpandFailed end # Chomp the string so that any trailing newlines are killed return real_path.chomp end end end end end vagrant-2.0.2/plugins/guests/openbsd/guest.rb000066400000000000000000000003421323370221500212530ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestOpenBSD class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("uname -s | grep 'OpenBSD'") end end end end vagrant-2.0.2/plugins/guests/openbsd/plugin.rb000066400000000000000000000026261323370221500214310ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestOpenBSD class Plugin < Vagrant.plugin("2") name "OpenBSD guest" description "OpenBSD guest support." guest(:openbsd, :bsd) do require_relative "guest" Guest end guest_capability(:openbsd, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:openbsd, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability(:openbsd, :halt) do require_relative "cap/halt" Cap::Halt end guest_capability(:openbsd, :rsync_install) do require_relative "cap/rsync" Cap::RSync end guest_capability(:openbsd, :rsync_installed) do require_relative "cap/rsync" Cap::RSync end guest_capability(:openbsd, :rsync_command) do require_relative "cap/rsync" Cap::RSync end guest_capability(:openbsd, :rsync_post) do require_relative "cap/rsync" Cap::RSync end guest_capability(:openbsd, :rsync_pre) do require_relative "cap/rsync" Cap::RSync end guest_capability(:openbsd, :shell_expand_guest_path) do require_relative "cap/shell_expand_guest_path" Cap::ShellExpandGuestPath end end end end vagrant-2.0.2/plugins/guests/photon/000077500000000000000000000000001323370221500174555ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/photon/cap/000077500000000000000000000000001323370221500202205ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/photon/cap/change_host_name.rb000066400000000000000000000013011323370221500240220ustar00rootroot00000000000000module VagrantPlugins module GuestPhoton module Cap class ChangeHostName 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 <<-EOH.gsub(/^ {14}/, '') # Set the hostname echo '#{name}' > /etc/hostname hostname '#{name}' # Prepend ourselves to /etc/hosts grep -w '#{name}' /etc/hosts || { sed -i'' '1i 127.0.0.1\\t#{name}\\t#{basename}' /etc/hosts } EOH end end end end end end vagrant-2.0.2/plugins/guests/photon/cap/configure_networks.rb000066400000000000000000000014341323370221500244640ustar00rootroot00000000000000module VagrantPlugins module GuestPhoton module Cap class ConfigureNetworks include Vagrant::Util def self.configure_networks(machine, networks) comm = machine.communicate commands = [] interfaces = [] comm.sudo("ifconfig | grep 'eth' | cut -f1 -d' '") do |_, result| interfaces = result.split("\n") end networks.each do |network| device = interfaces[network[:interface]] command = "ifconfig #{device}" command << " #{network[:ip]}" if network[:ip] command << " netmask #{network[:netmask]}" if network[:netmask] commands << command end comm.sudo(commands.join("\n")) end end end end end vagrant-2.0.2/plugins/guests/photon/cap/docker.rb000066400000000000000000000003451323370221500220160ustar00rootroot00000000000000module VagrantPlugins module GuestPhoton module Cap module Docker def self.docker_daemon_running(machine) machine.communicate.test('test -S /run/docker.sock') end end end end end vagrant-2.0.2/plugins/guests/photon/guest.rb000066400000000000000000000003511323370221500211300ustar00rootroot00000000000000module VagrantPlugins module GuestPhoton class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("cat /etc/photon-release | grep 'VMware Photon Linux'") end end end end vagrant-2.0.2/plugins/guests/photon/plugin.rb000066400000000000000000000013041323370221500212760ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestPhoton class Plugin < Vagrant.plugin("2") name "VMware Photon guest" description "VMware Photon guest support." guest(:photon, :linux) do require_relative "guest" Guest end guest_capability(:photon, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:photon, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability(:photon, :docker_daemon_running) do require_relative "cap/docker" Cap::Docker end end end end vagrant-2.0.2/plugins/guests/pld/000077500000000000000000000000001323370221500167255ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/pld/cap/000077500000000000000000000000001323370221500174705ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/pld/cap/change_host_name.rb000066400000000000000000000015531323370221500233030ustar00rootroot00000000000000module VagrantPlugins module GuestPld module Cap class ChangeHostName 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 <<-EOH.gsub(/^ {14}/, '') hostname '#{name}' sed -i 's/\\(HOSTNAME=\\).*/\\1#{name}/' /etc/sysconfig/network sed -i 's/\\(DHCP_HOSTNAME=\\).*/\\1\"#{name}\"/' /etc/sysconfig/interfaces/ifcfg-* # Prepend ourselves to /etc/hosts grep -w '#{name}' /etc/hosts || { sed -i'' '1i 127.0.0.1\\t#{name}\\t#{basename}' /etc/hosts } # Restart networking service network restart EOH end end end end end end vagrant-2.0.2/plugins/guests/pld/cap/flavor.rb000066400000000000000000000002511323370221500213040ustar00rootroot00000000000000module VagrantPlugins module GuestPld module Cap class Flavor def self.flavor(machine) return :pld end end end end end vagrant-2.0.2/plugins/guests/pld/cap/network_scripts_dir.rb000066400000000000000000000003211323370221500241070ustar00rootroot00000000000000module VagrantPlugins module GuestPld module Cap class NetworkScriptsDir def self.network_scripts_dir(machine) "/etc/sysconfig/interfaces" end end end end end vagrant-2.0.2/plugins/guests/pld/guest.rb000066400000000000000000000003061323370221500204000ustar00rootroot00000000000000module VagrantPlugins module GuestPld class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("cat /etc/pld-release") end end end end vagrant-2.0.2/plugins/guests/pld/plugin.rb000066400000000000000000000012411323370221500205460ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestPld class Plugin < Vagrant.plugin("2") name "PLD Linux guest" description "PLD Linux guest support." guest(:pld, :redhat) do require_relative "guest" Guest end guest_capability(:pld, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:pld, :network_scripts_dir) do require_relative "cap/network_scripts_dir" Cap::NetworkScriptsDir end guest_capability(:pld, :flavor) do require_relative "cap/flavor" Cap::Flavor end end end end vagrant-2.0.2/plugins/guests/redhat/000077500000000000000000000000001323370221500174155ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/redhat/cap/000077500000000000000000000000001323370221500201605ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/redhat/cap/change_host_name.rb000066400000000000000000000023641323370221500237740ustar00rootroot00000000000000module VagrantPlugins module GuestRedHat module Cap class ChangeHostName 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 <<-EOH.gsub(/^ {14}/, '') # Update sysconfig sed -i 's/\\(HOSTNAME=\\).*/\\1#{name}/' /etc/sysconfig/network # Update DNS sed -i 's/\\(DHCP_HOSTNAME=\\).*/\\1\"#{basename}\"/' /etc/sysconfig/network-scripts/ifcfg-* # Set the hostname - use hostnamectl if available echo '#{name}' > /etc/hostname if command -v hostnamectl; then hostnamectl set-hostname --static '#{name}' hostnamectl set-hostname --transient '#{name}' else hostname -F /etc/hostname fi # Prepend ourselves to /etc/hosts grep -w '#{name}' /etc/hosts || { sed -i'' '1i 127.0.0.1\\t#{name}\\t#{basename}' /etc/hosts } # Restart network service network restart EOH end end end end end end vagrant-2.0.2/plugins/guests/redhat/cap/configure_networks.rb000066400000000000000000000064531323370221500244320ustar00rootroot00000000000000require "tempfile" require_relative "../../../../lib/vagrant/util/template_renderer" module VagrantPlugins module GuestRedHat module Cap class ConfigureNetworks include Vagrant::Util extend Vagrant::Util::GuestInspection::Linux def self.configure_networks(machine, networks) comm = machine.communicate network_scripts_dir = machine.guest.capability(:network_scripts_dir) commands = {:start => [], :middle => [], :end => []} interfaces = machine.guest.capability(:network_interfaces) # Check if NetworkManager is installed on the system nmcli_installed = nmcli?(comm) networks.each.with_index do |network, i| network[:device] = interfaces[network[:interface]] extra_opts = machine.config.vm.networks[i].last.dup if nmcli_installed # Now check if the device is actively being managed by NetworkManager nm_controlled = nm_controlled?(comm, network[:device]) end if !extra_opts.key?(:nm_controlled) extra_opts[:nm_controlled] = !!nm_controlled end extra_opts[:nm_controlled] = case extra_opts[:nm_controlled] when true "yes" when false, nil "no" else extra_opts[:nm_controlled].to_s end if extra_opts[:nm_controlled] == "yes" && !nmcli_installed raise Vagrant::Errors::NetworkManagerNotInstalled, device: network[:device] end # Render a new configuration entry = TemplateRenderer.render("guests/redhat/network_#{network[:type]}", options: network.merge(extra_opts), ) # Upload the new configuration remote_path = "/tmp/vagrant-network-entry-#{network[:device]}-#{Time.now.to_i}-#{i}" Tempfile.open("vagrant-redhat-configure-networks") do |f| f.binmode f.write(entry) f.fsync f.close machine.communicate.upload(f.path, remote_path) end # Add the new interface and bring it back up final_path = "#{network_scripts_dir}/ifcfg-#{network[:device]}" if nm_controlled commands[:start] << "nmcli d disconnect iface '#{network[:device]}'" else commands[:start] << "/sbin/ifdown '#{network[:device]}'" end commands[:middle] << "mv -f '#{remote_path}' '#{final_path}'" if extra_opts[:nm_controlled] == "no" commands[:end] << "/sbin/ifup '#{network[:device]}'" end end if nmcli_installed commands[:middle] << "(test -f /etc/init.d/NetworkManager && /etc/init.d/NetworkManager restart) || " \ "((systemctl | grep NetworkManager.service) && systemctl restart NetworkManager)" end commands = commands[:start] + commands[:middle] + commands[:end] comm.sudo(commands.join("\n")) comm.wait_for_ready(5) end end end end end vagrant-2.0.2/plugins/guests/redhat/cap/flavor.rb000066400000000000000000000010551323370221500217770ustar00rootroot00000000000000module VagrantPlugins module GuestRedHat module Cap class Flavor def self.flavor(machine) # Read the version file output = "" machine.communicate.sudo("cat /etc/redhat-release") do |_, data| output = data end # Detect various flavors we care about if output =~ /(CentOS|Red Hat Enterprise|Scientific|Cloud|Virtuozzo)\s*Linux( .+)? release 7/i return :rhel_7 else return :rhel end end end end end end vagrant-2.0.2/plugins/guests/redhat/cap/network_scripts_dir.rb000066400000000000000000000003311323370221500246000ustar00rootroot00000000000000module VagrantPlugins module GuestRedHat module Cap class NetworkScriptsDir def self.network_scripts_dir(machine) "/etc/sysconfig/network-scripts" end end end end end vagrant-2.0.2/plugins/guests/redhat/cap/nfs_client.rb000066400000000000000000000012101323370221500226230ustar00rootroot00000000000000module VagrantPlugins module GuestRedHat module Cap class NFSClient def self.nfs_client_install(machine) machine.communicate.sudo <<-EOH.gsub(/^ {12}/, '') if command -v dnf; then dnf -y install nfs-utils nfs-utils-lib portmap else yum -y install nfs-utils nfs-utils-lib portmap fi if test $(ps -o comm= 1) == 'systemd'; then /bin/systemctl restart rpcbind nfs else /etc/init.d/rpcbind restart /etc/init.d/nfs restart fi EOH end end end end end vagrant-2.0.2/plugins/guests/redhat/cap/rsync.rb000066400000000000000000000005611323370221500216450ustar00rootroot00000000000000module VagrantPlugins module GuestRedHat module Cap class RSync def self.rsync_install(machine) machine.communicate.sudo <<-EOH.gsub(/^ {12}/, '') if command -v dnf; then dnf -y install rsync else yum -y install rsync fi EOH end end end end end vagrant-2.0.2/plugins/guests/redhat/guest.rb000066400000000000000000000003141323370221500210670ustar00rootroot00000000000000module VagrantPlugins module GuestRedHat class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("cat /etc/redhat-release") end end end end vagrant-2.0.2/plugins/guests/redhat/plugin.rb000066400000000000000000000021331323370221500212370ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestRedHat class Plugin < Vagrant.plugin("2") name "Red Hat Enterprise Linux guest" description "Red Hat Enterprise Linux guest support." guest(:redhat, :linux) do require_relative "guest" Guest end guest_capability(:redhat, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:redhat, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability(:redhat, :flavor) do require_relative "cap/flavor" Cap::Flavor end guest_capability(:redhat, :network_scripts_dir) do require_relative "cap/network_scripts_dir" Cap::NetworkScriptsDir end guest_capability(:redhat, :nfs_client_install) do require_relative "cap/nfs_client" Cap::NFSClient end guest_capability(:redhat, :rsync_install) do require_relative "cap/rsync" Cap::RSync end end end end vagrant-2.0.2/plugins/guests/slackware/000077500000000000000000000000001323370221500201225ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/slackware/cap/000077500000000000000000000000001323370221500206655ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/slackware/cap/change_host_name.rb000066400000000000000000000014271323370221500245000ustar00rootroot00000000000000module VagrantPlugins module GuestSlackware module Cap class ChangeHostName 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 <<-EOH.gsub(/^ {14}/, '') # Set the hostname chmod o+w /etc/hostname echo '#{name}' > /etc/hostname chmod o-w /etc/hostname hostname -F /etc/hostname # Prepend ourselves to /etc/hosts grep -w '#{name}' /etc/hosts || { sed -i'' '1i 127.0.0.1\\t#{name}\\t#{basename}' /etc/hosts } EOH end end end end end end vagrant-2.0.2/plugins/guests/slackware/cap/configure_networks.rb000066400000000000000000000025461323370221500251360ustar00rootroot00000000000000require "tempfile" require_relative "../../../../lib/vagrant/util/template_renderer" module VagrantPlugins module GuestSlackware module Cap class ConfigureNetworks include Vagrant::Util def self.configure_networks(machine, networks) comm = machine.communicate commands = [] interfaces = machine.guest.capability(:network_interfaces) # Remove any previous configuration commands << "sed -i'' -e '/^#VAGRANT-BEGIN/,/^#VAGRANT-END/ d' /etc/rc.d/rc.inet1.conf" networks.each.with_index do |network, i| network[:device] = interfaces[network[:interface]] entry = TemplateRenderer.render("guests/slackware/network_#{network[:type]}", i: i+1, options: network, ) remote_path = "/tmp/vagrant-network-#{network[:device]}-#{Time.now}-#{i}" Tempfile.open("vagrant-slackware-configure-networks") do |f| f.binmode f.write(entry) f.fsync f.close comm.upload(f.path, remote_path) end commands << "cat '#{remote_path}' >> /etc/rc.d/rc.inet1.conf" end # Restart networking commands << "/etc/rc.d/rc.inet1" comm.sudo(commands.join("\n")) end end end end end vagrant-2.0.2/plugins/guests/slackware/guest.rb000066400000000000000000000003221323370221500215730ustar00rootroot00000000000000module VagrantPlugins module GuestSlackware class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("cat /etc/slackware-version") end end end end vagrant-2.0.2/plugins/guests/slackware/plugin.rb000066400000000000000000000011101323370221500217360ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestSlackware class Plugin < Vagrant.plugin("2") name "Slackware guest" description "Slackware guest support." guest(:slackware, :linux) do require_relative "guest" Guest end guest_capability(:slackware, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:slackware, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end end end end vagrant-2.0.2/plugins/guests/smartos/000077500000000000000000000000001323370221500176365ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/smartos/cap/000077500000000000000000000000001323370221500204015ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/smartos/cap/change_host_name.rb000066400000000000000000000013001323370221500242020ustar00rootroot00000000000000module VagrantPlugins module GuestSmartos module Cap class ChangeHostName def self.change_host_name(machine, name) sudo = machine.config.smartos.suexec_cmd machine.communicate.tap do |comm| comm.execute <<-EOH.sub(/^ */, '') if hostname | grep '#{name}' ; then exit 0 fi if [ -d /usbkey ] && [ "$(zonename)" == "global" ] ; then #{sudo} sed -i '' 's/hostname=.*/hostname=#{name}/' /usbkey/config fi #{sudo} echo '#{name}' > /etc/nodename #{sudo} hostname #{name} EOH end end end end end end vagrant-2.0.2/plugins/guests/smartos/cap/configure_networks.rb000066400000000000000000000017151323370221500246470ustar00rootroot00000000000000module VagrantPlugins module GuestSmartos module Cap class ConfigureNetworks def self.configure_networks(machine, networks) su_cmd = machine.config.smartos.suexec_cmd networks.each do |network| device = "#{machine.config.smartos.device}#{network[:interface]}" ifconfig_cmd = "#{su_cmd} /sbin/ifconfig #{device}" machine.communicate.execute("#{ifconfig_cmd} plumb") if network[:type].to_sym == :static machine.communicate.execute("#{ifconfig_cmd} inet #{network[:ip]} netmask #{network[:netmask]}") machine.communicate.execute("#{ifconfig_cmd} up") machine.communicate.execute("#{su_cmd} sh -c \"echo '#{network[:ip]}' > /etc/hostname.#{device}\"") elsif network[:type].to_sym == :dhcp machine.communicate.execute("#{ifconfig_cmd} dhcp start") end end end end end end end vagrant-2.0.2/plugins/guests/smartos/cap/halt.rb000066400000000000000000000011771323370221500216640ustar00rootroot00000000000000module VagrantPlugins module GuestSmartos module Cap class Halt def self.halt(machine) # There should be an exception raised if the line # # vagrant::::profiles=Primary Administrator # # does not exist in /etc/user_attr. TODO begin machine.communicate.execute( "#{machine.config.smartos.suexec_cmd} /usr/sbin/poweroff") rescue IOError, Vagrant::Errors::SSHDisconnected # Ignore, this probably means connection closed because it # shut down. end end end end end end vagrant-2.0.2/plugins/guests/smartos/cap/insert_public_key.rb000066400000000000000000000015711323370221500244440ustar00rootroot00000000000000require "vagrant/util/shell_quote" module VagrantPlugins module GuestSmartos module Cap class InsertPublicKey def self.insert_public_key(machine, contents) contents = contents.chomp contents = Vagrant::Util::ShellQuote.escape(contents, "'") machine.communicate.tap do |comm| comm.execute <<-EOH.sub(/^ */, '') if [ -d /usbkey ] && [ "$(zonename)" == "global" ] ; then printf '#{contents}\\n' >> /usbkey/config.inc/authorized_keys cp /usbkey/config.inc/authorized_keys ~/.ssh/authorized_keys else mkdir -p ~/.ssh chmod 0700 ~/.ssh printf '#{contents}\\n' >> ~/.ssh/authorized_keys chmod 0600 ~/.ssh/authorized_keys fi EOH end end end end end end vagrant-2.0.2/plugins/guests/smartos/cap/mount_nfs.rb000066400000000000000000000015621323370221500227420ustar00rootroot00000000000000module VagrantPlugins module GuestSmartos module Cap class MountNFS def self.mount_nfs_folder(machine, ip, folders) sudo = machine.config.smartos.suexec_cmd folders.each do |name, opts| machine.communicate.tap do |comm| nfsDescription = "#{ip}:#{opts[:hostpath]}:#{opts[:guestpath]}" comm.execute <<-EOH.sub(/^ */, '') if [ -d /usbkey ] && [ "$(zonename)" == "global" ] ; then #{sudo} mkdir -p /usbkey/config.inc printf '#{nfsDescription}\\n' | #{sudo} tee -a /usbkey/config.inc/nfs_mounts fi #{sudo} mkdir -p #{opts[:guestpath]} #{sudo} /usr/sbin/mount -F nfs '#{ip}:#{opts[:hostpath]}' '#{opts[:guestpath]}' EOH end end end end end end end vagrant-2.0.2/plugins/guests/smartos/cap/remove_public_key.rb000066400000000000000000000013641323370221500244350ustar00rootroot00000000000000require "vagrant/util/shell_quote" module VagrantPlugins module GuestSmartos module Cap class RemovePublicKey def self.remove_public_key(machine, contents) contents = contents.chomp contents = Vagrant::Util::ShellQuote.escape(contents, "'") machine.communicate.tap do |comm| comm.execute <<-EOH.sub(/^ */, '') if test -f /usbkey/config.inc/authorized_keys ; then sed -i '' '/^.*#{contents}.*$/d' /usbkey/config.inc/authorized_keys fi if test -f ~/.ssh/authorized_keys ; then sed -i '' '/^.*#{contents}.*$/d' ~/.ssh/authorized_keys fi EOH end end end end end end vagrant-2.0.2/plugins/guests/smartos/cap/rsync.rb000066400000000000000000000016471323370221500220740ustar00rootroot00000000000000require_relative "../../../synced_folders/rsync/default_unix_cap" module VagrantPlugins module GuestSmartos module Cap class RSync extend VagrantPlugins::SyncedFolderRSync::DefaultUnixCap def self.rsync_installed(machine) machine.communicate.test("which rsync") end def self.rsync_command(machine) "#{machine.config.smartos.suexec_cmd} rsync" end def self.rsync_pre(machine, opts) machine.communicate.tap do |comm| comm.execute("#{machine.config.smartos.suexec_cmd} mkdir -p '#{opts[:guestpath]}'") end end def self.rsync_post(machine, opts) if opts.key?(:chown) && !opts[:chown] return end suexec_cmd = machine.config.smartos.suexec_cmd machine.communicate.execute("#{suexec_cmd} #{build_rsync_chown(opts)}") end end end end end vagrant-2.0.2/plugins/guests/smartos/config.rb000066400000000000000000000005441323370221500214330ustar00rootroot00000000000000module VagrantPlugins module GuestSmartos class Config < Vagrant.plugin("2", :config) # This sets the command to use to execute items as a superuser. # @default sudo attr_accessor :suexec_cmd attr_accessor :device def initialize @suexec_cmd = 'pfexec' @device = "e1000g" end end end end vagrant-2.0.2/plugins/guests/smartos/guest.rb000066400000000000000000000003531323370221500213130ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestSmartos class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("cat /etc/release | grep -i SmartOS") end end end end vagrant-2.0.2/plugins/guests/smartos/plugin.rb000066400000000000000000000031631323370221500214640ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestSmartos class Plugin < Vagrant.plugin("2") name "SmartOS guest." description "SmartOS guest support." config(:smartos) do require_relative "config" Config end guest(:smartos) do require_relative "guest" Guest end guest_capability(:smartos, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:smartos, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability(:smartos, :halt) do require_relative "cap/halt" Cap::Halt end guest_capability(:smartos, :insert_public_key) do require_relative "cap/insert_public_key" Cap::InsertPublicKey end guest_capability(:smartos, :mount_nfs_folder) do require_relative "cap/mount_nfs" Cap::MountNFS end guest_capability(:smartos, :remove_public_key) do require_relative "cap/remove_public_key" Cap::RemovePublicKey end guest_capability(:smartos, :rsync_installed) do require_relative "cap/rsync" Cap::RSync end guest_capability(:smartos, :rsync_command) do require_relative "cap/rsync" Cap::RSync end guest_capability(:smartos, :rsync_post) do require_relative "cap/rsync" Cap::RSync end guest_capability(:smartos, :rsync_pre) do require_relative "cap/rsync" Cap::RSync end end end end vagrant-2.0.2/plugins/guests/solaris/000077500000000000000000000000001323370221500176225ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/solaris/cap/000077500000000000000000000000001323370221500203655ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/solaris/cap/change_host_name.rb000066400000000000000000000010411323370221500241700ustar00rootroot00000000000000module VagrantPlugins module GuestSolaris module Cap class ChangeHostName def self.change_host_name(machine, name) su_cmd = machine.config.solaris.suexec_cmd # Only do this if the hostname is not already set if !machine.communicate.test("#{su_cmd} hostname | grep '#{name}'") machine.communicate.execute("#{su_cmd} sh -c \"echo '#{name}' > /etc/nodename\"") machine.communicate.execute("#{su_cmd} uname -S #{name}") end end end end end end vagrant-2.0.2/plugins/guests/solaris/cap/configure_networks.rb000066400000000000000000000017151323370221500246330ustar00rootroot00000000000000module VagrantPlugins module GuestSolaris module Cap class ConfigureNetworks def self.configure_networks(machine, networks) networks.each do |network| device = "#{machine.config.solaris.device}#{network[:interface]}" su_cmd = machine.config.solaris.suexec_cmd ifconfig_cmd = "#{su_cmd} /sbin/ifconfig #{device}" machine.communicate.execute("#{ifconfig_cmd} plumb") if network[:type].to_sym == :static machine.communicate.execute("#{ifconfig_cmd} inet #{network[:ip]} netmask #{network[:netmask]}") machine.communicate.execute("#{ifconfig_cmd} up") machine.communicate.execute("#{su_cmd} sh -c \"echo '#{network[:ip]}' > /etc/hostname.#{device}\"") elsif network[:type].to_sym == :dhcp machine.communicate.execute("#{ifconfig_cmd} dhcp start") end end end end end end end vagrant-2.0.2/plugins/guests/solaris/cap/halt.rb000066400000000000000000000012121323370221500216360ustar00rootroot00000000000000module VagrantPlugins module GuestSolaris module Cap class Halt def self.halt(machine) # There should be an exception raised if the line # # vagrant::::profiles=Primary Administrator # # does not exist in /etc/user_attr. TODO begin machine.communicate.execute( "#{machine.config.solaris.suexec_cmd} /usr/sbin/shutdown -y -i5 -g0") rescue IOError, Vagrant::Errors::SSHDisconnected # Ignore, this probably means connection closed because it # shut down. end end end end end end vagrant-2.0.2/plugins/guests/solaris/cap/insert_public_key.rb000066400000000000000000000012461323370221500244270ustar00rootroot00000000000000require "vagrant/util/shell_quote" module VagrantPlugins module GuestSolaris module Cap class InsertPublicKey def self.insert_public_key(machine, contents) # TODO: Code is identical to linux/cap/insert_public_key contents = contents.chomp contents = Vagrant::Util::ShellQuote.escape(contents, "'") machine.communicate.tap do |comm| comm.execute("mkdir -p ~/.ssh") comm.execute("chmod 0700 ~/.ssh") comm.execute("printf '#{contents}\\n' >> ~/.ssh/authorized_keys") comm.execute("chmod 0600 ~/.ssh/authorized_keys") end end end end end end vagrant-2.0.2/plugins/guests/solaris/cap/mount_virtualbox_shared_folder.rb000066400000000000000000000027311323370221500272170ustar00rootroot00000000000000module VagrantPlugins module GuestSolaris module Cap class MountVirtualBoxSharedFolder def self.mount_virtualbox_shared_folder(machine, name, guestpath, options) # These are just far easier to use than the full options syntax owner = options[:owner] group = options[:group] # Create the shared folder machine.communicate.execute("#{machine.config.solaris.suexec_cmd} mkdir -p #{guestpath}") if owner.is_a? Integer mount_uid = owner else # We have to use this `id` command instead of `/usr/bin/id` since this # one accepts the "-u" and "-g" flags. mount_uid = "`/usr/xpg4/bin/id -u #{owner}`" end if group.is_a? Integer mount_gid = group else mount_gid = "`/usr/xpg4/bin/id -g #{group}`" end # Mount the folder with the proper owner/group mount_options = "-o uid=#{mount_uid},gid=#{mount_gid}" if options[:mount_options] mount_options += ",#{options[:mount_options].join(",")}" end machine.communicate.execute("#{machine.config.solaris.suexec_cmd} /sbin/mount -F vboxfs #{mount_options} #{name} #{guestpath}") # chown the folder to the proper owner/group machine.communicate.execute("#{machine.config.solaris.suexec_cmd} chown #{mount_uid}:#{mount_gid} #{guestpath}") end end end end end vagrant-2.0.2/plugins/guests/solaris/cap/remove_public_key.rb000066400000000000000000000011571323370221500244210ustar00rootroot00000000000000require "vagrant/util/shell_quote" module VagrantPlugins module GuestSolaris module Cap class RemovePublicKey def self.remove_public_key(machine, contents) # TODO: code is identical to linux/cap/remove_public_key contents = contents.chomp contents = Vagrant::Util::ShellQuote.escape(contents, "'") machine.communicate.tap do |comm| if comm.test("test -f ~/.ssh/authorized_keys") comm.execute( "sed -i '/^.*#{contents}.*$/d' ~/.ssh/authorized_keys") end end end end end end end vagrant-2.0.2/plugins/guests/solaris/cap/rsync.rb000066400000000000000000000015771323370221500220620ustar00rootroot00000000000000require_relative "../../../synced_folders/rsync/default_unix_cap" module VagrantPlugins module GuestSolaris module Cap class RSync extend VagrantPlugins::SyncedFolderRSync::DefaultUnixCap def self.rsync_installed(machine) machine.communicate.test("which rsync") end def self.rsync_command(machine) "#{machine.config.solaris.suexec_cmd} rsync" end def self.rsync_pre(machine, opts) machine.communicate.tap do |comm| comm.sudo("mkdir -p '#{opts[:guestpath]}'") end end def self.rsync_post(machine, opts) if opts.key?(:chown) && !opts[:chown] return end suexec_cmd = machine.config.solaris.suexec_cmd machine.communicate.execute("#{suexec_cmd} #{build_rsync_chown(opts)}") end end end end end vagrant-2.0.2/plugins/guests/solaris/config.rb000066400000000000000000000015361323370221500214210ustar00rootroot00000000000000module VagrantPlugins module GuestSolaris class Config < Vagrant.plugin("2", :config) attr_accessor :halt_timeout attr_accessor :halt_check_interval attr_accessor :suexec_cmd attr_accessor :device def initialize @halt_timeout = UNSET_VALUE @halt_check_interval = UNSET_VALUE @suexec_cmd = UNSET_VALUE @device = UNSET_VALUE end def finalize! if @halt_timeout != UNSET_VALUE puts "solaris.halt_timeout is deprecated and will be removed in Vagrant 1.7" end if @halt_check_interval != UNSET_VALUE puts "solaris.halt_check_interval is deprecated and will be removed in Vagrant 1.7" end @suexec_cmd = "sudo" if @suexec_cmd == UNSET_VALUE @device = "e1000g" if @device == UNSET_VALUE end end end end vagrant-2.0.2/plugins/guests/solaris/guest.rb000066400000000000000000000005511323370221500212770ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestSolaris # A general Vagrant system implementation for "solaris". # # Contributed by Blake Irvin class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("uname -sr | grep SunOS | grep -v 5.11") end end end end vagrant-2.0.2/plugins/guests/solaris/plugin.rb000066400000000000000000000032501323370221500214450ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestSolaris class Plugin < Vagrant.plugin("2") name "Solaris guest." description "Solaris guest support." config(:solaris) do require_relative "config" Config end guest(:solaris) do require_relative "guest" Guest end guest_capability(:solaris, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:solaris, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability(:solaris, :insert_public_key) do require_relative "cap/insert_public_key" Cap::InsertPublicKey end guest_capability(:solaris, :halt) do require_relative "cap/halt" Cap::Halt end guest_capability(:solaris, :mount_virtualbox_shared_folder) do require_relative "cap/mount_virtualbox_shared_folder" Cap::MountVirtualBoxSharedFolder end guest_capability(:solaris, :remove_public_key) do require_relative "cap/remove_public_key" Cap::RemovePublicKey end guest_capability(:solaris, :rsync_installed) do require_relative "cap/rsync" Cap::RSync end guest_capability(:solaris, :rsync_command) do require_relative "cap/rsync" Cap::RSync end guest_capability(:solaris, :rsync_post) do require_relative "cap/rsync" Cap::RSync end guest_capability(:solaris, :rsync_pre) do require_relative "cap/rsync" Cap::RSync end end end end vagrant-2.0.2/plugins/guests/solaris11/000077500000000000000000000000001323370221500177645ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/solaris11/cap/000077500000000000000000000000001323370221500205275ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/solaris11/cap/change_host_name.rb000066400000000000000000000017761323370221500243510ustar00rootroot00000000000000# A general Vagrant system implementation for "solaris 11". # # Contributed by Jan Thomas Moldung module VagrantPlugins module GuestSolaris11 module Cap class ChangeHostName def self.change_host_name(machine, name) su_cmd = machine.config.solaris11.suexec_cmd # Only do this if the hostname is not already set if !machine.communicate.test("/usr/sbin/svccfg -s system/identity:node listprop config/nodename | /usr/bin/grep '#{name}'") machine.communicate.execute("#{su_cmd} /usr/sbin/svccfg -s system/identity:node setprop config/nodename=\"#{name}\"") machine.communicate.execute("#{su_cmd} /usr/sbin/svccfg -s system/identity:node setprop config/loopback=\"#{name}\"") machine.communicate.execute("#{su_cmd} /usr/sbin/svccfg -s system/identity:node refresh ") machine.communicate.execute("#{su_cmd} /usr/sbin/svcadm restart system/identity:node ") end end end end end end vagrant-2.0.2/plugins/guests/solaris11/cap/configure_networks.rb000066400000000000000000000024131323370221500247710ustar00rootroot00000000000000# A general Vagrant system implementation for "solaris 11". # # Contributed by Jan Thomas Moldung module VagrantPlugins module GuestSolaris11 module Cap class ConfigureNetworks def self.configure_networks(machine, networks) networks.each do |network| device = "#{machine.config.solaris11.device}#{network[:interface]}" su_cmd = machine.config.solaris11.suexec_cmd mask = "#{network[:netmask]}" 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 if machine.communicate.test("ipadm | grep #{device}/v4") machine.communicate.execute("#{su_cmd} ipadm delete-addr #{device}/v4") end machine.communicate.execute("#{su_cmd} ipadm create-addr -T static -a #{network[:ip]}/#{cidr} #{device}/v4") elsif network[:type].to_sym == :dhcp if machine.communicate.test("ipadm show-if -o all | grep #{device} | tr -s ' ' | cut -d ' ' -f 6 | grep '4\|6'") machine.communicate.execute("#{su_cmd} ipadm create-addr -T addrconf #{device}/v4") end end end end end end end end vagrant-2.0.2/plugins/guests/solaris11/config.rb000066400000000000000000000020531323370221500215560ustar00rootroot00000000000000# A general Vagrant system implementation for "solaris 11". # # Contributed by Jan Thomas Moldung module VagrantPlugins module GuestSolaris11 class Config < Vagrant.plugin("2", :config) attr_accessor :halt_timeout attr_accessor :halt_check_interval # This sets the command to use to execute items as a superuser. sudo is default attr_accessor :suexec_cmd attr_accessor :device def initialize @halt_timeout = UNSET_VALUE @halt_check_interval = UNSET_VALUE @suexec_cmd = UNSET_VALUE @device = UNSET_VALUE end def finalize! if @halt_timeout != UNSET_VALUE puts "solaris11.halt_timeout is deprecated and will be removed in Vagrant 1.7" end if @halt_check_interval != UNSET_VALUE puts "solaris11.halt_check_interval is deprecated and will be removed in Vagrant 1.7" end @suexec_cmd = "sudo" if @suexec_cmd == UNSET_VALUE @device = "net" if @device == UNSET_VALUE end end end end vagrant-2.0.2/plugins/guests/solaris11/guest.rb000066400000000000000000000005411323370221500214400ustar00rootroot00000000000000# A general Vagrant system implementation for "solaris 11". # # Contributed by Jan Thomas Moldung require "vagrant" module VagrantPlugins module GuestSolaris11 class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("uname -sr | grep 'SunOS.*5\\.11'") end end end end vagrant-2.0.2/plugins/guests/solaris11/plugin.rb000066400000000000000000000014331323370221500216100ustar00rootroot00000000000000# A general Vagrant system implementation for "solaris 11". # # Contributed by Jan Thomas Moldung require "vagrant" module VagrantPlugins module GuestSolaris11 class Plugin < Vagrant.plugin("2") name "Solaris 11 guest." description "Solaris 11 guest support." guest(:solaris11, :solaris) do require_relative "guest" Guest end config(:solaris11) do require_relative "config" Config end guest_capability(:solaris11, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:solaris11, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end end end end vagrant-2.0.2/plugins/guests/suse/000077500000000000000000000000001323370221500171255ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/suse/cap/000077500000000000000000000000001323370221500176705ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/suse/cap/change_host_name.rb000066400000000000000000000012461323370221500235020ustar00rootroot00000000000000module VagrantPlugins module GuestSUSE module Cap class ChangeHostName 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 <<-EOH.gsub(/^ {14}/, '') echo '#{basename}' > /etc/HOSTNAME hostname '#{basename}' # Prepend ourselves to /etc/hosts grep -w '#{name}' /etc/hosts || { sed -i'' '1i 127.0.0.1\\t#{name}\\t#{basename}' /etc/hosts } EOH end end end end end end vagrant-2.0.2/plugins/guests/suse/cap/configure_networks.rb000066400000000000000000000027011323370221500241320ustar00rootroot00000000000000require "tempfile" require_relative "../../../../lib/vagrant/util/template_renderer" module VagrantPlugins module GuestSUSE module Cap class ConfigureNetworks extend Vagrant::Util::Retryable include Vagrant::Util def self.configure_networks(machine, networks) comm = machine.communicate network_scripts_dir = machine.guest.capability(:network_scripts_dir) commands = [] interfaces = machine.guest.capability(:network_interfaces) networks.each.with_index do |network, i| network[:device] = interfaces[network[:interface]] entry = TemplateRenderer.render("guests/suse/network_#{network[:type]}", options: network, ) remote_path = "/tmp/vagrant-network-#{network[:device]}-#{Time.now.to_i}-#{i}" Tempfile.open("vagrant-suse-configure-networks") do |f| f.binmode f.write(entry) f.fsync f.close comm.upload(f.path, remote_path) end local_path = "#{network_scripts_dir}/ifcfg-#{network[:device]}" commands << <<-EOH.gsub(/^ {14}/, '') /sbin/ifdown '#{network[:device]}' || true mv '#{remote_path}' '#{local_path}' /sbin/ifup '#{network[:device]}' EOH end comm.sudo(commands.join("\n")) end end end end end vagrant-2.0.2/plugins/guests/suse/cap/halt.rb000066400000000000000000000006341323370221500211500ustar00rootroot00000000000000module VagrantPlugins module GuestSUSE module Cap class Halt def self.halt(machine) begin machine.communicate.sudo("/sbin/shutdown -h now") rescue IOError, Vagrant::Errors::SSHDisconnected # Do nothing, because it probably means the machine shut down # and SSH connection was lost. end end end end end end vagrant-2.0.2/plugins/guests/suse/cap/network_scripts_dir.rb000066400000000000000000000003171323370221500243140ustar00rootroot00000000000000module VagrantPlugins module GuestSUSE module Cap class NetworkScriptsDir def self.network_scripts_dir(machine) "/etc/sysconfig/network" end end end end end vagrant-2.0.2/plugins/guests/suse/cap/nfs_client.rb000066400000000000000000000005471323370221500223470ustar00rootroot00000000000000module VagrantPlugins module GuestSUSE module Cap class NFSClient 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 EOH end end end end end vagrant-2.0.2/plugins/guests/suse/cap/rsync.rb000066400000000000000000000005141323370221500213530ustar00rootroot00000000000000module VagrantPlugins module GuestSUSE 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.sudo("zypper -n install rsync") end end end end end vagrant-2.0.2/plugins/guests/suse/guest.rb000066400000000000000000000003541323370221500206030ustar00rootroot00000000000000module VagrantPlugins module GuestSUSE class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("test -f /etc/SuSE-release || grep -q SUSE /etc/os-release") end end end end vagrant-2.0.2/plugins/guests/suse/plugin.rb000066400000000000000000000022231323370221500207470ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestSUSE class Plugin < Vagrant.plugin("2") name "SUSE guest" description "SUSE guest support." guest(:suse, :linux) do require_relative "guest" Guest end guest_capability(:suse, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:suse, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability(:suse, :halt) do require_relative "cap/halt" Cap::Halt end guest_capability(:suse, :network_scripts_dir) do require_relative "cap/network_scripts_dir" Cap::NetworkScriptsDir end guest_capability(:suse, :nfs_client_install) do require_relative "cap/nfs_client" Cap::NFSClient end guest_capability(:suse, :rsync_install) do require_relative "cap/rsync" Cap::RSync end guest_capability(:suse, :rsync_installed) do require_relative "cap/rsync" Cap::RSync end end end end vagrant-2.0.2/plugins/guests/tinycore/000077500000000000000000000000001323370221500200025ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/tinycore/cap/000077500000000000000000000000001323370221500205455ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/tinycore/cap/change_host_name.rb000066400000000000000000000005111323370221500243510ustar00rootroot00000000000000module VagrantPlugins module GuestTinyCore module Cap class ChangeHostName def self.change_host_name(machine, name) if !machine.communicate.test("hostname | grep '^#{name}$'") machine.communicate.sudo("/usr/bin/sethostname #{name}") end end end end end end vagrant-2.0.2/plugins/guests/tinycore/cap/configure_networks.rb000066400000000000000000000014431323370221500250110ustar00rootroot00000000000000require "ipaddr" module VagrantPlugins module GuestTinyCore module Cap class ConfigureNetworks def self.configure_networks(machine, networks) machine.communicate.tap do |comm| networks.each do |n| if n[:type] == :dhcp comm.sudo("/sbin/udhcpc -b -i eth#{n[:interface]} -p /var/run/udhcpc.eth#{n[:interface]}.pid") return end ifc = "/sbin/ifconfig eth#{n[:interface]}" broadcast = (IPAddr.new(n[:ip]) | (~ IPAddr.new(n[:netmask]))).to_s comm.sudo("#{ifc} down") comm.sudo("#{ifc} #{n[:ip]} netmask #{n[:netmask]} broadcast #{broadcast}") comm.sudo("#{ifc} up") end end end end end end end vagrant-2.0.2/plugins/guests/tinycore/cap/halt.rb000066400000000000000000000006231323370221500220230ustar00rootroot00000000000000module VagrantPlugins module GuestTinyCore module Cap class Halt def self.halt(machine) begin machine.communicate.sudo("poweroff") rescue IOError, Vagrant::Errors::SSHDisconnected # Do nothing, because it probably means the machine shut down # and SSH connection was lost. end end end end end end vagrant-2.0.2/plugins/guests/tinycore/cap/mount_nfs.rb000066400000000000000000000026301323370221500231030ustar00rootroot00000000000000require_relative "../../../synced_folders/unix_mount_helpers" module VagrantPlugins module GuestTinyCore module Cap class MountNFS extend SyncedFolder::UnixMountHelpers def self.mount_nfs_folder(machine, ip, folders) folders.each do |name, opts| # Expand the guest path so we can handle things like "~/vagrant" expanded_guest_path = machine.guest.capability( :shell_expand_guest_path, opts[:guestpath]) # Do the actual creating and mounting machine.communicate.sudo("mkdir -p #{expanded_guest_path}") # Mount hostpath = opts[:hostpath].dup hostpath.gsub!("'", "'\\\\''") # Figure out any options mount_opts = ["vers=#{opts[:nfs_version]}"] mount_opts << "udp" if opts[:nfs_udp] if opts[:mount_options] mount_opts = opts[:mount_options].dup end mount_command = "mount.nfs -o '#{mount_opts.join(",")}' #{ip}:'#{hostpath}' #{expanded_guest_path}" retryable(on: Vagrant::Errors::NFSMountFailed, tries: 8, sleep: 3) do machine.communicate.sudo(mount_command, error_class: Vagrant::Errors::NFSMountFailed) end emit_upstart_notification(machine, expanded_guest_path) end end end end end end vagrant-2.0.2/plugins/guests/tinycore/cap/rsync.rb000066400000000000000000000007151323370221500222330ustar00rootroot00000000000000module VagrantPlugins module GuestTinyCore module Cap class RSync def self.rsync_install(machine) machine.communicate.tap do |comm| # Run it but don't error check because this is always failing currently comm.execute("tce-load -wi acl attr rsync", error_check: false) # Verify it by executing rsync comm.execute("rsync --help") end end end end end end vagrant-2.0.2/plugins/guests/tinycore/guest.rb000066400000000000000000000003551323370221500214610ustar00rootroot00000000000000require_relative '../linux/guest' module VagrantPlugins module GuestTinyCore class Guest < VagrantPlugins::GuestLinux::Guest # Name used for guest detection GUEST_DETECTION_NAME = "Core Linux".freeze end end end vagrant-2.0.2/plugins/guests/tinycore/plugin.rb000066400000000000000000000016661323370221500216360ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestTinyCore class Plugin < Vagrant.plugin("2") name "TinyCore Linux guest." description "TinyCore Linux guest support." guest(:tinycore, :linux) do require_relative "guest" Guest end guest_capability(:tinycore, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability(:tinycore, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:tinycore, :halt) do require_relative "cap/halt" Cap::Halt end guest_capability(:tinycore, :rsync_install) do require_relative "cap/rsync" Cap::RSync end guest_capability(:tinycore, :mount_nfs_folder) do require_relative "cap/mount_nfs" Cap::MountNFS end end end end vagrant-2.0.2/plugins/guests/trisquel/000077500000000000000000000000001323370221500200165ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/trisquel/guest.rb000066400000000000000000000004111323370221500214660ustar00rootroot00000000000000module VagrantPlugins module GuestTrisquel class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("[ -x /usr/bin/lsb_release ] && /usr/bin/lsb_release -i 2>/dev/null | grep Trisquel") end end end end vagrant-2.0.2/plugins/guests/trisquel/plugin.rb000066400000000000000000000004361323370221500216440ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestTrisquel class Plugin < Vagrant.plugin("2") name "Trisquel guest" description "Trisquel guest support." guest(:trisquel, :ubuntu) do require_relative "guest" Guest end end end end vagrant-2.0.2/plugins/guests/ubuntu/000077500000000000000000000000001323370221500174705ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/ubuntu/guest.rb000066400000000000000000000003471323370221500211500ustar00rootroot00000000000000require_relative '../linux/guest' module VagrantPlugins module GuestUbuntu class Guest < VagrantPlugins::GuestLinux::Guest # Name used for guest detection GUEST_DETECTION_NAME = "ubuntu".freeze end end end vagrant-2.0.2/plugins/guests/ubuntu/plugin.rb000066400000000000000000000004261323370221500213150ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestUbuntu class Plugin < Vagrant.plugin("2") name "Ubuntu guest" description "Ubuntu guest support." guest(:ubuntu, :debian) do require_relative "guest" Guest end end end end vagrant-2.0.2/plugins/guests/windows/000077500000000000000000000000001323370221500176405ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/windows/cap/000077500000000000000000000000001323370221500204035ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/windows/cap/change_host_name.rb000066400000000000000000000023551323370221500242170ustar00rootroot00000000000000module VagrantPlugins module GuestWindows module Cap module ChangeHostName def self.change_host_name(machine, name) change_host_name_and_wait(machine, name, machine.config.vm.graceful_halt_timeout) end def self.change_host_name_and_wait(machine, name, sleep_timeout) # If the configured name matches the current name, then bail # We cannot use %ComputerName% because it truncates at 15 chars return if machine.communicate.test("if ([System.Net.Dns]::GetHostName() -eq '#{name}') { exit 0 } exit 1") # Rename and reboot host if rename succeeded script = <<-EOH $computer = Get-WmiObject -Class Win32_ComputerSystem $retval = $computer.rename("#{name}").returnvalue if ($retval -eq 0) { shutdown /r /t 5 /f /d p:4:1 /c "Vagrant Rename Computer" } exit $retval EOH machine.communicate.execute( script, error_class: Errors::RenameComputerFailed, error_key: :rename_computer_failed) # Don't continue until the machine has shutdown and rebooted sleep(sleep_timeout) end end end end end vagrant-2.0.2/plugins/guests/windows/cap/choose_addressable_ip_addr.rb000066400000000000000000000007011323370221500262210ustar00rootroot00000000000000module VagrantPlugins module GuestWindows module Cap module ChooseAddressableIPAddr def self.choose_addressable_ip_addr(machine, possible) machine.communicate.tap do |comm| possible.each do |ip| command = "ping -n 1 -w 1 #{ip}" if comm.test(command) return ip end end end nil end end end end end vagrant-2.0.2/plugins/guests/windows/cap/configure_networks.rb000066400000000000000000000060201323370221500246430ustar00rootroot00000000000000require "log4r" require_relative "../guest_network" module VagrantPlugins module GuestWindows module Cap module ConfigureNetworks @@logger = Log4r::Logger.new("vagrant::guest::windows::configure_networks") def self.configure_networks(machine, networks) @@logger.debug("Networks: #{networks.inspect}") guest_network = GuestNetwork.new(machine.communicate) if machine.provider_name.to_s.start_with?("vmware") machine.ui.warn("Configuring secondary network adapters through VMware ") machine.ui.warn("on Windows is not yet supported. You will need to manually") machine.ui.warn("configure the network adapter.") else vm_interface_map = create_vm_interface_map(machine, guest_network) networks.each do |network| interface = vm_interface_map[network[:interface]+1] if interface.nil? @@logger.warn("Could not find interface for network #{network.inspect}") next end network_type = network[:type].to_sym if network_type == :static guest_network.configure_static_interface( interface[:index], interface[:net_connection_id], network[:ip], network[:netmask]) elsif network_type == :dhcp guest_network.configure_dhcp_interface( interface[:index], interface[:net_connection_id]) else raise "#{network_type} network type is not supported, try static or dhcp" end end end if machine.config.windows.set_work_network guest_network.set_all_networks_to_work end end def self.create_vm_interface_map(machine, guest_network) if !machine.provider.capability?(:nic_mac_addresses) raise Vagrant::Errors::CantReadMACAddresses, provider: machine.provider_name.to_s end driver_mac_address = machine.provider.capability(:nic_mac_addresses).invert @@logger.debug("mac addresses: #{driver_mac_address.inspect}") vm_interface_map = {} guest_network.network_adapters.each do |nic| @@logger.debug("nic: #{nic.inspect}") naked_mac = nic[:mac_address].gsub(':','') # If the :net_connection_id entry is nil then it is probably a virtual connection # and should be ignored. if driver_mac_address[naked_mac] && !nic[:net_connection_id].nil? vm_interface_map[driver_mac_address[naked_mac]] = { net_connection_id: nic[:net_connection_id], mac_address: naked_mac, interface_index: nic[:interface_index], index: nic[:index] } end end @@logger.debug("vm_interface_map: #{vm_interface_map.inspect}") vm_interface_map end end end end end vagrant-2.0.2/plugins/guests/windows/cap/halt.rb000066400000000000000000000007361323370221500216660ustar00rootroot00000000000000module VagrantPlugins module GuestWindows module Cap module Halt def self.halt(machine) # Fix vagrant-windows GH-129, if there's an existing scheduled # reboot cancel it so shutdown succeeds machine.communicate.execute("shutdown -a", error_check: false) # Force shutdown the machine now machine.communicate.execute("shutdown /s /t 1 /c \"Vagrant Halt\" /f /d p:4:1") end end end end end vagrant-2.0.2/plugins/guests/windows/cap/mount_shared_folder.rb000066400000000000000000000047221323370221500247600ustar00rootroot00000000000000require "vagrant/util/template_renderer" require "base64" module VagrantPlugins module GuestWindows module Cap class MountSharedFolder def self.mount_virtualbox_shared_folder(machine, name, guestpath, options) mount_shared_folder(machine, name, guestpath, "\\\\vboxsrv\\") end def self.mount_vmware_shared_folder(machine, name, guestpath, options) mount_shared_folder(machine, name, guestpath, "\\\\vmware-host\\Shared Folders\\") end def self.mount_parallels_shared_folder(machine, name, guestpath, options) mount_shared_folder(machine, name, guestpath, "\\\\psf\\") end def self.mount_smb_shared_folder(machine, name, guestpath, options) if !options[:smb_password].to_s.empty? # Ensure password is scrubbed Vagrant::Util::CredentialScrubber.sensitive(options[:smb_password]) end machine.communicate.execute("cmdkey /add:#{options[:smb_host]} /user:#{options[:smb_username]} /pass:#{options[:smb_password]}", {shell: :powershell, elevated: true}) mount_shared_folder(machine, name, guestpath, "\\\\#{options[:smb_host]}\\") end protected def self.mount_shared_folder(machine, name, guestpath, vm_provider_unc_base) name = name.gsub(/[\/\/]/,'_').sub(/^_/, '') path = File.expand_path("../../scripts/mount_volume.ps1", __FILE__) script = Vagrant::Util::TemplateRenderer.render(path, options: { mount_point: guestpath, share_name: name, vm_provider_unc_path: vm_provider_unc_base + name, }) if machine.config.vm.communicator == :winrm || machine.config.vm.communicator == :winssh machine.communicate.execute(script, shell: :powershell) else # Convert script to double byte unicode string then base64 encode # just like PowerShell -EncodedCommand expects. # Suppress the progress stream from leaking to stderr. wrapped_encoded_command = Base64.strict_encode64( "$ProgressPreference='SilentlyContinue'; #{script}; exit $LASTEXITCODE".encode('UTF-16LE', 'UTF-8')) # Execute encoded PowerShell script via OpenSSH shell machine.communicate.execute("powershell.exe -noprofile -executionpolicy bypass " + "-encodedcommand '#{wrapped_encoded_command}'", shell: "sh") end end end end end end vagrant-2.0.2/plugins/guests/windows/cap/public_key.rb000066400000000000000000000060211323370221500230550ustar00rootroot00000000000000require "tempfile" require_relative '../../../communicators/winssh/communicator' module VagrantPlugins module GuestWindows module Cap class PublicKey def self.insert_public_key(machine, contents) if machine.communicate.is_a?(CommunicatorWinSSH::Communicator) contents = contents.strip winssh_modify_authorized_keys machine do |keys| if !keys.include?(contents) keys << contents end end else raise Vagrant::Errors::SSHInsertKeyUnsupported end end def self.remove_public_key(machine, contents) if machine.communicate.is_a?(CommunicatorWinSSH::Communicator) winssh_modify_authorized_keys machine do |keys| keys.delete(contents) end else raise Vagrant::Errors::SSHInsertKeyUnsupported end end def self.winssh_modify_authorized_keys(machine) comm = machine.communicate directories = fetch_guest_paths(comm) home_dir = directories[:home] temp_dir = directories[:temp] # Ensure the user's ssh directory exists remote_ssh_dir = "#{home_dir}\\.ssh" comm.execute("dir \"#{remote_ssh_dir}\"\n if errorlevel 1 (mkdir \"#{remote_ssh_dir}\")", shell: "cmd") remote_upload_path = "#{temp_dir}\\vagrant-insert-pubkey-#{Time.now.to_i}" remote_authkeys_path = "#{remote_ssh_dir}\\authorized_keys" keys_file = Tempfile.new("vagrant-windows-insert-public-key") keys_file.close # Check if an authorized_keys file already exists result = comm.execute("dir \"#{remote_authkeys_path}\"", shell: "cmd", error_check: false) if result == 0 comm.download(remote_authkeys_path, keys_file.path) keys = File.read(keys_file.path).split(/[\r\n]+/) else keys = [] end yield keys File.write(keys_file.path, keys.join("\r\n") + "\r\n") comm.upload(keys_file.path, remote_upload_path) keys_file.delete comm.execute <<-EOC.gsub(/^\s*/, ""), shell: "powershell" Set-Acl "#{remote_upload_path}" (Get-Acl "#{remote_authkeys_path}") Move-Item -Force "#{remote_upload_path}" "#{remote_authkeys_path}" EOC end # Fetch user's temporary and home directory paths from the Windows guest # # @param [Communicator] # @return [Hash] {:temp, :home} def self.fetch_guest_paths(communicator) output = "" communicator.execute("echo %TEMP%\necho %USERPROFILE%", shell: "cmd") do |type, data| if type == :stdout output << data end end temp_dir, home_dir = output.strip.split(/[\r\n]+/) if temp_dir.nil? || home_dir.nil? raise Errors::PublicKeyDirectoryFailure end {temp: temp_dir, home: home_dir} end end end end end vagrant-2.0.2/plugins/guests/windows/cap/reboot.rb000066400000000000000000000013431323370221500222230ustar00rootroot00000000000000module VagrantPlugins module GuestWindows module Cap class Reboot def self.wait_for_reboot(machine) # Technically it should be possible to make it work with SSH # too, but we don't yet. return if machine.config.vm.communicator != :winrm script = File.expand_path("../../scripts/reboot_detect.ps1", __FILE__) script = File.read(script) while machine.communicate.execute(script, error_check: false) != 0 sleep 10 end # This re-establishes our symbolic links if they were # created between now and a reboot machine.communicate.execute("net use", error_check: false) end end end end end vagrant-2.0.2/plugins/guests/windows/cap/rsync.rb000066400000000000000000000014211323370221500220640ustar00rootroot00000000000000module VagrantPlugins module GuestWindows module Cap class RSync def self.rsync_scrub_guestpath( machine, opts ) # Windows guests most often use cygwin-dependent rsync utilities # that expect "/cygdrive/c" instead of "c:" as the path prefix # some vagrant code may pass guest paths with drive-lettered paths here opts[:guestpath].gsub( /^([a-zA-Z]):/, '/cygdrive/\1' ) end def self.rsync_pre(machine, opts) machine.communicate.tap do |comm| # rsync does not construct any gaps in the path to the target directory # make sure that all subdirectories are created comm.execute("mkdir -p '#{opts[:guestpath]}'") end end end end end end vagrant-2.0.2/plugins/guests/windows/config.rb000066400000000000000000000007721323370221500214400ustar00rootroot00000000000000module VagrantPlugins module GuestWindows class Config < Vagrant.plugin("2", :config) attr_accessor :set_work_network def initialize @set_work_network = UNSET_VALUE end def validate(machine) errors = [] errors << "windows.set_work_network cannot be nil." if @set_work_network.nil? { "Windows Guest" => errors } end def finalize! @set_work_network = false if @set_work_network == UNSET_VALUE end end end end vagrant-2.0.2/plugins/guests/windows/errors.rb000066400000000000000000000010661323370221500215040ustar00rootroot00000000000000module VagrantPlugins module GuestWindows module Errors # A convenient superclass for all our errors. class WindowsError < Vagrant::Errors::VagrantError error_namespace("vagrant_windows.errors") end class NetworkWinRMRequired < WindowsError error_key(:network_winrm_required) end class RenameComputerFailed < WindowsError error_key(:rename_computer_failed) end class PublicKeyDirectoryFailure < WindowsError error_key(:public_key_directory_failure) end end end end vagrant-2.0.2/plugins/guests/windows/guest.rb000066400000000000000000000004001323370221500213060ustar00rootroot00000000000000module VagrantPlugins module GuestWindows class Guest < Vagrant.plugin("2", :guest) def detect?(machine) # See if the Windows directory is present. machine.communicate.test("test -d $Env:SystemRoot") end end end end vagrant-2.0.2/plugins/guests/windows/guest_network.rb000066400000000000000000000114301323370221500230640ustar00rootroot00000000000000require "log4r" module VagrantPlugins module GuestWindows # Manages the remote Windows guest network. class GuestNetwork PS_GET_WSMAN_VER = '((test-wsman).productversion.split(" ") | select -last 1).split("\.")[0]' WQL_NET_ADAPTERS_V2 = 'SELECT * FROM Win32_NetworkAdapter WHERE MACAddress IS NOT NULL' def initialize(communicator) @logger = Log4r::Logger.new("vagrant::windows::guestnetwork") @communicator = communicator end # Returns an array of all NICs on the guest. Each array entry is a # Hash of the NICs properties. # # @return [Array] def network_adapters wsman_version == 2? network_adapters_v2_winrm : network_adapters_v3_winrm end # Checks to see if the specified NIC is currently configured for DHCP. # # @return [Boolean] def is_dhcp_enabled(nic_index) cmd = <<-EOH if (Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "Index=#{nic_index} and DHCPEnabled=True") { exit 0 } exit 1 EOH @communicator.test(cmd) end # Configures the specified interface for DHCP # # @param [Integer] The interface index. # @param [String] The unique name of the NIC, such as 'Local Area Connection'. def configure_dhcp_interface(nic_index, net_connection_id) @logger.info("Configuring NIC #{net_connection_id} for DHCP") if !is_dhcp_enabled(nic_index) netsh = "netsh interface ip set address \"#{net_connection_id}\" dhcp" @communicator.execute(netsh) end end # Configures the specified interface using a static address # # @param [Integer] The interface index. # @param [String] The unique name of the NIC, such as 'Local Area Connection'. # @param [String] The static IP address to assign to the specified NIC. # @param [String] The network mask to use with the static IP. def configure_static_interface(nic_index, net_connection_id, ip, netmask) @logger.info("Configuring NIC #{net_connection_id} using static ip #{ip}") #netsh interface ip set address "Local Area Connection 2" static 192.168.33.10 255.255.255.0 netsh = "netsh interface ip set address \"#{net_connection_id}\" static #{ip} #{netmask}" @communicator.execute(netsh) end # Sets all networks on the guest to 'Work Network' mode. This is # to allow guest access from the host via a private IP on Win7 # https://github.com/WinRb/vagrant-windows/issues/63 def set_all_networks_to_work @logger.info("Setting all networks to 'Work Network'") command = File.read(File.expand_path("../scripts/set_work_network.ps1", __FILE__)) @communicator.execute(command, { shell: :powershell }) end protected # Checks the WinRS version on the guest. Usually 2 on Windows 7/2008 # and 3 on Windows 8/2012. # # @return [Integer] def wsman_version @logger.debug("querying WSMan version") version = '' @communicator.execute(PS_GET_WSMAN_VER, { shell: :powershell }) do |type, line| version = version + "#{line}" if type == :stdout && !line.nil? end @logger.debug("wsman version: #{version}") Integer(version) end # Returns an array of all NICs on the guest. Each array entry is a # Hash of the NICs properties. This method should only be used on # guests that have WinRS version 2. # # @return [Array] def network_adapters_v2_winrm @logger.debug("querying network adapters") # Get all NICs that have a MAC address # https://msdn.microsoft.com/en-us/library/windows/desktop/aa394216(v=vs.85).aspx adapters = @communicator.execute(WQL_NET_ADAPTERS_V2, { shell: :wql } )[:win32_network_adapter] @logger.debug("#{adapters.inspect}") return adapters end # Returns an array of all NICs on the guest. Each array entry is a # Hash of the NICs properties. This method should only be used on # guests that have WinRS version 3. # # This method is a workaround until the WinRM gem supports WinRS version 3. # # @return [Array] def network_adapters_v3_winrm command = File.read(File.expand_path("../scripts/winrs_v3_get_adapters.ps1", __FILE__)) output = "" @communicator.execute(command, { shell: :powershell }) do |type, line| output = output + "#{line}" if type == :stdout && !line.nil? end adapters = [] JSON.parse(output).each do |nic| adapters << nic.inject({}){ |memo,(k,v)| memo[k.to_sym] = v; memo } end @logger.debug("#{adapters.inspect}") return adapters end end end end vagrant-2.0.2/plugins/guests/windows/plugin.rb000066400000000000000000000047221323370221500214700ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestWindows autoload :Errors, File.expand_path("../errors", __FILE__) class Plugin < Vagrant.plugin("2") name "Windows guest." description "Windows guest support." config(:windows) do require_relative "config" Config end guest(:windows) do require_relative "guest" init! Guest end guest_capability(:windows, :change_host_name) do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability(:windows, :configure_networks) do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability(:windows, :halt) do require_relative "cap/halt" Cap::Halt end guest_capability(:windows, :mount_virtualbox_shared_folder) do require_relative "cap/mount_shared_folder" Cap::MountSharedFolder end guest_capability(:windows, :mount_vmware_shared_folder) do require_relative "cap/mount_shared_folder" Cap::MountSharedFolder end guest_capability(:windows, :mount_parallels_shared_folder) do require_relative "cap/mount_shared_folder" Cap::MountSharedFolder end guest_capability(:windows, :wait_for_reboot) do require_relative "cap/reboot" Cap::Reboot end guest_capability(:windows, :choose_addressable_ip_addr) do require_relative "cap/choose_addressable_ip_addr" Cap::ChooseAddressableIPAddr end guest_capability(:windows, :mount_smb_shared_folder) do require_relative "cap/mount_shared_folder" Cap::MountSharedFolder end guest_capability(:windows, :rsync_scrub_guestpath) do require_relative "cap/rsync" Cap::RSync end guest_capability(:windows, :rsync_pre) do require_relative "cap/rsync" Cap::RSync end guest_capability(:windows, :insert_public_key) do require_relative "cap/public_key" Cap::PublicKey end guest_capability(:windows, :remove_public_key) do require_relative "cap/public_key" Cap::PublicKey end protected def self.init! return if defined?(@_init) I18n.load_path << File.expand_path( "templates/locales/guest_windows.yml", Vagrant.source_root) I18n.reload! @_init = true end end end end vagrant-2.0.2/plugins/guests/windows/scripts/000077500000000000000000000000001323370221500213275ustar00rootroot00000000000000vagrant-2.0.2/plugins/guests/windows/scripts/mount_volume.ps1.erb000066400000000000000000000025371323370221500252630ustar00rootroot00000000000000function Test-ReparsePoint([string]$path) { $file = Get-Item $path -Force -ea 0 return [bool]($file.Attributes -band [IO.FileAttributes]::ReparsePoint) } $MountPoint = [System.IO.Path]::GetFullPath("<%= options[:mount_point] %>") $ShareName = "<%= options[:share_name] %>" $VmProviderUncPath = "<%= options[:vm_provider_unc_path] %>" # https://github.com/BIAINC/vagrant-windows/issues/4 # Not sure why this works, but it does. & net use $ShareName 2>&1 | Out-Null Write-Debug "Attempting to mount $ShareName to $MountPoint" if( (Test-Path "$MountPoint") -and (Test-ReparsePoint "$MountPoint") ) { Write-Debug "Junction already exists, so I will delete it" # Powershell refuses to delete junctions, oh well use cmd cmd /c rd "$MountPoint" if ( $LASTEXITCODE -ne 0 ) { Write-Error "Failed to delete symbolic link at $MountPoint" exit 1 } } elseif(Test-Path $MountPoint) { Write-Error "Mount point already exists and is not a symbolic link" exit 1 } $BaseDirectory = [System.IO.Path]::GetDirectoryName($MountPoint) if (-not (Test-Path $BaseDirectory)) { Write-Debug "Creating parent directory for mount point $BaseDirectory" New-Item $BaseDirectory -Type Directory -Force | Out-Null } cmd /c mklink /D "$MountPoint" "$VmProviderUncPath" | out-null if ( $LASTEXITCODE -ne 0 ) { exit 1 } vagrant-2.0.2/plugins/guests/windows/scripts/reboot_detect.ps1000066400000000000000000000020771323370221500246040ustar00rootroot00000000000000# Function to check whether machine is currently shutting down function ShuttingDown { [string]$sourceCode = @" using System; using System.Runtime.InteropServices; namespace Vagrant { public static class RemoteManager { private const int SM_SHUTTINGDOWN = 0x2000; [DllImport("User32.dll", CharSet = CharSet.Unicode)] private static extern int GetSystemMetrics(int Index); public static bool Shutdown() { return (0 != GetSystemMetrics(SM_SHUTTINGDOWN)); } } } "@ $type = Add-Type -TypeDefinition $sourceCode -PassThru return $type::Shutdown() } if (ShuttingDown) { exit 1 } else { # See if a reboot is scheduled in the future by trying to schedule a reboot . shutdown.exe -f -r -t 60 if ($LASTEXITCODE -eq 1190) { # reboot is already pending exit 2 } if ($LASTEXITCODE -eq 1115) { # A system shutdown is in progress exit 2 } # Remove the pending reboot we just created above if ($LASTEXITCODE -eq 0) { . shutdown.exe -a } } # no reboot in progress or scheduled exit 0 vagrant-2.0.2/plugins/guests/windows/scripts/set_work_network.ps1000066400000000000000000000004751323370221500253700ustar00rootroot00000000000000# Get network connections $networkListManager = [Activator]::CreateInstance([Type]::GetTypeFromCLSID([Guid]"{DCB00C01-570F-4A9B-8D69-199FDBA5723B}")) $connections = $networkListManager.GetNetworkConnections() # Set network location to Private for all networks $connections | % {$_.GetNetwork().SetCategory(1)} vagrant-2.0.2/plugins/guests/windows/scripts/winrs_v3_get_adapters.ps1000066400000000000000000000006161323370221500262530ustar00rootroot00000000000000$adapters = get-ciminstance win32_networkadapter -filter "macaddress is not null" $processed = @() foreach ($adapter in $adapters) { $Processed += new-object PSObject -Property @{ mac_address = $adapter.macaddress net_connection_id = $adapter.netconnectionid interface_index = $adapter.interfaceindex index = $adapter.index } } convertto-json -inputobject $processed vagrant-2.0.2/plugins/hosts/000077500000000000000000000000001323370221500157745ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/alt/000077500000000000000000000000001323370221500165545ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/alt/cap/000077500000000000000000000000001323370221500173175ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/alt/cap/nfs.rb000066400000000000000000000023221323370221500204310ustar00rootroot00000000000000require "vagrant/util/subprocess" require "vagrant/util/which" module VagrantPlugins module HostALT module Cap class NFS def self.nfs_check_command(env) if systemd? return "systemctl status --no-pager nfs-server.service" else return "/etc/init.d/nfs status" end end def self.nfs_start_command(env) if systemd? return "systemctl start rpcbind nfs-server.service" else return "/etc/init.d/nfs restart" end end def self.nfs_installed(environment) if systemd? system("systemctl --no-pager --no-legend --plain list-unit-files --all --type=service | grep --fixed-strings --quiet nfs-server.service") else system("rpm -q nfs-server --quiet 2>&1") end end protected # This tests to see if systemd is used on the system. This is used # in newer versions of ALT, and requires a change in behavior. def self.systemd? result = Vagrant::Util::Subprocess.execute("ps", "-o", "comm=", "1") return result.stdout.chomp == "systemd" end end end end end vagrant-2.0.2/plugins/hosts/alt/host.rb000066400000000000000000000003061323370221500200550ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostALT class Host < Vagrant.plugin("2", :host) def detect?(env) File.exist?("/etc/altlinux-release") end end end end vagrant-2.0.2/plugins/hosts/alt/plugin.rb000066400000000000000000000013101323370221500203720ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostALT class Plugin < Vagrant.plugin("2") name "ALT Platform host" description "ALT Platform host support." host("alt", "linux") do require_relative "host" Host end host_capability("alt", "nfs_installed") do require_relative "cap/nfs" Cap::NFS end # Linux-specific helpers we need to determine paths that can # be overriden. host_capability("alt", "nfs_check_command") do require_relative "cap/nfs" Cap::NFS end host_capability("alt", "nfs_start_command") do require_relative "cap/nfs" Cap::NFS end end end end vagrant-2.0.2/plugins/hosts/arch/000077500000000000000000000000001323370221500167115ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/arch/cap/000077500000000000000000000000001323370221500174545ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/arch/cap/nfs.rb000066400000000000000000000010541323370221500205670ustar00rootroot00000000000000module VagrantPlugins module HostArch module Cap class NFS def self.nfs_check_command(env) return "/usr/sbin/systemctl status --no-pager nfs-server.service" end def self.nfs_start_command(env) return "/usr/sbin/systemctl start nfs-server.service" end def self.nfs_installed(environment) Kernel.system("systemctl --no-pager --no-legend --plain list-unit-files --all --type=service | grep --fixed-strings --quiet nfs-server.service") end end end end end vagrant-2.0.2/plugins/hosts/arch/host.rb000066400000000000000000000003031323370221500202070ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostArch class Host < Vagrant.plugin("2", :host) def detect?(env) File.exist?("/etc/arch-release") end end end end vagrant-2.0.2/plugins/hosts/arch/plugin.rb000066400000000000000000000012751323370221500205410ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostArch class Plugin < Vagrant.plugin("2") name "Arch host" description "Arch host support." host("arch", "linux") do require_relative "host" Host end host_capability("arch", "nfs_installed") do require_relative "cap/nfs" Cap::NFS end # Linux-specific helpers we need to determine paths that can # be overriden. host_capability("arch", "nfs_check_command") do require_relative "cap/nfs" Cap::NFS end host_capability("arch", "nfs_start_command") do require_relative "cap/nfs" Cap::NFS end end end end vagrant-2.0.2/plugins/hosts/bsd/000077500000000000000000000000001323370221500165445ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/bsd/cap/000077500000000000000000000000001323370221500173075ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/bsd/cap/nfs.rb000066400000000000000000000145511323370221500204300ustar00rootroot00000000000000require "log4r" require "vagrant/util" require "vagrant/util/shell_quote" require "vagrant/util/which" module VagrantPlugins module HostBSD module Cap class NFS def self.nfs_export(environment, ui, id, ips, folders) nfs_exports_template = environment.host.capability(:nfs_exports_template) nfs_restart_command = environment.host.capability(:nfs_restart_command) logger = Log4r::Logger.new("vagrant::hosts::bsd") nfs_checkexports! if File.file?("/etc/exports") # We need to build up mapping of directories that are enclosed # within each other because the exports file has to have subdirectories # of an exported directory on the same line. e.g.: # # "/foo" "/foo/bar" ... # "/bar" # # We build up this mapping within the following hash. logger.debug("Compiling map of sub-directories for NFS exports...") dirmap = {} folders.sort_by { |_, opts| opts[:hostpath] }.each do |_, opts| hostpath = opts[:hostpath].dup hostpath.gsub!('"', '\"') found = false dirmap.each do |dirs, diropts| dirs.each do |dir| if dir.start_with?(hostpath) || hostpath.start_with?(dir) # TODO: verify opts and diropts are _identical_, raise an error # if not. NFS mandates subdirectories have identical options. dirs << hostpath found = true break end end break if found end if !found dirmap[[hostpath]] = opts.dup end end # Sort all the keys by length so that the directory closest to # the root is exported first. Also, remove duplicates so that # checkexports will work properly. dirmap.each do |dirs, _| dirs.uniq! dirs.sort_by! { |d| d.length } end # Setup the NFS options dirmap.each do |dirs, opts| if !opts[:bsd__nfs_options] opts[:bsd__nfs_options] = ["alldirs"] end hasmapall = false opts[:bsd__nfs_options].each do |opt| # mapall/maproot are mutually exclusive, so we have to check # for both here. if opt =~ /^mapall=/ || opt =~ /^maproot=/ hasmapall = true break end end if !hasmapall opts[:bsd__nfs_options] << "mapall=#{opts[:map_uid]}:#{opts[:map_gid]}" end opts[:bsd__compiled_nfs_options] = opts[:bsd__nfs_options].map do |opt| "-#{opt}" end.join(" ") end logger.info("Exporting the following for NFS...") dirmap.each do |dirs, opts| logger.info("NFS DIR: #{dirs.inspect}") logger.info("NFS OPTS: #{opts.inspect}") end output = Vagrant::Util::TemplateRenderer.render(nfs_exports_template, uuid: id, ips: ips, folders: dirmap, user: Process.uid) # The sleep ensures that the output is truly flushed before any `sudo` # commands are issued. ui.info I18n.t("vagrant.hosts.bsd.nfs_export") sleep 0.5 # First, clean up the old entry nfs_cleanup(id) # Only use "sudo" if we can't write to /etc/exports directly sudo_command = "" sudo_command = "sudo " if !File.writable?("/etc/exports") # Output the rendered template into the exports output.split("\n").each do |line| line = Vagrant::Util::ShellQuote.escape(line, "'") system( "echo '#{line}' | " + "#{sudo_command}/usr/bin/tee -a /etc/exports >/dev/null") end # We run restart here instead of "update" just in case nfsd # is not starting system(*nfs_restart_command) end def self.nfs_exports_template(environment) "nfs/exports" end def self.nfs_installed(environment) !!Vagrant::Util::Which.which("nfsd") end def self.nfs_prune(environment, ui, valid_ids) return if !File.exist?("/etc/exports") logger = Log4r::Logger.new("vagrant::hosts::bsd") logger.info("Pruning invalid NFS entries...") output = false user = Process.uid File.read("/etc/exports").lines.each do |line| if id = line[/^# VAGRANT-BEGIN:( #{user})? ([\.\/A-Za-z0-9\-_:]+?)$/, 2] if valid_ids.include?(id) logger.debug("Valid ID: #{id}") else if !output # We want to warn the user but we only want to output once ui.info I18n.t("vagrant.hosts.bsd.nfs_prune") output = true end logger.info("Invalid ID, pruning: #{id}") nfs_cleanup(id) end end end rescue Errno::EACCES raise Vagrant::Errors::NFSCantReadExports end def self.nfs_restart_command(environment) ["sudo", "nfsd", "restart"] end protected def self.nfs_cleanup(id) return if !File.exist?("/etc/exports") # Escape sed-sensitive characters: id = id.gsub("/", "\\/") id = id.gsub(".", "\\.") user = Process.uid command = [] command << "sudo" if !File.writable?("/etc/exports") command += [ "sed", "-E", "-e", "/^# VAGRANT-BEGIN:( #{user})? #{id}/," + "/^# VAGRANT-END:( #{user})? #{id}/ d", "-ibak", "/etc/exports" ] # Use sed to just strip out the block of code which was inserted # by Vagrant, and restart NFS. system(*command) end def self.nfs_checkexports! r = Vagrant::Util::Subprocess.execute("nfsd", "checkexports") if r.exit_code != 0 raise Vagrant::Errors::NFSBadExports, output: r.stderr end end end end end end vagrant-2.0.2/plugins/hosts/bsd/host.rb000066400000000000000000000003571323370221500200530ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostBSD # Represents a BSD host, such as FreeBSD. class Host < Vagrant.plugin("2", :host) def detect?(env) Vagrant::Util::Platform.darwin? end end end end vagrant-2.0.2/plugins/hosts/bsd/plugin.rb000066400000000000000000000015101323370221500203640ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostBSD class Plugin < Vagrant.plugin("2") name "BSD host" description "BSD host support." host("bsd") do require File.expand_path("../host", __FILE__) Host end host_capability("bsd", "nfs_export") do require_relative "cap/nfs" Cap::NFS end host_capability("bsd", "nfs_exports_template") do require_relative "cap/nfs" Cap::NFS end host_capability("bsd", "nfs_installed") do require_relative "cap/nfs" Cap::NFS end host_capability("bsd", "nfs_prune") do require_relative "cap/nfs" Cap::NFS end host_capability("bsd", "nfs_restart_command") do require_relative "cap/nfs" Cap::NFS end end end end vagrant-2.0.2/plugins/hosts/darwin/000077500000000000000000000000001323370221500172605ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/darwin/cap/000077500000000000000000000000001323370221500200235ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/darwin/cap/configured_ip_addresses.rb000066400000000000000000000006071323370221500252250ustar00rootroot00000000000000require "socket" module VagrantPlugins module HostDarwin module Cap class ConfiguredIPAddresses def self.configured_ip_addresses(env) Socket.getifaddrs.map do |interface| if interface.addr.ipv4? && !interface.addr.ipv4_loopback? interface.addr.ip_address end end.compact end end end end end vagrant-2.0.2/plugins/hosts/darwin/cap/provider_install_virtualbox.rb000066400000000000000000000042571323370221500262170ustar00rootroot00000000000000require "pathname" require "tempfile" require "vagrant/util/downloader" require "vagrant/util/file_checksum" require "vagrant/util/subprocess" module VagrantPlugins module HostDarwin module Cap class ProviderInstallVirtualBox # The URL to download VirtualBox is hardcoded so we can have a # known-good version to download. URL = "http://download.virtualbox.org/virtualbox/5.0.10/VirtualBox-5.0.10-104061-OSX.dmg".freeze VERSION = "5.0.10".freeze SHA256SUM = "62f933115498e51ddf5f2dab47dc1eebb42eb78ea1a7665cb91c53edacc847c6".freeze def self.provider_install_virtualbox(env) path = Dir::Tmpname.create("vagrant-provider-install-virtualbox") {} # Prefixed UI for prettiness ui = Vagrant::UI::Prefixed.new(env.ui, "") # Start by downloading the file using the standard mechanism ui.output(I18n.t( "vagrant.hosts.darwin.virtualbox_install_download", version: VERSION)) ui.detail(I18n.t( "vagrant.hosts.darwin.virtualbox_install_detail")) dl = Vagrant::Util::Downloader.new(URL, path, ui: ui) dl.download! # Validate that the file checksum matches actual = FileChecksum.new(path, Digest::SHA2).checksum if actual != SHA256SUM raise Vagrant::Errors::ProviderChecksumMismatch, provider: "virtualbox", actual: actual, expected: SHA256SUM end # Launch it ui.output(I18n.t( "vagrant.hosts.darwin.virtualbox_install_install")) ui.detail(I18n.t( "vagrant.hosts.darwin.virtualbox_install_install_detail")) script = File.expand_path("../../scripts/install_virtualbox.sh", __FILE__) result = Vagrant::Util::Subprocess.execute("bash", script, path) if result.exit_code != 0 raise Vagrant::Errors::ProviderInstallFailed, provider: "virtualbox", stdout: result.stdout, stderr: result.stderr end ui.success(I18n.t("vagrant.hosts.darwin.virtualbox_install_success")) end end end end end vagrant-2.0.2/plugins/hosts/darwin/cap/rdp.rb000066400000000000000000000035501323370221500211400ustar00rootroot00000000000000require "pathname" require "tmpdir" require "vagrant/util/subprocess" module VagrantPlugins module HostDarwin module Cap class RDP def self.rdp_client(env, rdp_info) config_path = self.generate_config_file(rdp_info) begin Vagrant::Util::Subprocess.execute("open", config_path.to_s) ensure # Note: this technically will never get run; neither would an # at_exit call. The reason is that `exec` replaces this process, # effectively the same as `kill -9`. This is solely here to prove # that and so that future developers do not waste a ton of time # try to identify why Vagrant is leaking RDP connection files. # There is a catch-22 here in that we can't delete the file before # we exec, and we can't delete the file after we exec :(. File.unlink(config_path) if File.file?(config_path) end end protected # Generates an RDP connection file and returns the resulting path. # @return [String] def self.generate_config_file(rdp_info) opts = { "drivestoredirect:s" => "*", "full address:s" => "#{rdp_info[:host]}:#{rdp_info[:port]}", "prompt for credentials:i" => "1", "username:s" => rdp_info[:username], } # Create the ".rdp" file t = ::Tempfile.new(["vagrant-rdp", ".rdp"]).tap do |f| f.binmode opts.each do |k, v| f.puts("#{k}:#{v}") end if rdp_info[:extra_args] rdp_info[:extra_args].each do |arg| f.puts("#{arg}") end end f.fsync f.close end return t.path end end end end end vagrant-2.0.2/plugins/hosts/darwin/cap/smb.rb000066400000000000000000000116011323370221500211300ustar00rootroot00000000000000module VagrantPlugins module HostDarwin module Cap class SMB @@logger = Log4r::Logger.new("vagrant::host::darwin::smb") # If we have the sharing binary available, smb is installed def self.smb_installed(env) File.exist?("/usr/sbin/sharing") end # Check if the required SMB services are loaded and enabled. If they are # not, then start them up def self.smb_start(env) result = Vagrant::Util::Subprocess.execute("pwpolicy", "gethashtypes") if result.exit_code == 0 && !result.stdout.include?("SMB-NT") @@logger.error("SMB compatible password has not been stored") raise SyncedFolderSMB::Errors::SMBCredentialsMissing end result = Vagrant::Util::Subprocess.execute("launchctl", "list", "com.apple.smb.preferences") if result.exit_code != 0 @@logger.warn("smb preferences service not enabled. enabling and starting...") cmd = ["/bin/launchctl", "load", "-w", "/System/Library/LaunchDaemons/com.apple.smb.preferences.plist"] result = Vagrant::Util::Subprocess.execute("/usr/bin/sudo", *cmd) if result.exit_code != 0 raise SyncedFolderSMB::Errors::SMBStartFailed, command: cmd.join(" "), stderr: result.stderr, stdout: result.stdout end end result = Vagrant::Util::Subprocess.execute("launchctl", "list", "com.apple.smbd") if result.exit_code != 0 @@logger.warn("smbd service not enabled. enabling and starting...") cmd = ["/bin/launchctl", "load", "-w", "/System/Library/LaunchDaemons/com.apple.smbd.plist"] result = Vagrant::Util::Subprocess.execute("/usr/bin/sudo", *cmd) if result.exit_code != 0 raise SyncedFolderSMB::Errors::SMBStartFailed, command: cmd.join(" "), stderr: result.stderr, stdout: result.stdout end Vagrant::Util::Subprocess.execute("/usr/bin/sudo", "/bin/launchctl", "start", "com.apple.smbd") end end # Required options for mounting a share hosted # on macos. def self.smb_mount_options(env) ["sec=ntlmssp", "nounix", "noperm"] end def self.smb_cleanup(env, machine, opts) m_id = machine_id(machine) result = Vagrant::Util::Subprocess.execute("/usr/bin/sudo", "/usr/sbin/sharing", "-l") if result.exit_code != 0 @@logger.warn("failed to locate any shares for cleanup") end shares = result.stdout.split("\n").map do |line| if line.start_with?("name:") share_name = line.sub("name:", "").strip share_name if share_name.start_with?("vgt-#{m_id}") end end.compact @@logger.debug("shares to be removed: #{shares}") shares.each do |share_name| @@logger.info("removing share name=#{share_name}") share_name.strip! result = Vagrant::Util::Subprocess.execute("/usr/bin/sudo", "/usr/sbin/sharing", "-r", share_name) if result.exit_code != 0 # Removing always returns 0 even if there are currently # guests attached so if we get a non-zero value just # log it as unexpected @@logger.warn("removing share `#{share_name}` returned non-zero") end end end def self.smb_prepare(env, machine, folders, opts) folders.each do |id, data| hostpath = data[:hostpath] chksum_id = Digest::MD5.hexdigest(id) name = "vgt-#{machine_id(machine)}-#{chksum_id}" data[:smb_id] ||= name @@logger.info("creating new share name=#{name} id=#{data[:smb_id]}") cmd = [ "/usr/bin/sudo", "/usr/sbin/sharing", "-a", hostpath, "-S", data[:smb_id], "-s", "001", "-g", "000", "-n", name ] r = Vagrant::Util::Subprocess.execute(*cmd) if r.exit_code != 0 raise VagrantPlugins::SyncedFolderSMB::Errors::DefineShareFailed, host: hostpath.to_s, stderr: r.stderr, stdout: r.stdout end end end # Generates a unique identifier for the given machine # based on the name, provider name, and working directory # of the environment. # # @param [Vagrant::Machine] machine # @return [String] def self.machine_id(machine) @@logger.debug("generating machine ID name=#{machine.name} cwd=#{machine.env.cwd}") Digest::MD5.hexdigest("#{machine.name}-#{machine.provider_name}-#{machine.env.cwd}") end end end end end vagrant-2.0.2/plugins/hosts/darwin/host.rb000066400000000000000000000003221323370221500205570ustar00rootroot00000000000000require "vagrant/util/platform" module VagrantPlugins module HostDarwin class Host < Vagrant.plugin("2", :host) def detect?(env) Vagrant::Util::Platform.darwin? end end end end vagrant-2.0.2/plugins/hosts/darwin/plugin.rb000066400000000000000000000024201323370221500211010ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostDarwin class Plugin < Vagrant.plugin("2") name "Mac OS X host" description "Mac OS X host support." host("darwin", "bsd") do require_relative "host" Host end host_capability("darwin", "provider_install_virtualbox") do require_relative "cap/provider_install_virtualbox" Cap::ProviderInstallVirtualBox end host_capability("darwin", "rdp_client") do require_relative "cap/rdp" Cap::RDP end host_capability("darwin", "smb_installed") do require_relative "cap/smb" Cap::SMB end host_capability("darwin", "smb_prepare") do require_relative "cap/smb" Cap::SMB end host_capability("darwin", "smb_mount_options") do require_relative "cap/smb" Cap::SMB end host_capability("darwin", "smb_cleanup") do require_relative "cap/smb" Cap::SMB end host_capability("darwin", "smb_start") do require_relative "cap/smb" Cap::SMB end host_capability("darwin", "configured_ip_addresses") do require_relative "cap/configured_ip_addresses" Cap::ConfiguredIPAddresses end end end end vagrant-2.0.2/plugins/hosts/darwin/scripts/000077500000000000000000000000001323370221500207475ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/darwin/scripts/install_virtualbox.sh000077500000000000000000000003611323370221500252330ustar00rootroot00000000000000#!/bin/bash set -e hdiutil attach $1 cd /Volumes/VirtualBox/ sudo installer -pkg VirtualBox.pkg -target "/" cd /tmp flag=1 while [ $flag -ne 0 ]; do sleep 1 set +e hdiutil detach /Volumes/VirtualBox/ flag=$? set -e done vagrant-2.0.2/plugins/hosts/freebsd/000077500000000000000000000000001323370221500174065ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/freebsd/cap/000077500000000000000000000000001323370221500201515ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/freebsd/cap/nfs.rb000066400000000000000000000014721323370221500212700ustar00rootroot00000000000000require "vagrant/util" require "vagrant/util/retryable" require Vagrant.source_root.join("plugins", "hosts", "bsd", "cap", "nfs") module VagrantPlugins module HostFreeBSD module Cap class NFS def self.nfs_export(environment, ui, id, ips, folders) folders.each do |folder_name, folder_values| if folder_values[:hostpath] =~ /\s+/ raise Vagrant::Errors::VagrantError, _key: :freebsd_nfs_whitespace end end HostBSD::Cap::NFS.nfs_export(environment, ui, id, ips, folders) end def self.nfs_exports_template(environment) "nfs/exports_freebsd" end def self.nfs_restart_command(environment) ["sudo", "/etc/rc.d/mountd", "onereload"] end end end end end vagrant-2.0.2/plugins/hosts/freebsd/host.rb000066400000000000000000000003461323370221500207130ustar00rootroot00000000000000require "vagrant" require 'vagrant/util/platform' module VagrantPlugins module HostFreeBSD class Host < Vagrant.plugin("2", :host) def detect?(env) Vagrant::Util::Platform.freebsd? end end end end vagrant-2.0.2/plugins/hosts/freebsd/plugin.rb000066400000000000000000000012261323370221500212320ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostFreeBSD class Plugin < Vagrant.plugin("2") name "FreeBSD host" description "FreeBSD host support." host("freebsd", "bsd") do require_relative "host" Host end host_capability("freebsd", "nfs_export") do require_relative "cap/nfs" Cap::NFS end # BSD-specific helpers host_capability("freebsd", "nfs_exports_template") do require_relative "cap/nfs" Cap::NFS end host_capability("freebsd", "nfs_restart_command") do require_relative "cap/nfs" Cap::NFS end end end end vagrant-2.0.2/plugins/hosts/gentoo/000077500000000000000000000000001323370221500172675ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/gentoo/cap/000077500000000000000000000000001323370221500200325ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/gentoo/cap/nfs.rb000066400000000000000000000017011323370221500211440ustar00rootroot00000000000000require "vagrant/util/subprocess" require "vagrant/util/which" module VagrantPlugins module HostGentoo module Cap class NFS def self.nfs_check_command(env) if Vagrant::Util::Platform.systemd? "#{systemctl_path} status --no-pager nfs-server.service" else "/etc/init.d/nfs status" end end def self.nfs_start_command(env) if Vagrant::Util::Platform.systemd? "#{systemctl_path} start rpcbind nfs-server.service" else "/etc/init.d/nfs restart" end end protected def self.systemctl_path path = Vagrant::Util::Which.which("systemctl") return path if path folders = ["/usr/bin", "/usr/sbin"] folders.each do |folder| path = "#{folder}/systemctl" return path if File.file?(path) end end end end end end vagrant-2.0.2/plugins/hosts/gentoo/host.rb000066400000000000000000000003101323370221500205630ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostGentoo class Host < Vagrant.plugin("2", :host) def detect?(env) File.exists?("/etc/gentoo-release") end end end end vagrant-2.0.2/plugins/hosts/gentoo/plugin.rb000066400000000000000000000011311323370221500211060ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostGentoo class Plugin < Vagrant.plugin("2") name "Gentoo host" description "Gentoo host support." host("gentoo", "linux") do require_relative "host" Host end # Linux-specific helpers we need to determine paths that can # be overriden. host_capability("gentoo", "nfs_check_command") do require_relative "cap/nfs" Cap::NFS end host_capability("gentoo", "nfs_start_command") do require_relative "cap/nfs" Cap::NFS end end end end vagrant-2.0.2/plugins/hosts/linux/000077500000000000000000000000001323370221500171335ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/linux/cap/000077500000000000000000000000001323370221500176765ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/linux/cap/nfs.rb000066400000000000000000000242511323370221500210150ustar00rootroot00000000000000require "shellwords" require "vagrant/util" require "vagrant/util/shell_quote" require "vagrant/util/retryable" module VagrantPlugins module HostLinux module Cap class NFS NFS_EXPORTS_PATH = "/etc/exports".freeze NFS_DEFAULT_NAME_SYSTEMD = "nfs-server.service".freeze NFS_DEFAULT_NAME_SYSV = "nfs-kernel-server".freeze extend Vagrant::Util::Retryable def self.nfs_service_name_systemd if !defined?(@_nfs_systemd) result = Vagrant::Util::Subprocess.execute("systemctl", "list-units", "*nfs*server*", "--no-pager", "--no-legend") if result.exit_code == 0 @_nfs_systemd = result.stdout.to_s.split(/\s+/).first end if @_nfs_systemd.to_s.empty? @_nfs_systemd = NFS_DEFAULT_NAME_SYSTEMD end end @_nfs_systemd end def self.nfs_service_name_sysv if !defined?(@_nfs_sysv) @_nfs_sysv = Dir.glob("/etc/init.d/*nfs*server*").first.to_s if @_nfs_sysv.empty? @_nfs_sysv = NFS_DEFAULT_NAME_SYSV else @_nfs_sysv = File.basename(@_nfs_sysv) end end @_nfs_sysv end def self.nfs_apply_command(env) "exportfs -ar" end def self.nfs_check_command(env) if Vagrant::Util::Platform.systemd? "systemctl status --no-pager #{nfs_service_name_systemd}" else "/etc/init.d/#{nfs_service_name_sysv} status" end end def self.nfs_start_command(env) if Vagrant::Util::Platform.systemd? "systemctl start #{nfs_service_name_systemd}" else "/etc/init.d/#{nfs_service_name_sysv} start" end end def self.nfs_export(env, ui, id, ips, folders) # Get some values we need before we do anything nfs_apply_command = env.host.capability(:nfs_apply_command) nfs_check_command = env.host.capability(:nfs_check_command) nfs_start_command = env.host.capability(:nfs_start_command) nfs_opts_setup(folders) folders = folder_dupe_check(folders) output = Vagrant::Util::TemplateRenderer.render('nfs/exports_linux', uuid: id, ips: ips, folders: folders, user: Process.uid) ui.info I18n.t("vagrant.hosts.linux.nfs_export") sleep 0.5 nfs_cleanup("#{Process.uid} #{id}") output = "#{nfs_exports_content}\n#{output}" nfs_write_exports(output) if nfs_running?(nfs_check_command) Vagrant::Util::Subprocess.execute("sudo", *Shellwords.split(nfs_apply_command)).exit_code == 0 else Vagrant::Util::Subprocess.execute("sudo", *Shellwords.split(nfs_start_command)).exit_code == 0 end end def self.nfs_installed(environment) if Vagrant::Util::Platform.systemd? Vagrant::Util::Subprocess.execute("/bin/sh", "-c", "systemctl --no-pager --no-legend --plain list-unit-files --all --type=service " \ "| grep #{nfs_service_name_systemd}").exit_code == 0 else Vagrant::Util::Subprocess.execute("modinfo", "nfsd").exit_code == 0 || Vagrant::Util::Subprocess.execute("grep", "nfsd", "/proc/filesystems").exit_code == 0 end end def self.nfs_prune(environment, ui, valid_ids) return if !File.exist?(NFS_EXPORTS_PATH) logger = Log4r::Logger.new("vagrant::hosts::linux") logger.info("Pruning invalid NFS entries...") user = Process.uid # Create editor instance for removing invalid IDs editor = Vagrant::Util::StringBlockEditor.new(nfs_exports_content) # Build composite IDs with UID information and discover invalid entries composite_ids = valid_ids.map do |v_id| "#{user} #{v_id}" end remove_ids = editor.keys - composite_ids logger.debug("Known valid NFS export IDs: #{valid_ids}") logger.debug("Composite valid NFS export IDs with user: #{composite_ids}") logger.debug("NFS export IDs to be removed: #{remove_ids}") if !remove_ids.empty? ui.info I18n.t("vagrant.hosts.linux.nfs_prune") nfs_cleanup(remove_ids) end end protected # Takes a hash of folders and removes any duplicate exports that # share the same hostpath to avoid duplicate entries in /etc/exports # ref: GH-4666 def self.folder_dupe_check(folders) return_folders = {} # Group by hostpath to see if there are multiple exports coming # from the same folder export_groups = folders.values.group_by { |h| h[:hostpath] } # We need to check that each group key only has 1 value, # and if not, check each nfs option. If all nfs options are the same # we're good, otherwise throw an exception export_groups.each do |path,group| if group.size > 1 # if the linux nfs options aren't all the same throw an exception group1_opts = group.first[:linux__nfs_options] if !group.all? {|g| g[:linux__nfs_options] == group1_opts} raise Vagrant::Errors::NFSDupePerms, hostpath: group.first[:hostpath] else # if they're the same just pick the first one return_folders[path] = group.first end else # just return folder, there are no duplicates return_folders[path] = group.first end end return_folders end def self.nfs_cleanup(remove_ids) return if !File.exist?(NFS_EXPORTS_PATH) editor = Vagrant::Util::StringBlockEditor.new(nfs_exports_content) remove_ids = Array(remove_ids) # Remove all invalid ID entries remove_ids.each do |r_id| editor.delete(r_id) end nfs_write_exports(editor.value) end def self.nfs_write_exports(new_exports_content) if(nfs_exports_content != new_exports_content.strip) begin # Write contents out to temporary file new_exports_file = Tempfile.create('vagrant') new_exports_file.puts(new_exports_content) new_exports_file.close new_exports_path = new_exports_file.path # Only use "sudo" if we can't write to /etc/exports directly sudo_command = "" sudo_command = "sudo " if !File.writable?(NFS_EXPORTS_PATH) # Ensure new file mode and uid/gid match existing file to replace existing_stat = File.stat(NFS_EXPORTS_PATH) new_stat = File.stat(new_exports_path) if existing_stat.mode != new_stat.mode File.chmod(existing_stat.mode, new_exports_path) end if existing_stat.uid != new_stat.uid || existing_stat.gid != new_stat.gid chown_cmd = "#{sudo_command}chown #{existing_stat.uid}:#{existing_stat.gid} #{new_exports_path}" result = Vagrant::Util::Subprocess.execute(*Shellwords.split(chown_cmd)) if result.exit_code != 0 raise Vagrant::Errors::NFSExportsFailed, command: chown_cmd, stderr: result.stderr, stdout: result.stdout end end # Always force move the file to prevent overwrite prompting mv_cmd = "#{sudo_command}mv -f #{new_exports_path} #{NFS_EXPORTS_PATH}" result = Vagrant::Util::Subprocess.execute(*Shellwords.split(mv_cmd)) if result.exit_code != 0 raise Vagrant::Errors::NFSExportsFailed, command: mv_cmd, stderr: result.stderr, stdout: result.stdout end ensure if File.exist?(new_exports_path) File.unlink(new_exports_path) end end end end def self.nfs_exports_content if(File.exist?(NFS_EXPORTS_PATH)) if(File.readable?(NFS_EXPORTS_PATH)) File.read(NFS_EXPORTS_PATH) else cmd = "sudo cat #{NFS_EXPORTS_PATH}" result = Vagrant::Util::Subprocess.execute(*Shellwords.split(cmd)) if result.exit_code != 0 raise Vagrant::Errors::NFSExportsFailed, command: cmd, stderr: result.stderr, stdout: result.stdout else result.stdout end end else "" end end def self.nfs_opts_setup(folders) folders.each do |k, opts| if !opts[:linux__nfs_options] opts[:linux__nfs_options] ||= ["rw", "no_subtree_check", "all_squash"] end # Only automatically set anonuid/anongid if they weren't # explicitly set by the user. hasgid = false hasuid = false opts[:linux__nfs_options].each do |opt| hasgid = !!(opt =~ /^anongid=/) if !hasgid hasuid = !!(opt =~ /^anonuid=/) if !hasuid end opts[:linux__nfs_options] << "anonuid=#{opts[:map_uid]}" if !hasuid opts[:linux__nfs_options] << "anongid=#{opts[:map_gid]}" if !hasgid opts[:linux__nfs_options] << "fsid=#{opts[:uuid]}" end end def self.nfs_running?(check_command) Vagrant::Util::Subprocess.execute(*Shellwords.split(check_command)).exit_code == 0 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 vagrant-2.0.2/plugins/hosts/linux/cap/rdp.rb000066400000000000000000000025551323370221500210170ustar00rootroot00000000000000require "vagrant/util/which" module VagrantPlugins module HostLinux module Cap class RDP def self.rdp_client(env, rdp_info) # Detect if an RDP client is available. # Prefer xfreerdp as it supports newer versions of RDP. rdp_client = if Vagrant::Util::Which.which("xfreerdp") "xfreerdp" elsif Vagrant::Util::Which.which("rdesktop") "rdesktop" else raise Vagrant::Errors::LinuxRDPClientNotFound end args = [] # Build appropriate arguments for the RDP client. case rdp_client when "xfreerdp" args << "/u:#{rdp_info[:username]}" args << "/p:#{rdp_info[:password]}" if rdp_info[:password] args << "/v:#{rdp_info[:host]}:#{rdp_info[:port]}" args += rdp_info[:extra_args] if rdp_info[:extra_args] when "rdesktop" args << "-u" << rdp_info[:username] args << "-p" << rdp_info[:password] if rdp_info[:password] args += rdp_info[:extra_args] if rdp_info[:extra_args] args << "#{rdp_info[:host]}:#{rdp_info[:port]}" end # Finally, run the client. Vagrant::Util::Subprocess.execute(rdp_client, *args) end end end end end vagrant-2.0.2/plugins/hosts/linux/host.rb000066400000000000000000000003671323370221500204430ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostLinux # Represents a Linux based host, such as Ubuntu. class Host < Vagrant.plugin("2", :host) def detect?(env) Vagrant::Util::Platform.linux? end end end end vagrant-2.0.2/plugins/hosts/linux/plugin.rb000066400000000000000000000021751323370221500207630ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostLinux class Plugin < Vagrant.plugin("2") name "Linux host" description "Linux host support." host("linux") do require_relative "host" Host end host_capability("linux", "nfs_export") do require_relative "cap/nfs" Cap::NFS end host_capability("linux", "nfs_installed") do require_relative "cap/nfs" Cap::NFS end host_capability("linux", "nfs_prune") do require_relative "cap/nfs" Cap::NFS end host_capability("linux", "rdp_client") do require_relative "cap/rdp" Cap::RDP end # Linux-specific helpers we need to determine paths that can # be overriden. host_capability("linux", "nfs_apply_command") do require_relative "cap/nfs" Cap::NFS end host_capability("linux", "nfs_check_command") do require_relative "cap/nfs" Cap::NFS end host_capability("linux", "nfs_start_command") do require_relative "cap/nfs" Cap::NFS end end end end vagrant-2.0.2/plugins/hosts/null/000077500000000000000000000000001323370221500167465ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/null/host.rb000066400000000000000000000003331323370221500202470ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostNull class Host < Vagrant.plugin("2", :host) def detect?(env) # This host can only be explicitly chosen. false end end end end vagrant-2.0.2/plugins/hosts/null/plugin.rb000066400000000000000000000004251323370221500205720ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostNull class Plugin < Vagrant.plugin("2") name "null host" description "A host that implements no capabilities." host("null") do require_relative "host" Host end end end end vagrant-2.0.2/plugins/hosts/redhat/000077500000000000000000000000001323370221500172435ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/redhat/cap/000077500000000000000000000000001323370221500200065ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/redhat/cap/nfs.rb000066400000000000000000000033211323370221500211200ustar00rootroot00000000000000require "pathname" module VagrantPlugins module HostRedHat module Cap class NFS def self.nfs_check_command(env) if Vagrant::Util::Platform.systemd? "systemctl status --no-pager nfs-server.service" else "#{nfs_server_binary} status" end end def self.nfs_start_command(env) if Vagrant::Util::Platform.systemd? "systemctl start nfs-server.service" else "#{nfs_server_binary} start" end end protected def self.nfs_server_binary nfs_server_binary = "/etc/init.d/nfs" # On Fedora 16+, systemd replaced init.d, so we have to use the # proper NFS binary. This checks to see if we need to do that. release_file = Pathname.new("/etc/redhat-release") begin release_file.open("r:ISO-8859-1:UTF-8") do |f| match = /(Red Hat|CentOS|Fedora).* release ([0-9]+)/.match(f.gets) if match distribution = match[1] version_number = match[2].to_i if (distribution =~ /Fedora/ && version_number >= 16) || (distribution =~ /Red Hat|CentOS/ && version_number >= 7) # "service nfs-server" will redirect properly to systemctl # when "service nfs-server restart" is called. nfs_server_binary = "/usr/sbin/service nfs-server" end end end rescue Errno::ENOENT # File doesn't exist, not a big deal, assume we're on a # lower version. end nfs_server_binary end end end end end vagrant-2.0.2/plugins/hosts/redhat/host.rb000066400000000000000000000014521323370221500205470ustar00rootroot00000000000000require "pathname" require "vagrant" module VagrantPlugins module HostRedHat class Host < Vagrant.plugin("2", :host) def detect?(env) release_file = Pathname.new("/etc/redhat-release") if release_file.exist? release_file.open("r:ISO-8859-1:UTF-8") do |f| contents = f.gets return true if contents =~ /^CentOS/ # CentOS return true if contents =~ /^Fedora/ # Fedora return true if contents =~ /^Korora/ # Korora # Oracle Linux < 5.3 return true if contents =~ /^Enterprise Linux Enterprise Linux/ # Red Hat Enterprise Linux and Oracle Linux >= 5.3 return true if contents =~ /^Red Hat Enterprise Linux/ end end false end end end end vagrant-2.0.2/plugins/hosts/redhat/plugin.rb000066400000000000000000000012221323370221500210630ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostRedHat class Plugin < Vagrant.plugin("2") name "Red Hat Enterprise Linux host" description "Red Hat Enterprise Linux host support." host("redhat", "linux") do require File.expand_path("../host", __FILE__) Host end # Linux-specific helpers we need to determine paths that can # be overriden. host_capability("redhat", "nfs_check_command") do require_relative "cap/nfs" Cap::NFS end host_capability("redhat", "nfs_start_command") do require_relative "cap/nfs" Cap::NFS end end end end vagrant-2.0.2/plugins/hosts/slackware/000077500000000000000000000000001323370221500177505ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/slackware/cap/000077500000000000000000000000001323370221500205135ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/slackware/cap/nfs.rb000066400000000000000000000004351323370221500216300ustar00rootroot00000000000000module VagrantPlugins module HostSlackware module Cap class NFS def self.nfs_check_command(env) "/sbin/pidof nfsd >/dev/null" end def self.nfs_start_command(env) "/etc/rc.d/rc.nfsd start" end end end end end vagrant-2.0.2/plugins/hosts/slackware/host.rb000066400000000000000000000004151323370221500212520ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostSlackware class Host < Vagrant.plugin("2", :host) def detect?(env) return File.exists?("/etc/slackware-version") || !Dir.glob("/usr/lib/setup/Plamo-*").empty? end end end end vagrant-2.0.2/plugins/hosts/slackware/plugin.rb000066400000000000000000000012211323370221500215670ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostSlackware class Plugin < Vagrant.plugin("2") name "Slackware host" description "Slackware and derivertives host support." host("slackware", "linux") do require File.expand_path("../host", __FILE__) Host end # Linux-specific helpers we need to determine paths that can # be overriden. host_capability("slackware", "nfs_check_command") do require_relative "cap/nfs" Cap::NFS end host_capability("slackware", "nfs_start_command") do require_relative "cap/nfs" Cap::NFS end end end end vagrant-2.0.2/plugins/hosts/suse/000077500000000000000000000000001323370221500167535ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/suse/cap/000077500000000000000000000000001323370221500175165ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/suse/cap/nfs.rb000066400000000000000000000006201323370221500206270ustar00rootroot00000000000000module VagrantPlugins module HostSUSE module Cap class NFS def self.nfs_installed(env) system("rpm -q nfs-kernel-server > /dev/null 2>&1") end def self.nfs_check_command(env) "/sbin/service nfsserver status" end def self.nfs_start_command(env) "/sbin/service nfsserver start" end end end end end vagrant-2.0.2/plugins/hosts/suse/host.rb000066400000000000000000000012121323370221500202510ustar00rootroot00000000000000require "pathname" require "vagrant" module VagrantPlugins module HostSUSE class Host < Vagrant.plugin("2", :host) def detect?(env) old_release_file = Pathname.new("/etc/SuSE-release") if old_release_file.exist? old_release_file.open("r") do |f| return true if f.gets =~ /^(openSUSE|SUSE Linux Enterprise)/ end end new_release_file = Pathname.new("/etc/os-release") if new_release_file.exist? new_release_file.open("r") do |f| return true if f.gets =~ /(openSUSE|SLES)/ end end false end end end end vagrant-2.0.2/plugins/hosts/suse/plugin.rb000066400000000000000000000011441323370221500205760ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostSUSE class Plugin < Vagrant.plugin("2") name "SUSE host" description "SUSE host support." host("suse", "linux") do require_relative "host" Host end host_capability("suse", "nfs_installed") do require_relative "cap/nfs" Cap::NFS end host_capability("suse", "nfs_check_command") do require_relative "cap/nfs" Cap::NFS end host_capability("suse", "nfs_start_command") do require_relative "cap/nfs" Cap::NFS end end end end vagrant-2.0.2/plugins/hosts/windows/000077500000000000000000000000001323370221500174665ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/windows/cap/000077500000000000000000000000001323370221500202315ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/windows/cap/configured_ip_addresses.rb000066400000000000000000000013421323370221500254300ustar00rootroot00000000000000require "pathname" require "tempfile" require "vagrant/util/downloader" require "vagrant/util/file_checksum" require "vagrant/util/powershell" require "vagrant/util/subprocess" module VagrantPlugins module HostWindows module Cap class ConfiguredIPAddresses def self.configured_ip_addresses(env) script_path = File.expand_path("../../scripts/host_info.ps1", __FILE__) r = Vagrant::Util::PowerShell.execute(script_path) if r.exit_code != 0 raise Vagrant::Errors::PowerShellError, script: script_path, stderr: r.stderr end res = JSON.parse(r.stdout)["ip_addresses"] Array(res) end end end end end vagrant-2.0.2/plugins/hosts/windows/cap/nfs.rb000066400000000000000000000002461323370221500213460ustar00rootroot00000000000000module VagrantPlugins module HostWindows module Cap class NFS def self.nfs_installed(env) false end end end end end vagrant-2.0.2/plugins/hosts/windows/cap/provider_install_virtualbox.rb000066400000000000000000000043201323370221500264140ustar00rootroot00000000000000require "pathname" require "tempfile" require "vagrant/util/downloader" require "vagrant/util/file_checksum" require "vagrant/util/powershell" require "vagrant/util/subprocess" module VagrantPlugins module HostWindows module Cap class ProviderInstallVirtualBox # The URL to download VirtualBox is hardcoded so we can have a # known-good version to download. URL = "http://download.virtualbox.org/virtualbox/5.0.10/VirtualBox-5.0.10-104061-Win.exe".freeze VERSION = "5.0.10".freeze SHA256SUM = "3e5ed8fe4ada6eef8dfb4fe6fd79fcab4b242acf799f7d3ab4a17b43838b1e04".freeze def self.provider_install_virtualbox(env) path = Dir::Tmpname.create("vagrant-provider-install-virtualbox") {} # Prefixed UI for prettiness ui = Vagrant::UI::Prefixed.new(env.ui, "") # Start by downloading the file using the standard mechanism ui.output(I18n.t( "vagrant.hosts.windows.virtualbox_install_download", version: VERSION)) ui.detail(I18n.t( "vagrant.hosts.windows.virtualbox_install_detail")) dl = Vagrant::Util::Downloader.new(URL, path, ui: ui) dl.download! # Validate that the file checksum matches actual = FileChecksum.new(path, Digest::SHA2).checksum if actual != SHA256SUM raise Vagrant::Errors::ProviderChecksumMismatch, provider: "virtualbox", actual: actual, expected: SHA256SUM end # Launch it ui.output(I18n.t( "vagrant.hosts.windows.virtualbox_install_install")) ui.detail(I18n.t( "vagrant.hosts.windows.virtualbox_install_install_detail")) script = File.expand_path("../../scripts/install_virtualbox.ps1", __FILE__) result = Vagrant::Util::PowerShell.execute(script, path) if result.exit_code != 0 raise Vagrant::Errors::ProviderInstallFailed, provider: "virtualbox", stdout: result.stdout, stderr: result.stderr end ui.success(I18n.t("vagrant.hosts.windows.virtualbox_install_success")) end end end end end vagrant-2.0.2/plugins/hosts/windows/cap/ps.rb000066400000000000000000000026401323370221500212020ustar00rootroot00000000000000require "pathname" require "tmpdir" require "vagrant/util/safe_exec" module VagrantPlugins module HostWindows module Cap class PS def self.ps_client(env, ps_info) logger = Log4r::Logger.new("vagrant::hosts::windows") command = <<-EOS $plain_password = "#{ps_info[:password]}" $username = "#{ps_info[:username]}" $port = "#{ps_info[:port]}" $hostname = "#{ps_info[:host]}" $password = ConvertTo-SecureString $plain_password -asplaintext -force $creds = New-Object System.Management.Automation.PSCredential ("$hostname\\$username", $password) function prompt { kill $PID } Enter-PSSession -ComputerName $hostname -Credential $creds -Port $port EOS logger.debug("Starting remote powershell with command:\n#{command}") args = ["-NoProfile"] args << "-ExecutionPolicy" args << "Bypass" args << "-NoExit" args << "-EncodedCommand" args << encoded(command) if ps_info[:extra_args] args << ps_info[:extra_args] end # Launch it Vagrant::Util::SafeExec.exec("powershell", *args) end def self.encoded(script) encoded_script = script.encode('UTF-16LE', 'UTF-8') Base64.strict_encode64(encoded_script) end end end end end vagrant-2.0.2/plugins/hosts/windows/cap/rdp.rb000066400000000000000000000014231323370221500213430ustar00rootroot00000000000000require "pathname" require "tmpdir" require "vagrant/util/subprocess" module VagrantPlugins module HostWindows module Cap class RDP def self.rdp_client(env, rdp_info) # Setup password cmdKeyArgs = [ "/add:#{rdp_info[:host]}:#{rdp_info[:port]}", "/user:#{rdp_info[:username]}", "/pass:#{rdp_info[:password]}", ] Vagrant::Util::Subprocess.execute("cmdkey", *cmdKeyArgs) # Build up the args to mstsc args = ["/v:#{rdp_info[:host]}:#{rdp_info[:port]}"] if rdp_info[:extra_args] args = rdp_info[:extra_args] + args end # Launch it Vagrant::Util::Subprocess.execute("mstsc", *args) end end end end end vagrant-2.0.2/plugins/hosts/windows/cap/smb.rb000066400000000000000000000123731323370221500213450ustar00rootroot00000000000000module VagrantPlugins module HostWindows module Cap class SMB # Number of seconds to display UAC warning to user UAC_PROMPT_WAIT = 4 @@logger = Log4r::Logger.new("vagrant::host::windows::smb") def self.smb_installed(env) psv = Vagrant::Util::PowerShell.version.to_i if psv < 3 return false end true end def self.smb_validate_password(env, machine, username, password) script_path = File.expand_path("../../scripts/check_credentials.ps1", __FILE__) args = [] args << "-username" << "'#{username.gsub("'", "''")}'" args << "-password" << "'#{password.gsub("'", "''")}'" r = Vagrant::Util::PowerShell.execute(script_path, *args) r.exit_code == 0 end def self.smb_cleanup(env, machine, opts) script_path = File.expand_path("../../scripts/unset_share.ps1", __FILE__) m_id = machine_id(machine) prune_shares = existing_shares.map do |share_name, share_info| if share_info["Description"].start_with?("vgt-#{m_id}-") @@logger.info("removing smb share name=#{share_name} id=#{m_id}") share_name else @@logger.info("skipping smb share removal, not owned name=#{share_name}") @@logger.debug("smb share ID not present name=#{share_name} id=#{m_id} description=#{share_info["Description"]}") nil end end.compact @@logger.debug("shares to be removed: #{prune_shares}") if prune_shares.size > 0 machine.env.ui.warn("\n" + I18n.t("vagrant_sf_smb.uac.prune_warning") + "\n") sleep UAC_PROMPT_WAIT @@logger.info("remove shares: #{prune_shares}") result = Vagrant::Util::PowerShell.execute(script_path, *prune_shares, sudo: true) if result.exit_code != 0 failed_name = result.stdout.to_s.sub("share name: ", "") raise SyncedFolderSMB::Errors::PruneShareFailed, name: failed_name, stderr: result.stderr, stdout: result.stdout end end end def self.smb_prepare(env, machine, folders, opts) script_path = File.expand_path("../../scripts/set_share.ps1", __FILE__) shares = [] current_shares = existing_shares folders.each do |id, data| hostpath = data[:hostpath].to_s chksum_id = Digest::MD5.hexdigest(id) name = "vgt-#{machine_id(machine)}-#{chksum_id}" data[:smb_id] ||= name # Check if this name is already in use if share_info = current_shares[data[:smb_id]] exist_path = File.expand_path(share_info["Path"]).downcase request_path = File.expand_path(hostpath).downcase if !hostpath.empty? && exist_path != request_path raise SyncedFolderSMB::Errors::SMBNameError, path: hostpath, existing_path: share_info["Path"], name: data[:smb_id] end @@logger.info("skip creation of existing share name=#{name} id=#{data[:smb_id]}") next end @@logger.info("creating new share name=#{name} id=#{data[:smb_id]}") shares << [ "\"#{hostpath.gsub("/", "\\")}\"", name, data[:smb_id] ] end if !shares.empty? machine.env.ui.warn("\n" + I18n.t("vagrant_sf_smb.uac.create_warning") + "\n") sleep(UAC_PROMPT_WAIT) result = Vagrant::Util::PowerShell.execute(script_path, *shares, sudo: true) if result.exit_code != 0 share_path = result.stdout.to_s.sub("share path: ", "") raise SyncedFolderSMB::Errors::DefineShareFailed, host: share_path, stderr: result.stderr, stdout: result.stdout end end end # Generate a list of existing local smb shares # # @return [Hash] def self.existing_shares result = Vagrant::Util::PowerShell.execute_cmd("Get-SmbShare|Format-List") if result.nil? raise SyncedFolderSMB::Errors::SMBListFailed end shares = {} name = nil result.lines.each do |line| key, value = line.split(":", 2).map(&:strip) if key == "Name" name = value shares[name] = {} end next if name.nil? || key.to_s.empty? shares[name][key] = value end @@logger.debug("local share listing: #{shares}") shares end # Generates a unique identifier for the given machine # based on the name, provider name, and working directory # of the environment. # # @param [Vagrant::Machine] machine # @return [String] def self.machine_id(machine) @@logger.debug("generating machine ID name=#{machine.name} cwd=#{machine.env.cwd}") Digest::MD5.hexdigest("#{machine.name}-#{machine.provider_name}-#{machine.env.cwd}") end end end end end vagrant-2.0.2/plugins/hosts/windows/host.rb000066400000000000000000000003471323370221500207740ustar00rootroot00000000000000require "vagrant" require 'vagrant/util/platform' module VagrantPlugins module HostWindows class Host < Vagrant.plugin("2", :host) def detect?(env) Vagrant::Util::Platform.windows? end end end end vagrant-2.0.2/plugins/hosts/windows/plugin.rb000066400000000000000000000025751323370221500213220ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostWindows class Plugin < Vagrant.plugin("2") name "Windows host" description "Windows host support." host("windows") do require_relative "host" Host end host_capability("windows", "provider_install_virtualbox") do require_relative "cap/provider_install_virtualbox" Cap::ProviderInstallVirtualBox end host_capability("windows", "nfs_installed") do require_relative "cap/nfs" Cap::NFS end host_capability("windows", "rdp_client") do require_relative "cap/rdp" Cap::RDP end host_capability("windows", "ps_client") do require_relative "cap/ps" Cap::PS end host_capability("windows", "smb_installed") do require_relative "cap/smb" Cap::SMB end host_capability("windows", "smb_prepare") do require_relative "cap/smb" Cap::SMB end host_capability("windows", "smb_cleanup") do require_relative "cap/smb" Cap::SMB end host_capability("windows", "smb_cleanup") do require_relative "cap/smb" Cap::SMB end host_capability("windows", "configured_ip_addresses") do require_relative "cap/configured_ip_addresses" Cap::ConfiguredIPAddresses end end end end vagrant-2.0.2/plugins/hosts/windows/scripts/000077500000000000000000000000001323370221500211555ustar00rootroot00000000000000vagrant-2.0.2/plugins/hosts/windows/scripts/check_credentials.ps1000066400000000000000000000007431323370221500252400ustar00rootroot00000000000000Param( [Parameter(Mandatory=$true)] [string]$username, [Parameter(Mandatory=$true)] [string]$password ) Add-Type -AssemblyName System.DirectoryServices.AccountManagement $DSContext = New-Object System.DirectoryServices.AccountManagement.PrincipalContext( [System.DirectoryServices.AccountManagement.ContextType]::Machine, $env:COMPUTERNAME ) if ( $DSContext.ValidateCredentials( $username, $password ) ) { exit 0 } else { exit 1 }vagrant-2.0.2/plugins/hosts/windows/scripts/host_info.ps1000066400000000000000000000011511323370221500235700ustar00rootroot00000000000000$ErrorAction = "Stop" # Find all of the NICsq $nics = [System.Net.NetworkInformation.NetworkInterface]::GetAllNetworkInterfaces() # Save the IP addresses somewhere $nic_ip_addresses = @() foreach ($nic in $nics) { $nic_ip_addresses += $nic.GetIPProperties().UnicastAddresses | Where-Object { ($_.Address.IPAddressToString -ne "127.0.0.1") -and ($_.Address.IPAddressToString -ne "::1") } | Select -ExpandProperty Address } $nic_ip_addresses = $nic_ip_addresses | Sort-Object $_.AddressFamily $result = @{ ip_addresses = $nic_ip_addresses.IPAddressToString } Write-Output $(ConvertTo-Json $result) vagrant-2.0.2/plugins/hosts/windows/scripts/install_virtualbox.ps1000066400000000000000000000017741323370221500255400ustar00rootroot00000000000000Param( [Parameter(Mandatory=$True)] [string]$path ) # Stop on first error $ErrorActionPreference = "Stop" # Make the path complete $path = Resolve-Path $path # Determine if this is a 64-bit or 32-bit CPU $architecture="x86" if ((Get-WmiObject -Class Win32_OperatingSystem).OSArchitecture -eq "64-bit") { $architecture = "amd64" } # Extract the contents of the installer Start-Process -FilePath $path ` -ArgumentList ('--extract','--silent','--path','.') ` -Wait ` -NoNewWindow # Find the installer $matches = Get-ChildItem | Where-Object { $_.Name -match "VirtualBox-.*_$($architecture).msi" } if ($matches.Count -ne 1) { Write-Host "Multiple matches for VirtualBox MSI found: $($matches.Count)" exit 1 } $installerPath = Resolve-Path $matches[0] # Run the installer Start-Process -FilePath "$($env:systemroot)\System32\msiexec.exe" ` -ArgumentList "/i `"$installerPath`" /qn /norestart /l*v `"$($pwd)\install.log`"" ` -Verb RunAs ` -Wait ` -WorkingDirectory "$pwd" vagrant-2.0.2/plugins/hosts/windows/scripts/set_share.ps1000066400000000000000000000020331323370221500235550ustar00rootroot00000000000000# The names of the user are language dependent! $objSID = New-Object System.Security.Principal.SecurityIdentifier("S-1-1-0") $objUser = $objSID.Translate([System.Security.Principal.NTAccount]) $grant = "$objUser,Full" for ($i=0; $i -le $args.length; $i = $i + 3) { $path = $args[$i] $share_name = $args[$i+1] $share_id = $args[$i+2] if ($path -eq $null) { Write-Warning "empty path argument encountered - complete" exit 0 } if ($share_name -eq $null) { Write-Output "share path: ${path}" Write-Error "error - no share name provided" exit 1 } if ($share_id -eq $null) { Write-Output "share path: ${path}" Write-Error "error - no share ID provided" exit 1 } $result = net share $share_id=$path /unlimited /GRANT:$grant /REMARK:"${share_name}" if ($LastExitCode -ne 0) { $host.ui.WriteLine("share path: ${path}") $host.ui.WriteErrorLine("error ${result}") exit 1 } } exit 0 vagrant-2.0.2/plugins/hosts/windows/scripts/unset_share.ps1000066400000000000000000000004121323370221500241170ustar00rootroot00000000000000ForEach ($share_name in $args) { $result = net share $share_name /DELETE if ($LastExitCode -ne 0) { Write-Output "share name: ${share_name}" Write-Error "error - ${result}" exit 1 } } Write-Output "share removal completed" exit 0 vagrant-2.0.2/plugins/kernel_v1/000077500000000000000000000000001323370221500165225ustar00rootroot00000000000000vagrant-2.0.2/plugins/kernel_v1/config/000077500000000000000000000000001323370221500177675ustar00rootroot00000000000000vagrant-2.0.2/plugins/kernel_v1/config/nfs.rb000066400000000000000000000006671323370221500211130ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Kernel_V1 class NFSConfig < Vagrant.plugin("1", :config) attr_accessor :map_uid attr_accessor :map_gid def initialize @map_uid = UNSET_VALUE @map_gid = UNSET_VALUE end def upgrade(new) new.nfs.map_uid = @map_uid if @map_uid != UNSET_VALUE new.nfs.map_gid = @map_gid if @map_gid != UNSET_VALUE end end end end vagrant-2.0.2/plugins/kernel_v1/config/package.rb000066400000000000000000000004661323370221500217150ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Kernel_V1 class PackageConfig < Vagrant.plugin("1", :config) attr_accessor :name def initialize @name = UNSET_VALUE end def upgrade(new) new.package.name = @name if @name != UNSET_VALUE end end end end vagrant-2.0.2/plugins/kernel_v1/config/ssh.rb000066400000000000000000000032721323370221500211150ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Kernel_V1 class SSHConfig < Vagrant.plugin("1", :config) attr_accessor :username attr_accessor :password attr_accessor :host attr_accessor :port attr_accessor :guest_port attr_accessor :max_tries attr_accessor :timeout attr_accessor :private_key_path attr_accessor :forward_agent attr_accessor :forward_x11 attr_accessor :forward_env attr_accessor :shell def initialize @username = UNSET_VALUE @password = UNSET_VALUE @host = UNSET_VALUE @port = UNSET_VALUE @guest_port = UNSET_VALUE @max_tries = UNSET_VALUE @timeout = UNSET_VALUE @private_key_path = UNSET_VALUE @forward_agent = UNSET_VALUE @forward_x11 = UNSET_VALUE @forward_env = UNSET_VALUE @shell = UNSET_VALUE end def upgrade(new) new.ssh.username = @username if @username != UNSET_VALUE new.ssh.host = @host if @host != UNSET_VALUE new.ssh.port = @port if @port != UNSET_VALUE new.ssh.guest_port = @guest_port if @guest_port != UNSET_VALUE new.ssh.private_key_path = @private_key_path if @private_key_path != UNSET_VALUE new.ssh.forward_agent = @forward_agent if @forward_agent != UNSET_VALUE new.ssh.forward_x11 = @forward_x11 if @forward_x11 != UNSET_VALUE new.ssh.forward_env = @forward_env if @forward_env != UNSET_VALUE new.ssh.shell = @shell if @shell != UNSET_VALUE end end end end vagrant-2.0.2/plugins/kernel_v1/config/vagrant.rb000066400000000000000000000012351323370221500217570ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Kernel_V1 class VagrantConfig < Vagrant.plugin("1", :config) attr_accessor :dotfile_name attr_accessor :host def initialize @dotfile_name = UNSET_VALUE @host = UNSET_VALUE end def finalize! @dotfile_name = nil if @dotfile_name == UNSET_VALUE @host = nil if @host == UNSET_VALUE end def upgrade(new) new.vagrant.host = @host if @host.nil? warnings = [] if @dotfile_name warnings << "`config.vm.dotfile_name` has no effect anymore." end [warnings, []] end end end end vagrant-2.0.2/plugins/kernel_v1/config/vm.rb000066400000000000000000000136351323370221500207460ustar00rootroot00000000000000module VagrantPlugins module Kernel_V1 # This is the Version 1.0.x Vagrant VM configuration. This is # _outdated_ and exists purely to be upgraded over to the new V2 # format. class VMConfig < Vagrant.plugin("1", :config) DEFAULT_VM_NAME = :default attr_accessor :name attr_accessor :auto_port_range attr_accessor :base_mac attr_accessor :boot_mode attr_accessor :box attr_accessor :box_url attr_accessor :guest attr_accessor :host_name attr_reader :customizations attr_reader :networks attr_reader :provisioners attr_reader :shared_folders def initialize @shared_folders = {} @networks = [] @provisioners = [] @customizations = [] @define_calls = [] end def forward_port(guestport, hostport, options=nil) options ||= {} # Build up the network options for V2 network_options = {} network_options[:virtualbox__adapter] = options[:adapter] network_options[:virtualbox__protocol] = options[:protocol] # Just append the forwarded port to the networks @networks << [:forwarded_port, [guestport, hostport, network_options]] end def share_folder(name, guestpath, hostpath, opts=nil) @shared_folders[name] = { guestpath: guestpath.to_s, hostpath: hostpath.to_s, create: false, owner: nil, group: nil, nfs: false, transient: false, extra: nil }.merge(opts || {}) end def network(type, *args) # Convert to symbol so we can allow most anything... type = type.to_sym if type if type == :hostonly @networks << [:private_network, args] elsif type == :bridged @networks << [:public_network, args] else @networks << [:unknown, type] end end def provision(name, options=nil, &block) @provisioners << [name, options, block] end # This argument is nil only because the old style was deprecated and # we didn't want to break Vagrantfiles. This was never removed and # since we've moved onto V2 configuration, we might as well keep this # around forever. def customize(command=nil) @customizations << command if command end def define(name, options=nil, &block) # Force the V1 config on these calls options ||= {} options[:config_version] = "1" @define_calls << [name, options, block] end def finalize! # If we haven't defined a single VM, then we need to define a # default VM which just inherits the rest of the configuration. define(DEFAULT_VM_NAME) if defined_vm_keys.empty? end # Upgrade to a V2 configuration def upgrade(new) warnings = [] new.vm.base_mac = self.base_mac if self.base_mac new.vm.box = self.box if self.box new.vm.box_url = self.box_url if self.box_url new.vm.guest = self.guest if self.guest new.vm.hostname = self.host_name if self.host_name new.vm.usable_port_range = self.auto_port_range if self.auto_port_range if self.boot_mode new.vm.provider :virtualbox do |vb| # Enable the GUI if the boot mode is GUI. vb.gui = (self.boot_mode.to_s == "gui") end end # If we have VM customizations, then we enable them on the # VirtualBox provider on the new VM. if !self.customizations.empty? warnings << "`config.vm.customize` calls are VirtualBox-specific. If you're\n" + "using any other provider, you'll have to use config.vm.provider in a\n" + "v2 configuration block." new.vm.provider :virtualbox do |vb| self.customizations.each do |customization| vb.customize(customization) end end end # Re-define all networks. self.networks.each do |type, args| if type == :unknown warnings << "Unknown network type '#{args}' will be ignored." next end options = {} options = args.pop.dup if args.last.is_a?(Hash) # Determine the extra options we need to set for each type if type == :forwarded_port options[:guest] = args[0] options[:host] = args[1] elsif type == :private_network options[:ip] = args[0] end new.vm.network(type, options) end # Provisioners self.provisioners.each do |name, options, block| options ||= {} new.vm.provision(name, **options, &block) end # Shared folders self.shared_folders.each do |name, sf| options = sf.dup options[:id] = name guestpath = options.delete(:guestpath) hostpath = options.delete(:hostpath) # This was the name of the old default /vagrant shared folder. # We warn the use that this changed, but also silently change # it to try to make things work properly. if options[:id] == "v-root" warnings << "The 'v-root' shared folders have been renamed to 'vagrant-root'.\n" + "Assuming you meant 'vagrant-root'..." options[:id] = "vagrant-root" end new.vm.synced_folder(hostpath, guestpath, options) end # Defined sub-VMs @define_calls.each do |name, options, block| new.vm.define(name, options, &block) end # If name is used, warn that it has no effect anymore if @name warnings << "`config.vm.name` has no effect anymore. Names are derived\n" + "directly from any `config.vm.define` calls." end [warnings, []] end end end end vagrant-2.0.2/plugins/kernel_v1/plugin.rb000066400000000000000000000023751323370221500203540ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Kernel_V1 # This is the "kernel" of Vagrant and contains the configuration classes # that make up the core of Vagrant. class Plugin < Vagrant.plugin("1") name "kernel" description <<-DESC The kernel of Vagrant. This plugin contains required items for even basic functionality of Vagrant version 1. DESC # Core configuration keys provided by the kernel. Note that all # the kernel configuration classes are marked as _upgrade safe_ (the # true 2nd param). This means that these can be loaded in ANY version # of the core of Vagrant. config("ssh", true) do require File.expand_path("../config/ssh", __FILE__) SSHConfig end config("nfs", true) do require File.expand_path("../config/nfs", __FILE__) NFSConfig end config("package", true) do require File.expand_path("../config/package", __FILE__) PackageConfig end config("vagrant", true) do require File.expand_path("../config/vagrant", __FILE__) VagrantConfig end config("vm", true) do require File.expand_path("../config/vm", __FILE__) VMConfig end end end end vagrant-2.0.2/plugins/kernel_v2/000077500000000000000000000000001323370221500165235ustar00rootroot00000000000000vagrant-2.0.2/plugins/kernel_v2/config/000077500000000000000000000000001323370221500177705ustar00rootroot00000000000000vagrant-2.0.2/plugins/kernel_v2/config/package.rb000066400000000000000000000005221323370221500217070ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Kernel_V2 class PackageConfig < Vagrant.plugin("2", :config) attr_accessor :name def initialize @name = UNSET_VALUE end def finalize! @name = nil if @name == UNSET_VALUE end def to_s "Package" end end end end vagrant-2.0.2/plugins/kernel_v2/config/push.rb000066400000000000000000000077131323370221500213040ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Kernel_V2 class PushConfig < Vagrant.plugin("2", :config) VALID_OPTIONS = [:strategy].freeze attr_accessor :name def initialize @logger = Log4r::Logger.new("vagrant::config::push") # Internal state @__defined_pushes = {} @__compiled_pushes = {} @__finalized = false end def finalize! @logger.debug("finalizing") # Compile all the provider configurations @__defined_pushes.each do |name, tuples| # Capture the strategy so we can use it later. This will be used in # the block iteration for merging/overwriting strategy = name strategy = tuples[0][0] if tuples[0] # Find the configuration class for this push config_class = Vagrant.plugin("2").manager.push_configs[strategy] config_class ||= Vagrant::Config::V2::DummyConfig # Load it up config = config_class.new begin tuples.each do |s, b| # Update the strategy if it has changed, reseting the current # config object. if s != strategy @logger.warn("duplicate strategy defined, overwriting config") strategy = s config = config_class.new end # If we don't have any blocks, then ignore it next if b.nil? new_config = config_class.new b.call(new_config, Vagrant::Config::V2::DummyConfig.new) config = config.merge(new_config) end rescue Exception => e raise Vagrant::Errors::VagrantfileLoadError, path: "", message: e.message end config.finalize! # Store it for retrieval later @__compiled_pushes[name] = [strategy, config] end @__finalized = true end # Define a new push in the Vagrantfile with the given name. # # @example # vm.push.define "ftp" # # @example # vm.push.define "ftp" do |s| # s.host = "..." # end # # @example # vm.push.define "production", strategy: "docker" do |s| # # ... # end # # @param [#to_sym] name The name of the this strategy. By default, this # is also the name of the strategy, but the `:strategy` key can be given # to customize this behavior # @param [Hash] options The list of options # def define(name, **options, &block) name = name.to_sym strategy = options[:strategy] || name @__defined_pushes[name] ||= [] @__defined_pushes[name] << [strategy.to_sym, block] end # The String representation of this Push. # # @return [String] def to_s "Push" end # Custom merge method def merge(other) super.tap do |result| other_pushes = other.instance_variable_get(:@__defined_pushes) new_pushes = @__defined_pushes.dup other_pushes.each do |key, tuples| new_pushes[key] ||= [] new_pushes[key] += tuples end result.instance_variable_set(:@__defined_pushes, new_pushes) end end # Validate all pushes def validate(machine) errors = { "push" => _detected_errors } __compiled_pushes.each do |_, push| config = push[1] push_errors = config.validate(machine) if push_errors errors = Vagrant::Config::V2::Util.merge_errors(errors, push_errors) end end errors end # This returns the list of compiled pushes as a hash by name. # # @return [Hash>] def __compiled_pushes raise "Must finalize first!" if !@__finalized @__compiled_pushes.dup end end end end vagrant-2.0.2/plugins/kernel_v2/config/ssh.rb000066400000000000000000000052121323370221500211120ustar00rootroot00000000000000require "vagrant" require_relative "ssh_connect" module VagrantPlugins module Kernel_V2 class SSHConfig < SSHConnectConfig attr_accessor :forward_agent attr_accessor :forward_x11 attr_accessor :forward_env attr_accessor :guest_port attr_accessor :keep_alive attr_accessor :shell attr_accessor :proxy_command attr_accessor :ssh_command attr_accessor :pty attr_accessor :sudo_command attr_accessor :export_command_template attr_reader :default def initialize super @forward_agent = UNSET_VALUE @forward_x11 = UNSET_VALUE @forward_env = UNSET_VALUE @guest_port = UNSET_VALUE @keep_alive = UNSET_VALUE @proxy_command = UNSET_VALUE @ssh_command = UNSET_VALUE @pty = UNSET_VALUE @shell = UNSET_VALUE @sudo_command = UNSET_VALUE @export_command_template = UNSET_VALUE @default = SSHConnectConfig.new end def merge(other) super.tap do |result| merged_defaults = @default.merge(other.default) result.instance_variable_set(:@default, merged_defaults) end end def finalize! super @forward_agent = false if @forward_agent == UNSET_VALUE @forward_x11 = false if @forward_x11 == UNSET_VALUE @forward_env = false if @forward_env == UNSET_VALUE @guest_port = 22 if @guest_port == UNSET_VALUE @keep_alive = true if @keep_alive == UNSET_VALUE @proxy_command = nil if @proxy_command == UNSET_VALUE @ssh_command = nil if @ssh_command == UNSET_VALUE @pty = false if @pty == UNSET_VALUE @shell = "bash -l" if @shell == UNSET_VALUE if @export_command_template == UNSET_VALUE @export_command_template = 'export %ENV_KEY%="%ENV_VALUE%"' end if @sudo_command == UNSET_VALUE @sudo_command = "sudo -E -H %c" end @default.username = "vagrant" if @default.username == UNSET_VALUE @default.port = @guest_port if @default.port == UNSET_VALUE @default.finalize! end def to_s "SSH" end def validate(machine) errors = super # Return the errors result = { to_s => errors } # Figure out the errors for the defaults default_errors = @default.validate(machine) result["SSH Defaults"] = default_errors if !default_errors.empty? result end end end end vagrant-2.0.2/plugins/kernel_v2/config/ssh_connect.rb000066400000000000000000000053421323370221500226270ustar00rootroot00000000000000module VagrantPlugins module Kernel_V2 class SSHConnectConfig < Vagrant.plugin("2", :config) attr_accessor :host attr_accessor :port attr_accessor :private_key_path attr_accessor :username attr_accessor :password attr_accessor :insert_key attr_accessor :keys_only attr_accessor :paranoid attr_accessor :verify_host_key attr_accessor :compression attr_accessor :dsa_authentication attr_accessor :extra_args def initialize @host = UNSET_VALUE @port = UNSET_VALUE @private_key_path = UNSET_VALUE @username = UNSET_VALUE @password = UNSET_VALUE @insert_key = UNSET_VALUE @keys_only = UNSET_VALUE @paranoid = UNSET_VALUE @verify_host_key = UNSET_VALUE @compression = UNSET_VALUE @dsa_authentication = UNSET_VALUE @extra_args = UNSET_VALUE end def finalize! @host = nil if @host == UNSET_VALUE @port = nil if @port == UNSET_VALUE @private_key_path = nil if @private_key_path == UNSET_VALUE @username = nil if @username == UNSET_VALUE @password = nil if @password == UNSET_VALUE @insert_key = true if @insert_key == UNSET_VALUE @keys_only = true if @keys_only == UNSET_VALUE @paranoid = false if @paranoid == UNSET_VALUE @verify_host_key = false if @verify_host_key == UNSET_VALUE @compression = true if @compression == UNSET_VALUE @dsa_authentication = true if @dsa_authentication == UNSET_VALUE @extra_args = nil if @extra_args == UNSET_VALUE if @private_key_path && !@private_key_path.is_a?(Array) @private_key_path = [@private_key_path] end if @paranoid @verify_host_key = @paranoid end end # NOTE: This is _not_ a valid config validation method, since it # returns an _array_ of strings rather than a Hash. This is meant to # be used with a subclass that handles this. # # @return [Array] def validate(machine) errors = _detected_errors if @private_key_path @private_key_path.each do |raw_path| path = File.expand_path(raw_path, machine.env.root_path) if !File.file?(path) errors << I18n.t( "vagrant.config.ssh.private_key_missing", path: raw_path) end end end if @paranoid machine.env.ui.warn(I18n.t("vagrant.config.ssh.paranoid_deprecated")) end errors end end end end vagrant-2.0.2/plugins/kernel_v2/config/vagrant.rb000066400000000000000000000017061323370221500217630ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Kernel_V2 class VagrantConfig < Vagrant.plugin("2", :config) attr_accessor :host attr_accessor :sensitive def initialize @host = UNSET_VALUE @sensitive = UNSET_VALUE end def finalize! @host = :detect if @host == UNSET_VALUE @host = @host.to_sym if @host @sensitive = nil if @sensitive == UNSET_VALUE if @sensitive.is_a?(Array) || @sensitive.is_a?(String) Array(@sensitive).each do |value| Vagrant::Util::CredentialScrubber.sensitive(value.to_s) end end end def validate(machine) errors = _detected_errors if @sensitive && (!@sensitive.is_a?(Array) && !@sensitive.is_a?(String)) errors << I18n.t("vagrant.config.root.sensitive_bad_type") end {"vagrant" => errors} end def to_s "Vagrant" end end end end vagrant-2.0.2/plugins/kernel_v2/config/vm.rb000066400000000000000000000656451323370221500207570ustar00rootroot00000000000000require "pathname" require "securerandom" require "set" require "vagrant" require "vagrant/config/v2/util" require "vagrant/util/platform" require "vagrant/util/presence" require File.expand_path("../vm_provisioner", __FILE__) require File.expand_path("../vm_subvm", __FILE__) module VagrantPlugins module Kernel_V2 class VMConfig < Vagrant.plugin("2", :config) include Vagrant::Util::Presence DEFAULT_VM_NAME = :default attr_accessor :allowed_synced_folder_types attr_accessor :base_mac attr_accessor :boot_timeout attr_accessor :box attr_accessor :box_check_update attr_accessor :box_url attr_accessor :box_server_url attr_accessor :box_version attr_accessor :box_download_ca_cert attr_accessor :box_download_ca_path attr_accessor :box_download_checksum attr_accessor :box_download_checksum_type attr_accessor :box_download_client_cert attr_accessor :box_download_insecure attr_accessor :box_download_location_trusted attr_accessor :communicator attr_accessor :graceful_halt_timeout attr_accessor :guest attr_accessor :hostname attr_accessor :post_up_message attr_accessor :usable_port_range attr_reader :provisioners # This is an experimental feature that isn't public yet. attr_accessor :clone def initialize @logger = Log4r::Logger.new("vagrant::config::vm") @allowed_synced_folder_types = UNSET_VALUE @base_mac = UNSET_VALUE @boot_timeout = UNSET_VALUE @box = UNSET_VALUE @box_check_update = UNSET_VALUE @box_download_ca_cert = UNSET_VALUE @box_download_ca_path = UNSET_VALUE @box_download_checksum = UNSET_VALUE @box_download_checksum_type = UNSET_VALUE @box_download_client_cert = UNSET_VALUE @box_download_insecure = UNSET_VALUE @box_download_location_trusted = UNSET_VALUE @box_url = UNSET_VALUE @box_version = UNSET_VALUE @clone = UNSET_VALUE @communicator = UNSET_VALUE @graceful_halt_timeout = UNSET_VALUE @guest = UNSET_VALUE @hostname = UNSET_VALUE @post_up_message = UNSET_VALUE @provisioners = [] @usable_port_range = UNSET_VALUE # Internal state @__compiled_provider_configs = {} @__defined_vm_keys = [] @__defined_vms = {} @__finalized = false @__networks = {} @__providers = {} @__provider_order = [] @__provider_overrides = {} @__synced_folders = {} end # This was from V1, but we just kept it here as an alias for hostname # because too many people mess this up. def host_name=(value) @hostname = value end # Custom merge method since some keys here are merged differently. def merge(other) super.tap do |result| other_networks = other.instance_variable_get(:@__networks) result.instance_variable_set(:@__networks, @__networks.merge(other_networks)) # Merge defined VMs by first merging the defined VM keys, # preserving the order in which they were defined. other_defined_vm_keys = other.instance_variable_get(:@__defined_vm_keys) other_defined_vm_keys -= @__defined_vm_keys new_defined_vm_keys = @__defined_vm_keys + other_defined_vm_keys # Merge the actual defined VMs. other_defined_vms = other.instance_variable_get(:@__defined_vms) new_defined_vms = {} @__defined_vms.each do |key, subvm| new_defined_vms[key] = subvm.clone end other_defined_vms.each do |key, subvm| if !new_defined_vms.key?(key) new_defined_vms[key] = subvm.clone else new_defined_vms[key].config_procs.concat(subvm.config_procs) new_defined_vms[key].options.merge!(subvm.options) end end # Merge the providers by prepending any configuration blocks we # have for providers onto the new configuration. other_providers = other.instance_variable_get(:@__providers) new_providers = @__providers.dup other_providers.each do |key, blocks| new_providers[key] ||= [] new_providers[key] += blocks end # Merge the provider ordering. Anything defined in our CURRENT # scope is before anything else. other_order = other.instance_variable_get(:@__provider_order) new_order = @__provider_order.dup new_order = (new_order + other_order).uniq # Merge the provider overrides by appending them... other_overrides = other.instance_variable_get(:@__provider_overrides) new_overrides = @__provider_overrides.dup other_overrides.each do |key, blocks| new_overrides[key] ||= [] new_overrides[key] += blocks end # Merge provisioners. First we deal with overrides and making # sure the ordering is good there. Then we merge them. new_provs = [] other_provs = other.provisioners.dup @provisioners.each do |p| other_p = other_provs.find { |o| p.id == o.id } if other_p # There is an override. Take it. other_p.config = p.config.merge(other_p.config) other_p.run ||= p.run next if !other_p.preserve_order # We're preserving order, delete from other p = other_p other_provs.delete(other_p) end # There is an override, merge it into the new_provs << p.dup end other_provs.each do |p| new_provs << p.dup end result.instance_variable_set(:@provisioners, new_provs) # Merge synced folders. other_folders = other.instance_variable_get(:@__synced_folders) new_folders = {} @__synced_folders.each do |key, value| new_folders[key] = value.dup end other_folders.each do |id, options| new_folders[id] ||= {} new_folders[id].merge!(options) end result.instance_variable_set(:@__defined_vm_keys, new_defined_vm_keys) result.instance_variable_set(:@__defined_vms, new_defined_vms) result.instance_variable_set(:@__providers, new_providers) result.instance_variable_set(:@__provider_order, new_order) result.instance_variable_set(:@__provider_overrides, new_overrides) result.instance_variable_set(:@__synced_folders, new_folders) end end # Defines a synced folder pair. This pair of folders will be synced # to/from the machine. Note that if the machine you're using doesn't # support multi-directional syncing (perhaps an rsync backed synced # folder) then the host is always synced to the guest but guest data # may not be synced back to the host. # # @param [String] hostpath Path to the host folder to share. If this # is a relative path, it is relative to the location of the # Vagrantfile. # @param [String] guestpath Path on the guest to mount the shared # folder. # @param [Hash] options Additional options. def synced_folder(hostpath, guestpath, options=nil) if Vagrant::Util::Platform.windows? # On Windows, Ruby just uses normal '/' for path seps, so # just replace normal Windows style seps with Unix ones. hostpath = hostpath.to_s.gsub("\\", "/") end if guestpath.is_a?(Hash) options = guestpath guestpath = nil end options ||= {} if options.has_key?(:name) synced_folder_name = options.delete(:name) else synced_folder_name = guestpath end options[:guestpath] = guestpath.to_s.gsub(/\/$/, '') options[:hostpath] = hostpath options[:disabled] = false if !options.key?(:disabled) options = (@__synced_folders[options[:guestpath]] || {}). merge(options.dup) # Make sure the type is a symbol options[:type] = options[:type].to_sym if options[:type] @__synced_folders[synced_folder_name] = options end # Define a way to access the machine via a network. This exposes a # high-level abstraction for networking that may not directly map # 1-to-1 for every provider. For example, AWS has no equivalent to # "port forwarding." But most providers will attempt to implement this # in a way that behaves similarly. # # `type` can be one of: # # * `:forwarded_port` - A port that is accessible via localhost # that forwards into the machine. # * `:private_network` - The machine gets an IP that is not directly # publicly accessible, but ideally accessible from this machine. # * `:public_network` - The machine gets an IP on a shared network. # # @param [Symbol] type Type of network # @param [Hash] options Options for the network. def network(type, **options) options = options.dup options[:protocol] ||= "tcp" # Convert to symbol to allow strings type = type.to_sym if !options[:id] default_id = nil if type == :forwarded_port # For forwarded ports, set the default ID to be the # concat of host_ip, proto and host_port. This would ensure Vagrant # caters for port forwarding in an IP aliased environment where # different host IP addresses are to be listened on the same port. default_id = "#{options[:host_ip]}#{options[:protocol]}#{options[:host]}" end options[:id] = default_id || SecureRandom.uuid end # Scope the ID by type so that different types can share IDs id = options[:id] id = "#{type}-#{id}" # Merge in the previous settings if we have them. if @__networks.key?(id) options = @__networks[id][1].merge(options) end # Merge in the latest settings and set the internal state @__networks[id] = [type.to_sym, options] end # Configures a provider for this VM. # # @param [Symbol] name The name of the provider. def provider(name, &block) name = name.to_sym @__providers[name] ||= [] @__provider_overrides[name] ||= [] # Add the provider to the ordering list @__provider_order << name if block_given? @__providers[name] << block if block_given? # If this block takes two arguments, then we curry it and store # the configuration override for use later. if block.arity == 2 @__provider_overrides[name] << block.curry[Vagrant::Config::V2::DummyConfig.new] end end end def provision(name, **options, &block) type = name if options.key?(:type) type = options.delete(:type) else name = nil end if options.key?(:id) puts "Setting `id` on a provisioner is deprecated. Please use the" puts "new syntax of `config.vm.provision \"name\", type: \"type\"" puts "where \"name\" is the replacement for `id`. This will be" puts "fully removed in Vagrant 1.8." name = id end prov = nil if name name = name.to_sym prov = @provisioners.find { |p| p.name == name } end if !prov prov = VagrantConfigProvisioner.new(name, type.to_sym) @provisioners << prov end prov.preserve_order = !!options.delete(:preserve_order) if \ options.key?(:preserve_order) prov.run = options.delete(:run) if options.key?(:run) prov.add_config(options, &block) nil end def defined_vms @__defined_vms end # This returns the keys of the sub-vms in the order they were # defined. def defined_vm_keys @__defined_vm_keys end def define(name, options=nil, &block) name = name.to_sym options ||= {} options = options.dup options[:config_version] ||= "2" # Add the name to the array of VM keys. This array is used to # preserve the order in which VMs are defined. @__defined_vm_keys << name if !@__defined_vm_keys.include?(name) # Add the SubVM to the hash of defined VMs if !@__defined_vms[name] @__defined_vms[name] = VagrantConfigSubVM.new end @__defined_vms[name].options.merge!(options) @__defined_vms[name].config_procs << [options[:config_version], block] if block end #------------------------------------------------------------------- # Internal methods, don't call these. #------------------------------------------------------------------- def finalize! # Defaults @allowed_synced_folder_types = nil if @allowed_synced_folder_types == UNSET_VALUE @base_mac = nil if @base_mac == UNSET_VALUE @boot_timeout = 300 if @boot_timeout == UNSET_VALUE @box = nil if @box == UNSET_VALUE if @box_check_update == UNSET_VALUE @box_check_update = !present?(ENV["VAGRANT_BOX_UPDATE_CHECK_DISABLE"]) end @box_download_ca_cert = nil if @box_download_ca_cert == UNSET_VALUE @box_download_ca_path = nil if @box_download_ca_path == UNSET_VALUE @box_download_checksum = nil if @box_download_checksum == UNSET_VALUE @box_download_checksum_type = nil if @box_download_checksum_type == UNSET_VALUE @box_download_client_cert = nil if @box_download_client_cert == UNSET_VALUE @box_download_insecure = false if @box_download_insecure == UNSET_VALUE @box_download_location_trusted = false if @box_download_location_trusted == UNSET_VALUE @box_url = nil if @box_url == UNSET_VALUE @box_version = nil if @box_version == UNSET_VALUE @clone = nil if @clone == UNSET_VALUE @communicator = nil if @communicator == UNSET_VALUE @graceful_halt_timeout = 60 if @graceful_halt_timeout == UNSET_VALUE @guest = nil if @guest == UNSET_VALUE @hostname = nil if @hostname == UNSET_VALUE @hostname = @hostname.to_s if @hostname @post_up_message = "" if @post_up_message == UNSET_VALUE if @usable_port_range == UNSET_VALUE @usable_port_range = (2200..2250) end if @allowed_synced_folder_types @allowed_synced_folder_types = Array(@allowed_synced_folder_types).map(&:to_sym) end # Make sure that the download checksum is a string and that # the type is a symbol @box_download_checksum = "" if !@box_download_checksum if @box_download_checksum_type @box_download_checksum_type = @box_download_checksum_type.to_sym end # Make sure the box URL is an array if it is set @box_url = Array(@box_url) if @box_url # Set the communicator properly @communicator = @communicator.to_sym if @communicator # Set the guest properly @guest = @guest.to_sym if @guest # If we haven't defined a single VM, then we need to define a # default VM which just inherits the rest of the configuration. define(DEFAULT_VM_NAME) if defined_vm_keys.empty? # Make sure the SSH forwarding is added if it doesn't exist if @communicator == :winrm if !@__networks["forwarded_port-winrm"] network :forwarded_port, guest: 5985, host: 55985, host_ip: "127.0.0.1", id: "winrm", auto_correct: true end if !@__networks["forwarded_port-winrm-ssl"] network :forwarded_port, guest: 5986, host: 55986, host_ip: "127.0.0.1", id: "winrm-ssl", auto_correct: true end end # forward SSH ports regardless of communicator if !@__networks["forwarded_port-ssh"] network :forwarded_port, guest: 22, host: 2222, host_ip: "127.0.0.1", id: "ssh", auto_correct: true end # Clean up some network configurations @__networks.values.each do |type, opts| if type == :forwarded_port opts[:guest] = opts[:guest].to_i if opts[:guest] opts[:host] = opts[:host].to_i if opts[:host] end end # Compile all the provider configurations @__providers.each do |name, blocks| # If we don't have any configuration blocks, then ignore it next if blocks.empty? # Find the configuration class for this provider config_class = Vagrant.plugin("2").manager.provider_configs[name] config_class ||= Vagrant::Config::V2::DummyConfig # Load it up config = config_class.new begin blocks.each do |b| new_config = config_class.new b.call(new_config, Vagrant::Config::V2::DummyConfig.new) config = config.merge(new_config) end rescue Exception => e @logger.error("Vagrantfile load error: #{e.message}") @logger.error(e.inspect) @logger.error(e.message) @logger.error(e.backtrace.join("\n")) line = "(unknown)" if e.backtrace && e.backtrace[0] line = e.backtrace[0].split(":")[1] end raise Vagrant::Errors::VagrantfileLoadError, path: "", line: line, exception_class: e.class, message: e.message end config.finalize! # Store it for retrieval later @__compiled_provider_configs[name] = config end # Finalize all the provisioners @provisioners.each do |p| p.config.finalize! if !p.invalid? p.run = p.run.to_sym if p.run end current_dir_shared = false @__synced_folders.each do |id, options| if options[:nfs] options[:type] = :nfs end # Ignore NFS on Windows if options[:type] == :nfs && Vagrant::Util::Platform.windows? options.delete(:type) end if options[:hostpath] == '.' current_dir_shared = true end end if !current_dir_shared && !@__synced_folders["/vagrant"] synced_folder(".", "/vagrant") end # Flag that we finalized @__finalized = true end # This returns the compiled provider-specific configurationf or the # given provider. # # @param [Symbol] name Name of the provider. def get_provider_config(name) raise "Must finalize first." if !@__finalized result = @__compiled_provider_configs[name] # If no compiled configuration was found, then we try to just # use the default configuration from the plugin. if !result config_class = Vagrant.plugin("2").manager.provider_configs[name] if config_class result = config_class.new result.finalize! end end return result end # This returns a list of VM configurations that are overrides # for this provider. # # @param [Symbol] name Name of the provider # @return [Array] def get_provider_overrides(name) (@__provider_overrides[name] || []).map do |p| ["2", p] end end # This returns the list of networks configured. def networks @__networks.values end # This returns the list of synced folders def synced_folders @__synced_folders end def validate(machine) errors = _detected_errors if !box && !clone && !machine.provider_options[:box_optional] errors << I18n.t("vagrant.config.vm.box_missing") end if box && clone errors << I18n.t("vagrant.config.vm.clone_and_box") end errors << I18n.t("vagrant.config.vm.hostname_invalid_characters") if \ @hostname && @hostname !~ /^[a-z0-9][-.a-z0-9]*$/i if @box_version @box_version.to_s.split(",").each do |v| begin Gem::Requirement.new(v.strip) rescue Gem::Requirement::BadRequirementError errors << I18n.t( "vagrant.config.vm.bad_version", version: v) end end end if box_download_ca_cert path = Pathname.new(box_download_ca_cert). expand_path(machine.env.root_path) if !path.file? errors << I18n.t( "vagrant.config.vm.box_download_ca_cert_not_found", path: box_download_ca_cert) end end if box_download_ca_path path = Pathname.new(box_download_ca_path). expand_path(machine.env.root_path) if !path.directory? errors << I18n.t( "vagrant.config.vm.box_download_ca_path_not_found", path: box_download_ca_path) end end if box_download_checksum_type if box_download_checksum == "" errors << I18n.t("vagrant.config.vm.box_download_checksum_blank") end else if box_download_checksum != "" errors << I18n.t("vagrant.config.vm.box_download_checksum_notblank") end end used_guest_paths = Set.new @__synced_folders.each do |id, options| # If the shared folder is disabled then don't worry about validating it next if options[:disabled] guestpath = Pathname.new(options[:guestpath]) if options[:guestpath] hostpath = Pathname.new(options[:hostpath]).expand_path(machine.env.root_path) if guestpath.to_s != "" if guestpath.relative? && guestpath.to_s !~ /^\w+:/ errors << I18n.t("vagrant.config.vm.shared_folder_guestpath_relative", path: options[:guestpath]) else if used_guest_paths.include?(options[:guestpath]) errors << I18n.t("vagrant.config.vm.shared_folder_guestpath_duplicate", path: options[:guestpath]) end used_guest_paths.add(options[:guestpath]) end end if !hostpath.directory? && !options[:create] errors << I18n.t("vagrant.config.vm.shared_folder_hostpath_missing", path: options[:hostpath]) end if options[:type] == :nfs && !options[:nfs__quiet] if options[:owner] || options[:group] # Owner/group don't work with NFS errors << I18n.t("vagrant.config.vm.shared_folder_nfs_owner_group", path: options[:hostpath]) end end if options[:mount_options] && !options[:mount_options].is_a?(Array) errors << I18n.t("vagrant.config.vm.shared_folder_mount_options_array") end # One day remove this probably. if options[:extra] errors << "The 'extra' flag on synced folders is now 'mount_options'" end end # Validate networks has_fp_port_error = false fp_used = Set.new valid_network_types = [:forwarded_port, :private_network, :public_network] port_range=(1..65535) networks.each do |type, options| if !valid_network_types.include?(type) errors << I18n.t("vagrant.config.vm.network_type_invalid", type: type.to_s) end if type == :forwarded_port if !has_fp_port_error && (!options[:guest] || !options[:host]) errors << I18n.t("vagrant.config.vm.network_fp_requires_ports") has_fp_port_error = true end if options[:host] key = "#{options[:host_ip]}#{options[:protocol]}#{options[:host]}" if fp_used.include?(key) errors << I18n.t("vagrant.config.vm.network_fp_host_not_unique", host: options[:host].to_s, protocol: options[:protocol].to_s) end fp_used.add(key) end if !port_range.include?(options[:host]) || !port_range.include?(options[:guest]) errors << I18n.t("vagrant.config.vm.network_fp_invalid_port") end end if type == :private_network if options[:type] && options[:type].to_sym != :dhcp if !options[:ip] errors << I18n.t("vagrant.config.vm.network_ip_required") end end if options[:ip] && options[:ip].end_with?(".1") && (options[:type] || "").to_sym != :dhcp machine.ui.warn(I18n.t( "vagrant.config.vm.network_ip_ends_in_one")) end end end # We're done with VM level errors so prepare the section errors = { "vm" => errors } # Validate only the _active_ provider if machine.provider_config provider_errors = machine.provider_config.validate(machine) if provider_errors errors = Vagrant::Config::V2::Util.merge_errors(errors, provider_errors) end end # Validate provisioners @provisioners.each do |vm_provisioner| if vm_provisioner.invalid? name = vm_provisioner.name.to_s name = vm_provisioner.type.to_s if name.empty? errors["vm"] << I18n.t("vagrant.config.vm.provisioner_not_found", name: name) next end if vm_provisioner.config provisioner_errors = vm_provisioner.config.validate(machine) if provisioner_errors errors = Vagrant::Config::V2::Util.merge_errors(errors, provisioner_errors) end end end # Validate sub-VMs if there are any @__defined_vms.each do |name, _| if name =~ /[\[\]\{\}\/]/ errors["vm"] << I18n.t( "vagrant.config.vm.name_invalid", name: name) end end errors end def __providers @__provider_order end end end end vagrant-2.0.2/plugins/kernel_v2/config/vm_provisioner.rb000066400000000000000000000052221323370221500233770ustar00rootroot00000000000000require 'log4r' module VagrantPlugins module Kernel_V2 # Represents a single configured provisioner for a VM. class VagrantConfigProvisioner # Unique name for this provisioner # # @return [String] attr_reader :name # Internal unique name for this provisioner # Set to the given :name if exists, otherwise # it's set as a UUID. # # Note: This is for internal use only. # # @return [String] attr_reader :id # The type of the provisioner that should be registered # as a plugin. # # @return [Symbol] attr_reader :type # The configuration associated with the provisioner, if there is any. # # @return [Object] attr_accessor :config # When to run this provisioner. Either "once" or "always" # # @return [String] attr_accessor :run # Whether or not to preserve the order when merging this with a # parent scope. # # @return [Boolean] attr_accessor :preserve_order def initialize(name, type) @logger = Log4r::Logger.new("vagrant::config::vm::provisioner") @logger.debug("Provisioner defined: #{name}") @id = name || SecureRandom.uuid @config = nil @invalid = false @name = name @preserve_order = false @run = nil @type = type # Attempt to find the provisioner... if !Vagrant.plugin("2").manager.provisioners[type] @logger.warn("Provisioner '#{type}' not found.") @invalid = true end # Attempt to find the configuration class for this provider # if it exists and load the configuration. @config_class = Vagrant.plugin("2").manager. provisioner_configs[@type] if !@config_class @logger.info( "Provisioner config for '#{@type}' not found. Ignoring config.") @config_class = Vagrant::Config::V2::DummyConfig end end def initialize_copy(orig) super @config = @config.dup if @config end def add_config(**options, &block) return if invalid? current = @config_class.new current.set_options(options) if options block.call(current) if block current = @config.merge(current) if @config @config = current end def finalize! return if invalid? @config.finalize! end # Returns whether the provisioner used was invalid or not. A provisioner # is invalid if it can't be found. # # @return [Boolean] def invalid? @invalid end end end end vagrant-2.0.2/plugins/kernel_v2/config/vm_subvm.rb000066400000000000000000000012211323370221500221470ustar00rootroot00000000000000require "vagrant/util/stacked_proc_runner" module VagrantPlugins module Kernel_V2 # Represents a single sub-VM in a multi-VM environment. class VagrantConfigSubVM include Vagrant::Util::StackedProcRunner # Returns an array of the configuration procs in [version, proc] # format. # # @return [Array] attr_reader :config_procs attr_reader :options def initialize @config_procs = [] @options = {} end def initialize_copy(other) super @config_procs = other.config_procs.clone @options = other.options.clone end end end end vagrant-2.0.2/plugins/kernel_v2/plugin.rb000066400000000000000000000023621323370221500203510ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Kernel_V2 # This is the "kernel" of Vagrant and contains the configuration classes # that make up the core of Vagrant for V2. class Plugin < Vagrant.plugin("2") name "kernel" description <<-DESC The kernel of Vagrant. This plugin contains required items for even basic functionality of Vagrant version 2. DESC # Core configuration keys provided by the kernel. Note that unlike # "kernel_v1", none of these configuration classes are upgradable. # This is by design, since we can't be sure if they're upgradable # until another version is available. config("ssh") do require File.expand_path("../config/ssh", __FILE__) SSHConfig end config("package") do require File.expand_path("../config/package", __FILE__) PackageConfig end config("push") do require File.expand_path("../config/push", __FILE__) PushConfig end config("vagrant") do require File.expand_path("../config/vagrant", __FILE__) VagrantConfig end config("vm") do require File.expand_path("../config/vm", __FILE__) VMConfig end end end end vagrant-2.0.2/plugins/providers/000077500000000000000000000000001323370221500166515ustar00rootroot00000000000000vagrant-2.0.2/plugins/providers/docker/000077500000000000000000000000001323370221500201205ustar00rootroot00000000000000vagrant-2.0.2/plugins/providers/docker/action.rb000066400000000000000000000255131323370221500217300ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Action # Include the built-in modules so we can use them as top-level things. include Vagrant::Action::Builtin # This action starts another container just like the real one running # but only for the purpose of running a single command rather than # to exist long-running. def self.action_run_command Vagrant::Action::Builder.new.tap do |b| # We just call the "up" action. We create a separate action # to hold this though in case we modify it in the future, and # so that we can switch on the "machine_action" env var. b.use action_up end end # This action brings the "machine" up from nothing, including creating the # container, configuring metadata, and booting. def self.action_up Vagrant::Action::Builder.new.tap do |b| b.use Call, IsState, :not_created do |env, b2| if env[:result] b2.use HandleBox end end b.use ConfigValidate b.use HostMachine # Yeah, this is supposed to be here twice (once more above). This # catches the case when the container was supposed to be created, # but the host state was unknown, and now we know its not actually # created. b.use Call, IsState, :not_created do |env, b2| if env[:result] b2.use HandleBox b2.use DestroyBuildImage end end b.use action_start end end def self.action_package lambda do |env| raise Errors::PackageNotSupported end end # This action just runs the provisioners on the machine. def self.action_provision Vagrant::Action::Builder.new.tap do |b| b.use ConfigValidate b.use Call, IsState, :not_created do |env, b2| if env[:result] b2.use Message, I18n.t("docker_provider.messages.not_created") next end b2.use Call, IsState, :running do |env2, b3| if !env2[:result] b3.use Message, I18n.t("docker_provider.messages.not_running") next end b3.use Call, HasSSH do |env3, b4| if env3[:result] b4.use Provision else b4.use Message, I18n.t("docker_provider.messages.provision_no_ssh"), post: true end end end end end end # This is the action that is primarily responsible for halting # the virtual machine, gracefully or by force. def self.action_halt Vagrant::Action::Builder.new.tap do |b| b.use Call, IsState, :host_state_unknown do |env, b2| if env[:result] b2.use HostMachine end end b.use Call, IsState, :not_created do |env, b2| if env[:result] b2.use Message, I18n.t("docker_provider.messages.not_created") next end b2.use Stop end end end # This action is responsible for reloading the machine, which # brings it down, sucks in new configuration, and brings the # machine back up with the new configuration. def self.action_reload Vagrant::Action::Builder.new.tap do |b| b.use ConfigValidate b.use Call, IsState, :not_created do |env, b2| if env[:result] b2.use Message, I18n.t("docker_provider.messages.not_created") next end b2.use action_halt b2.use Call, IsBuild do |env2, b3| if env2[:result] b3.use EnvSet, force_halt: true b3.use action_halt b3.use HostMachineSyncFoldersDisable b3.use Destroy b3.use ProvisionerCleanup end end b2.use action_start end end end # This is the action that is primarily responsible for completely # freeing the resources of the underlying virtual machine. def self.action_destroy Vagrant::Action::Builder.new.tap do |b| b.use Call, IsHostMachineCreated do |env, b2| if !env[:result] b2.use Message, I18n.t("docker_provider.messages.not_created") next end b2.use Call, IsState, :host_state_unknown do |env2, b3| if env2[:result] b3.use HostMachine end end b2.use Call, IsState, :not_created do |env2, b3| if env2[:result] b3.use Message, I18n.t("docker_provider.messages.not_created") next end b3.use Call, DestroyConfirm do |env3, b4| if env3[:result] b4.use ConfigValidate b4.use ProvisionerCleanup, :before b4.use EnvSet, force_halt: true b4.use action_halt b4.use HostMachineSyncFoldersDisable b4.use Destroy b4.use DestroyBuildImage else b4.use Message, I18n.t("docker_provider.messages.will_not_destroy") end end end end end end # This is the action that will exec into an SSH shell. def self.action_ssh Vagrant::Action::Builder.new.tap do |b| b.use Call, IsState, :not_created do |env, b2| if env[:result] raise Errors::ContainerNotCreatedError end b2.use Call, IsState, :running do |env2, b3| if !env2[:result] raise Errors::ContainerNotRunningError end b3.use PrepareSSH b3.use SSHExec end end end end # This is the action that will run a single SSH command. def self.action_ssh_run Vagrant::Action::Builder.new.tap do |b| b.use Call, IsState, :not_created do |env, b2| if env[:result] raise Errors::ContainerNotCreatedError end b2.use Call, IsState, :running do |env2, b3| if !env2[:result] raise Errors::ContainerNotRunningError end b3.use SSHRun end end end end def self.action_start Vagrant::Action::Builder.new.tap do |b| b.use Call, IsState, :running do |env, b2| if env[:machine_action] != :run_command b2.use Call, HasSSH do |env2, b3| if env2[:result] b3.use Provision else b3.use Message, I18n.t("docker_provider.messages.provision_no_ssh"), post: true end end end # If the container is running and we're doing a run, we're done next if env[:result] && env[:machine_action] != :run_command b2.use Call, IsState, :not_created do |env2, b3| if env2[:result] # First time making this thing, set to the "preparing" state b3.use InitState else b3.use EnvSet, host_machine_sync_folders: false end end b2.use HostMachineBuildDir b2.use HostMachineSyncFolders b2.use PrepareNFSValidIds b2.use SyncedFolderCleanup b2.use PrepareNFSSettings b2.use Login b2.use Build if env[:machine_action] != :run_command # If the container is NOT created yet, then do some setup steps # necessary for creating it. b2.use Call, IsState, :preparing do |env2, b3| if env2[:result] b3.use EnvSet, port_collision_repair: true b3.use HostMachinePortWarning b3.use HostMachinePortChecker b3.use HandleForwardedPortCollisions b3.use SyncedFolders b3.use ForwardedPorts b3.use Pull b3.use Create b3.use WaitForRunning else b3.use CompareSyncedFolders end end b2.use Start b2.use WaitForRunning b2.use Call, HasSSH do |env2, b3| if env2[:result] b3.use WaitForCommunicator end end else # We're in a run command, so we do things a bit differently. b2.use SyncedFolders b2.use Create end end end end def self.action_suspend lambda do |env| raise Errors::SuspendNotSupported end end # The autoload farm action_root = Pathname.new(File.expand_path("../action", __FILE__)) autoload :Build, action_root.join("build") autoload :CompareSyncedFolders, action_root.join("compare_synced_folders") autoload :Create, action_root.join("create") autoload :Destroy, action_root.join("destroy") autoload :DestroyBuildImage, action_root.join("destroy_build_image") autoload :ForwardedPorts, action_root.join("forwarded_ports") autoload :HasSSH, action_root.join("has_ssh") autoload :HostMachine, action_root.join("host_machine") autoload :HostMachineBuildDir, action_root.join("host_machine_build_dir") autoload :HostMachinePortChecker, action_root.join("host_machine_port_checker") autoload :HostMachinePortWarning, action_root.join("host_machine_port_warning") autoload :HostMachineRequired, action_root.join("host_machine_required") autoload :HostMachineSyncFolders, action_root.join("host_machine_sync_folders") autoload :HostMachineSyncFoldersDisable, action_root.join("host_machine_sync_folders_disable") autoload :InitState, action_root.join("init_state") 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 :PrepareNFSValidIds, action_root.join("prepare_nfs_valid_ids") autoload :PrepareNFSSettings, action_root.join("prepare_nfs_settings") autoload :Start, action_root.join("start") autoload :WaitForRunning, action_root.join("wait_for_running") end end end vagrant-2.0.2/plugins/providers/docker/action/000077500000000000000000000000001323370221500213755ustar00rootroot00000000000000vagrant-2.0.2/plugins/providers/docker/action/build.rb000066400000000000000000000047531323370221500230320ustar00rootroot00000000000000require "log4r" require "vagrant/util/ansi_escape_code_remover" module VagrantPlugins module DockerProvider module Action class Build include Vagrant::Util::ANSIEscapeCodeRemover def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::docker::build") end def call(env) machine = env[:machine] build_dir = env[:build_dir] build_dir ||= machine.provider_config.build_dir # If we're not building a container, then just skip this step return @app.call(env) if !build_dir # Try to read the image ID from the cache file if we've # already built it. image_file = machine.data_dir.join("docker_build_image") image = nil if image_file.file? image = image_file.read.chomp end # Verify the image exists if we have one if image && !machine.provider.driver.image?(image) machine.ui.output(I18n.t("docker_provider.build_image_invalid")) image = nil end # If we have no image or we're rebuilding, we rebuild if !image || env[:build_rebuild] # Build it args = machine.provider_config.build_args.clone if machine.provider_config.dockerfile dockerfile = machine.provider_config.dockerfile dockerfile_path = File.join(build_dir, dockerfile) args.push("--file").push(dockerfile_path) machine.ui.output( I18n.t("docker_provider.building_named_dockerfile", file: machine.provider_config.dockerfile)) else machine.ui.output(I18n.t("docker_provider.building")) end image = machine.provider.driver.build( build_dir, extra_args: args) do |type, data| data = remove_ansi_escape_codes(data.chomp).chomp env[:ui].detail(data) if data != "" end # Output the final image machine.ui.detail("\nImage: #{image}") # Store the image ID image_file.open("w") do |f| f.binmode f.write("#{image}\n") end else machine.ui.output(I18n.t("docker_provider.already_built")) end # Set the image for creation env[:create_image] = image @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/docker/action/compare_synced_folders.rb000066400000000000000000000032071323370221500264350ustar00rootroot00000000000000require "vagrant/action/builtin/mixin_synced_folders" module VagrantPlugins module DockerProvider module Action class CompareSyncedFolders include Vagrant::Action::Builtin::MixinSyncedFolders def initialize(app, env) @app = app end def call(env) machine = env[:machine] # Get the synced folders that are cached, and those that aren't cached = synced_folders(machine, cached: true) fresh = synced_folders(machine) # Build up a mapping of existing setup synced folders existing = {} cached.each do |_, fs| fs.each do |_, data| existing[data[:guestpath]] = data[:hostpath] end end # Remove the matching folders, and build up non-matching or # new syncedf olders. invalids = {} fresh.each do |_, fs| fs.each do |_, data| invalid = false old = existing.delete(data[:guestpath]) invalid = true if !old if !invalid && old invalid = true if old != data[:hostpath] end if invalid invalids[data[:guestpath]] = data[:hostpath] end end end # If we have invalid entries, these are changed or new entries. # If we have existing entries, then we removed some entries. if !invalids.empty? || !existing.empty? machine.ui.warn(I18n.t("docker_provider.synced_folders_changed")) end @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/docker/action/create.rb000066400000000000000000000120631323370221500231670ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Action class Create def initialize(app, env) @app = app end def call(env) @env = env @machine = env[:machine] @provider_config = @machine.provider_config @machine_config = @machine.config @driver = @machine.provider.driver params = create_params # If we're running a single command, we modify the params a bit if env[:machine_action] == :run_command # Use the command that is given to us params[:cmd] = env[:run_command] # Don't detach, we want to watch the command run params[:detach] = false # No ports should be shared to the host params[:ports] = [] # Allocate a pty if it was requested params[:pty] = true if env[:run_pty] # Remove container after execution params[:rm] = true if env[:run_rm] # Name should be unique params[:name] = "#{params[:name]}_#{Time.now.to_i}" # We link to our original container # TODO end env[:ui].output(I18n.t("docker_provider.creating")) env[:ui].detail(" Name: #{params[:name]}") env[:ui].detail(" Image: #{params[:image]}") if params[:cmd] && !params[:cmd].empty? env[:ui].detail(" Cmd: #{params[:cmd].join(" ")}") end params[:volumes].each do |volume| env[:ui].detail("Volume: #{volume}") end params[:ports].each do |pair| env[:ui].detail(" Port: #{pair}") end params[:links].each do |name, other| env[:ui].detail(" Link: #{name}:#{other}") end if env[:machine_action] != :run_command # For regular "ups" create it and get the CID cid = @driver.create(params) env[:ui].detail(" \n"+I18n.t( "docker_provider.created", id: cid[0...16])) @machine.id = cid elsif params[:detach] env[:ui].detail(" \n"+I18n.t("docker_provider.running_detached")) else ui_opts = {} # If we're running with a pty, we want the output to look as # authentic as possible. We don't prefix things and we don't # output a newline. if env[:run_pty] ui_opts[:prefix] = false ui_opts[:new_line] = false end # For run commands, we run it and stream back the output env[:ui].detail(" \n"+I18n.t("docker_provider.running")+"\n ") @driver.create(params, stdin: env[:run_pty]) do |type, data| env[:ui].detail(data.chomp, **ui_opts) end end @app.call(env) end def create_params container_name = @provider_config.name if !container_name container_name = "#{@env[:root_path].basename.to_s}_#{@machine.name}" container_name.gsub!(/[^-a-z0-9_]/i, "") container_name << "_#{Time.now.to_i}" end image = @env[:create_image] image ||= @provider_config.image links = [] @provider_config._links.each do |link| parts = link.split(":", 2) links << parts end { cmd: @provider_config.cmd, detach: true, env: @provider_config.env, expose: @provider_config.expose, extra_args: @provider_config.create_args, hostname: @machine_config.vm.hostname, image: image, links: links, name: container_name, ports: forwarded_ports(@provider_config.has_ssh), privileged: @provider_config.privileged, pty: false, volumes: @provider_config.volumes, } end def forwarded_ports(include_ssh=false) mappings = {} random = [] @machine.config.vm.networks.each do |type, options| next if type != :forwarded_port # Don't include SSH if we've explicitly asked not to next if options[:id] == "ssh" && !include_ssh # Skip port if it is disabled next if options[:disabled] # If the guest port is 0, put it in the random group if options[:guest] == 0 random << options[:host] next end mappings["#{options[:host]}/#{options[:protocol]}"] = options end # Build the results result = random.map(&:to_s) result += mappings.values.map do |fp| protocol = "" protocol = "/udp" if fp[:protocol].to_s == "udp" host_ip = "" host_ip = "#{fp[:host_ip]}:" if fp[:host_ip] "#{host_ip}#{fp[:host]}:#{fp[:guest]}#{protocol}" end.compact result end end end end end vagrant-2.0.2/plugins/providers/docker/action/destroy.rb000066400000000000000000000013121323370221500234100ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Action class Destroy def initialize(app, env) @app = app end def call(env) env[:ui].info I18n.t("docker_provider.messages.destroying") machine = env[:machine] driver = machine.provider.driver # If we have a build image, store that image_file = machine.data_dir.join("docker_build_image") image = nil if image_file.file? image = image_file.read.chomp end env[:build_image] = image driver.rm(machine.id) machine.id = nil @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/docker/action/destroy_build_image.rb000066400000000000000000000023161323370221500257360ustar00rootroot00000000000000require "log4r" module VagrantPlugins module DockerProvider module Action class DestroyBuildImage def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::docker::destroybuildimage") end def call(env) machine = env[:machine] image = env[:build_image] image_file = nil if !image # Try to read the image ID from the cache file if we've # already built it. image_file = machine.data_dir.join("docker_build_image") image = nil if image_file.file? image = image_file.read.chomp end end if image machine.ui.output(I18n.t("docker_provider.build_image_destroy")) if !machine.provider.driver.rmi(image) machine.ui.detail(I18n.t( "docker_provider.build_image_destroy_in_use")) end end if image_file && image_file.file? begin image_file.delete rescue Errno::ENOENT # Its okay end end @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/docker/action/forwarded_ports.rb000066400000000000000000000014321323370221500251260ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Action class ForwardedPorts def initialize(app, env) @app = app end def call(env) env[:machine].provider_config.ports.each do |p| host_ip = nil protocol = "tcp" host, guest = p.split(":", 2) if guest.include?(":") host_ip = host host, guest = guest.split(":", 2) end guest, protocol = guest.split("/", 2) if guest.include?("/") env[:machine].config.vm.network "forwarded_port", host: host.to_i, guest: guest.to_i, host_ip: host_ip, protocol: protocol end @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/docker/action/has_ssh.rb000066400000000000000000000006071323370221500233550ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Action # This middleware is used with Call to test if this machine supports # SSH. class HasSSH def initialize(app, env) @app = app end def call(env) env[:result] = env[:machine].provider_config.has_ssh @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/docker/action/host_machine.rb000066400000000000000000000042561323370221500243720ustar00rootroot00000000000000require "log4r" module VagrantPlugins module DockerProvider module Action # This action is responsible for creating the host machine if # we need to. The host machine is where Docker containers will # live. class HostMachine def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::docker::hostmachine") end def call(env) if !env[:machine].provider.host_vm? @logger.info("No host machine needed.") return @app.call(env) end env[:machine].ui.output(I18n.t( "docker_provider.host_machine_needed")) host_machine = env[:machine].provider.host_vm begin env[:machine].provider.host_vm_lock do setup_host_machine(host_machine, env) end rescue Vagrant::Errors::EnvironmentLockedError sleep 1 retry end @app.call(env) end protected def setup_host_machine(host_machine, env) # Create a UI for this machine that stays at the detail level proxy_ui = host_machine.ui.dup proxy_ui.opts[:bold] = false proxy_ui.opts[:prefix_spaces] = true proxy_ui.opts[:target] = env[:machine].name.to_s # Reload the machine so that if it was created while we didn't # hold the lock, we'll see the updated state. host_machine.reload # See if the machine is ready already. If not, start it. if host_machine.communicate.ready? env[:machine].ui.detail(I18n.t("docker_provider.host_machine_ready")) else env[:machine].ui.detail( I18n.t("docker_provider.host_machine_starting")) env[:machine].ui.detail(" ") host_machine.with_ui(proxy_ui) do host_machine.action(:up) end # Verify communication is ready. If not, we have a problem. if !host_machine.communicate.ready? raise Errors::HostVMCommunicatorNotReady, id: host_machine.id end end end end end end end vagrant-2.0.2/plugins/providers/docker/action/host_machine_build_dir.rb000066400000000000000000000027321323370221500264040ustar00rootroot00000000000000require "digest/md5" require "log4r" module VagrantPlugins module DockerProvider module Action class HostMachineBuildDir def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::docker::hostmachinebuilddir") end def call(env) machine = env[:machine] build_dir = machine.provider_config.build_dir # If we're not building a Dockerfile, ignore return @app.call(env) if !build_dir # If we're building a docker file, expand the directory build_dir = File.expand_path(build_dir, env[:machine].env.root_path) env[:build_dir] = build_dir # If we're not on a host VM, we're done return @app.call(env) if !machine.provider.host_vm? # We're on a host VM, so we need to move our build dir to # that machine. We do this by putting the synced folder on # ourself and letting HostMachineSyncFolders handle it. new_build_dir = "/var/lib/docker/docker_build_#{Digest::MD5.hexdigest(build_dir)}" options = { docker__ignore: true, docker__exact: true, }.merge(machine.provider_config.host_vm_build_dir_options || {}) machine.config.vm.synced_folder(build_dir, new_build_dir, options) # Set the build dir to be the correct one env[:build_dir] = new_build_dir @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/docker/action/host_machine_port_checker.rb000066400000000000000000000014021323370221500271100ustar00rootroot00000000000000require "log4r" module VagrantPlugins module DockerProvider module Action # This sets up the middleware env var to check for ports in use. class HostMachinePortChecker def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::docker::hostmachineportchecker") end def call(env) return @app.call(env) if !env[:machine].provider.host_vm? @machine = env[:machine] env[:port_collision_port_check] = method(:port_check) @app.call(env) end protected def port_check(port) host_machine = @machine.provider.host_vm host_machine.guest.capability(:port_open_check, port) end end end end end vagrant-2.0.2/plugins/providers/docker/action/host_machine_port_warning.rb000066400000000000000000000015731323370221500271620ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Action class HostMachinePortWarning def initialize(app, env) @app = app end def call(env) if !env[:machine].provider.host_vm? return @app.call(env) end # If we have forwarded ports, then notify the user that they # won't be immediately available unless a private network # is created. if has_forwarded_ports?(env[:machine]) env[:machine].ui.warn(I18n.t( "docker_provider.host_machine_forwarded_ports")) end @app.call(env) end protected def has_forwarded_ports?(machine) machine.config.vm.networks.each do |type, _| return true if type == :forwarded_port end false end end end end end vagrant-2.0.2/plugins/providers/docker/action/host_machine_required.rb000066400000000000000000000005771323370221500262740ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Action # This middleware is used with Call to test if we're using a host VM. class HostMachineRequired def initialize(app, env) @app = app end def call(env) env[:result] = env[:machine].provider.host_vm? @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/docker/action/host_machine_sync_folders.rb000066400000000000000000000136111323370221500271370ustar00rootroot00000000000000require "digest/md5" require "securerandom" require "log4r" require "vagrant/action/builtin/mixin_synced_folders" module VagrantPlugins module DockerProvider module Action # This action is responsible for creating the host machine if # we need to. The host machine is where Docker containers will # live. class HostMachineSyncFolders include Vagrant::Action::Builtin::MixinSyncedFolders def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::docker::hostmachine") end def call(env) return @app.call(env) if !env[:machine].provider.host_vm? if !env.key?(:host_machine_sync_folders) env[:host_machine_sync_folders] = true end host_machine = env[:machine].provider.host_vm # Lock while we make changes begin env[:machine].provider.host_vm_lock do setup_synced_folders(host_machine, env) end rescue Vagrant::Errors::EnvironmentLockedError sleep 1 retry end @app.call(env) end protected def setup_synced_folders(host_machine, env) # Write the host machine SFID if we have one id_path = env[:machine].data_dir.join("host_machine_sfid") if !id_path.file? host_sfid = SecureRandom.uuid id_path.open("w") do |f| f.binmode f.write("#{host_sfid}\n") end else host_sfid = id_path.read.chomp end # Create a UI for this machine that stays at the detail level proxy_ui = host_machine.ui.dup proxy_ui.opts[:bold] = false proxy_ui.opts[:prefix_spaces] = true proxy_ui.opts[:target] = env[:machine].name.to_s # Read the existing folders that are setup existing_folders = synced_folders(host_machine, cached: true) existing_ids = {} if existing_folders existing_folders.each do |impl, fs| fs.each do |_name, data| if data[:docker_sfid] && data[:docker_host_sfid] == host_sfid existing_ids[data[:docker_sfid]] = data end end end end # Sync some folders so that our volumes work later. new_config = VagrantPlugins::Kernel_V2::VMConfig.new our_folders = synced_folders(env[:machine]) our_folders.each do |type, folders| folders.each do |id, data| data = data.dup if type == :docker # We don't use the Docker type explicitly on the host VM data.delete(:type) end # Expand the hostpath relative to _our_ root path. Otherwise, # it expands it relative to the proxy VM, which is not what # we want. data[:hostpath] = File.expand_path( data[:hostpath], env[:machine].env.root_path) # Generate an ID that is deterministic based on our machine # and Vagrantfile path... id = Digest::MD5.hexdigest( "#{env[:machine].env.root_path}" + "#{data[:hostpath]}" + "#{data[:guestpath]}" + "#{env[:machine].name}") # Generate a new guestpath data[:docker_guestpath] = data[:guestpath] data[:docker_sfid] = id data[:docker_host_sfid] = host_sfid data[:id] = id[0...6] + rand(10000).to_s # If we specify exact then we know what we're doing if !data[:docker__exact] data[:guestpath] = "/var/lib/docker/docker_#{Time.now.to_i}_#{rand(100000)}" end # Add this synced folder onto the new config if we haven't # already shared it before. if !existing_ids.key?(id) # A bit of a hack for VirtualBox to mount our # folder as transient. This can be removed once # the VirtualBox synced folder mechanism is smarter. data[:virtualbox__transient] = true new_config.synced_folder( data[:hostpath], data[:guestpath], data) else # We already have the folder, so just load its data data = existing_ids[id] end # Remove from our machine env[:machine].config.vm.synced_folders.delete(id) # Add the "fixed" folder to our machine data = data.merge({ hostpath_exact: true, type: :docker, }) env[:machine].config.vm.synced_folder( data[:guestpath], data[:docker_guestpath], data) end end if !env[:host_machine_sync_folders] @logger.info("Not syncing folders because container created.") end if !new_config.synced_folders.empty? # Sync the folders! env[:machine].ui.output(I18n.t( "docker_provider.host_machine_syncing_folders")) host_machine.with_ui(proxy_ui) do action_env = { synced_folders_config: new_config } begin host_machine.action(:sync_folders, action_env) rescue Vagrant::Errors::MachineActionLockedError sleep 1 retry rescue Vagrant::Errors::UnimplementedProviderAction callable = Vagrant::Action::Builder.new callable.use Vagrant::Action::Builtin::SyncedFolders host_machine.action_raw(:sync_folders, callable, action_env) end end end end end end end end vagrant-2.0.2/plugins/providers/docker/action/host_machine_sync_folders_disable.rb000066400000000000000000000052761323370221500306320ustar00rootroot00000000000000require "log4r" require "vagrant/action/builtin/mixin_synced_folders" module VagrantPlugins module DockerProvider module Action # This action disables the synced folders we created. class HostMachineSyncFoldersDisable include Vagrant::Action::Builtin::MixinSyncedFolders def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::docker::hostmachine") end def call(env) return @app.call(env) if !env[:machine].provider.host_vm? # Read our random ID for this instance id_path = env[:machine].data_dir.join("host_machine_sfid") return @app.call(env) if !id_path.file? host_sfid = id_path.read.chomp host_machine = env[:machine].provider.host_vm @app.call(env) begin env[:machine].provider.host_vm_lock do setup_synced_folders(host_machine, host_sfid, env) end rescue Vagrant::Errors::EnvironmentLockedError sleep 1 retry end end protected def setup_synced_folders(host_machine, host_sfid, env) to_disable = [] # Read the existing folders that are setup existing_folders = synced_folders(host_machine, cached: true) if existing_folders existing_folders.each do |impl, fs| fs.each do |id, data| if data[:docker_host_sfid] == host_sfid to_disable << id end end end end # Nothing to do if we have no bad folders return if to_disable.empty? # Create a UI for this machine that stays at the detail level proxy_ui = host_machine.ui.dup proxy_ui.opts[:bold] = false proxy_ui.opts[:prefix_spaces] = true proxy_ui.opts[:target] = env[:machine].name.to_s env[:machine].ui.output(I18n.t( "docker_provider.host_machine_disabling_folders")) host_machine.with_ui(proxy_ui) do action_env = { synced_folders_cached: true, synced_folders_disable: to_disable, } begin host_machine.action(:sync_folders, action_env) rescue Vagrant::Errors::MachineActionLockedError sleep 1 retry rescue Vagrant::Errors::UnimplementedProviderAction callable = Vagrant::Action::Builder.new callable.use Vagrant::Action::Builtin::SyncedFolders host_machine.action_raw(:sync_folders, callable, action_env) end end end end end end end vagrant-2.0.2/plugins/providers/docker/action/init_state.rb000066400000000000000000000007241323370221500240700ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Action class InitState def initialize(app, env) @app = app end def call(env) # We set the ID of the machine to "preparing" so that we can use # the data dir without it being deleted with the not_created state. env[:machine].id = nil env[:machine].id = "preparing" @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/docker/action/is_build.rb000066400000000000000000000004641323370221500235200ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Action class IsBuild def initialize(app, env) @app = app end def call(env) env[:result] = !!env[:machine].provider_config.build_dir @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/docker/action/is_host_machine_created.rb000066400000000000000000000013651323370221500265520ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Action class IsHostMachineCreated def initialize(app, env) @app = app end def call(env) if !env[:machine].provider.host_vm? env[:result] = true return @app.call(env) end host_machine = env[:machine].provider.host_vm env[:result] = host_machine.state.id != Vagrant::MachineState::NOT_CREATED_ID # If the host machine isn't created, neither are we. It is # important we set this to nil here so that global-status # sees the right thing. env[:machine].id = nil if !env[:result] @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/docker/action/login.rb000066400000000000000000000021211323370221500230260ustar00rootroot00000000000000require "log4r" module VagrantPlugins module DockerProvider module Action class Login def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::docker::login") end def call(env) config = env[:machine].provider_config driver = env[:machine].provider.driver # 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) end end end end end end vagrant-2.0.2/plugins/providers/docker/action/prepare_nfs_settings.rb000066400000000000000000000032631323370221500261520ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Action class PrepareNFSSettings include Vagrant::Util::Retryable def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::vm::nfs") end def call(env) @machine = env[:machine] @app.call(env) if using_nfs? && !privileged_container? raise Errors::NfsWithoutPrivilegedError end if using_nfs? @logger.info("Using NFS, preparing NFS settings by reading host IP and machine IP") add_ips_to_env!(env) end end # We're using NFS if we have any synced folder with NFS configured. If # we are not using NFS we don't need to do the extra work to # populate these fields in the environment. def using_nfs? @machine.config.vm.synced_folders.any? { |_, opts| opts[:type] == :nfs } end def privileged_container? @machine.provider.driver.privileged?(@machine.id) end # Extracts the proper host and guest IPs for NFS mounts and stores them # in the environment for the SyncedFolder action to use them in # mounting. # # The ! indicates that this method modifies its argument. def add_ips_to_env!(env) provider = env[:machine].provider host_ip = provider.driver.docker_bridge_ip machine_ip = provider.ssh_info[:host] raise Vagrant::Errors::NFSNoHostonlyNetwork if !host_ip || !machine_ip env[:nfs_host_ip] = host_ip env[:nfs_machine_ip] = machine_ip end end end end end vagrant-2.0.2/plugins/providers/docker/action/prepare_nfs_valid_ids.rb000066400000000000000000000006451323370221500262510ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Action class PrepareNFSValidIds def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::vm::nfs") end def call(env) machine = env[:machine] env[:nfs_valid_ids] = machine.provider.driver.all_containers @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/docker/action/prepare_ssh.rb000066400000000000000000000026461323370221500242450ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Action class PrepareSSH def initialize(app, env) @app = app end def call(env) # If we aren't using a host VM, then don't worry about it return @app.call(env) if !env[:machine].provider.host_vm? env[:machine].ui.output(I18n.t( "docker_provider.ssh_through_host_vm")) # Modify the SSH info to be the host VM's info env[:ssh_info] = env[:machine].provider.host_vm.ssh_info # Modify the SSH options for when we `vagrant ssh`... ssh_opts = env[:ssh_opts] || {} # Build the command we'll execute within the Docker host machine: ssh_command = env[:machine].communicate.container_ssh_command if !Array(ssh_opts[:extra_args]).empty? ssh_command << " #{Array(ssh_opts[:extra_args]).join(" ")}" end # Modify the SSH options for the original command: # Append "-t" to force a TTY allocation ssh_opts[:extra_args] = ["-t"] # Enable Agent forwarding when requested for the target VM if env[:machine].ssh_info[:forward_agent] ssh_opts[:extra_args] << "-o ForwardAgent=yes" end ssh_opts[:extra_args] << ssh_command # Set the opts env[:ssh_opts] = ssh_opts @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/docker/action/pull.rb000066400000000000000000000012651323370221500227020ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Action class Pull def initialize(app, env) @app = app end def call(env) @env = env @machine = env[:machine] @provider_config = @machine.provider_config @driver = @machine.provider.driver # Skip pulling if the image is built return @app.call(env) if @env[:create_image] || !@provider_config.pull image = @provider_config.image env[:ui].output(I18n.t("docker_provider.pull", image: image)) @driver.pull(image) @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/docker/action/start.rb000066400000000000000000000006501323370221500230600ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Action class Start def initialize(app, env) @app = app end def call(env) machine = env[:machine] driver = machine.provider.driver machine.ui.output(I18n.t("docker_provider.messages.starting")) driver.start(machine.id) @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/docker/action/stop.rb000066400000000000000000000010001323370221500226760ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Action class Stop def initialize(app, env) @app = app end def call(env) machine = env[:machine] driver = machine.provider.driver if driver.running?(machine.id) env[:ui].info I18n.t("docker_provider.messages.stopping") driver.stop(machine.id, machine.provider_config.stop_timeout) end @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/docker/action/wait_for_running.rb000066400000000000000000000030711323370221500252750ustar00rootroot00000000000000require "thread" require "log4r" module VagrantPlugins module DockerProvider module Action class WaitForRunning def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::docker::waitforrunning") end def call(env) machine = env[:machine] wait = true if !machine.provider_config.remains_running wait = false elsif machine.provider.state.id == :running wait = false end # If we're not waiting, just return return @app.call(env) if !wait machine.ui.output(I18n.t("docker_provider.waiting_for_running")) # First, make sure it leaves the stopped state if its supposed to. after = sleeper(5) while machine.provider.state.id == :stopped if after[:done] raise Errors::StateStopped end sleep 0.2 end # Then, wait for it to become running after = sleeper(30) while true state = machine.provider.state break if state.id == :running @logger.info("Waiting for container to run. State: #{state.id}") if after[:done] raise Errors::StateNotRunning end sleep 0.2 end @app.call(env) end protected def sleeper(duration) Thread.new(duration) do |d| sleep(d) Thread.current[:done] = true end end end end end end vagrant-2.0.2/plugins/providers/docker/cap/000077500000000000000000000000001323370221500206635ustar00rootroot00000000000000vagrant-2.0.2/plugins/providers/docker/cap/proxy_machine.rb000066400000000000000000000003751323370221500240620ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Cap module ProxyMachine def self.proxy_machine(machine) return nil if !machine.provider.host_vm? machine.provider.host_vm end end end end end vagrant-2.0.2/plugins/providers/docker/cap/public_address.rb000066400000000000000000000012531323370221500241740ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Cap module PublicAddress def self.public_address(machine) return nil if machine.state.id != :running # If we're using a host VM, then return the IP of that # rather than of our own machine. if machine.provider.host_vm? host_machine = machine.provider.host_vm return nil if !host_machine.provider.capability?(:public_address) return host_machine.provider.capability(:public_address) end ssh_info = machine.ssh_info return nil if !ssh_info ssh_info[:host] end end end end end vagrant-2.0.2/plugins/providers/docker/command/000077500000000000000000000000001323370221500215365ustar00rootroot00000000000000vagrant-2.0.2/plugins/providers/docker/command/exec.rb000066400000000000000000000061211323370221500230070ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Command class Exec < Vagrant.plugin("2", :command) def self.synopsis "attach to an already-running docker container" end def execute options = {} options[:detach] = false options[:pty] = false options[:interactive] = false options[:prefix] = true opts = OptionParser.new do |o| o.banner = "Usage: vagrant docker-exec [options] [name] -- [args]" o.separator "" o.separator "Options:" o.separator "" o.on("--[no-]detach", "Run in the background") do |d| options[:detach] = d end o.on("-i", "--[no-]interactive", "Keep STDIN open even if not attached") do |i| options[:interactive] = i end o.on("-t", "--[no-]tty", "Allocate a pty") do |t| options[:pty] = t end o.on("-u", "--user USER", "User or UID") do |u| options[:user] = u end o.on("--[no-]prefix", "Prefix output with machine names") do |p| options[:prefix] = p end end # Parse out the extra args to send to SSH, which is everything # after the "--" command = nil split_index = @argv.index("--") if split_index command = @argv.drop(split_index + 1) @argv = @argv.take(split_index) end # Parse the options argv = parse_options(opts) return if !argv # Show the error if we don't have "--" _after_ parse_options # so that "-h" and "--help" work properly. if !split_index raise Errors::ExecCommandRequired end target_opts = { provider: :docker } target_opts[:single_target] = options[:pty] with_target_vms(argv, target_opts) do |machine| if machine.state.id != :running @env.ui.info("#{machine.id} is not running.") next end exec_command(machine, command, options) end return 0 end def exec_command(machine, command, options) exec_cmd = %w(docker exec) exec_cmd << "-i" if options[:interactive] exec_cmd << "-t" if options[:pty] exec_cmd << "-u" << options[:user] if options[:user] exec_cmd << machine.id exec_cmd += options[:extra_args] if options[:extra_args] exec_cmd += command # Run this interactively if asked. exec_options = options exec_options[:stdin] = true if options[:pty] output = "" machine.provider.driver.execute(*exec_cmd, exec_options) do |type, data| output += data end output_options = {} output_options[:prefix] = false if !options[:prefix] if !output.empty? machine.ui.output(output.chomp, **output_options) end end end end end end vagrant-2.0.2/plugins/providers/docker/command/logs.rb000066400000000000000000000063531323370221500230360ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Command class Logs < Vagrant.plugin("2", :command) def self.synopsis "outputs the logs from the Docker container" end def execute options = {} options[:follow] = false options[:prefix] = true opts = OptionParser.new do |o| o.banner = "Usage: vagrant docker-logs [options]" o.separator "" o.separator "Options:" o.separator "" o.on("--[no-]follow", "Continue streaming in log output") do |f| options[:follow] = f end o.on("--[no-]prefix", "Prefix output with machine names") do |p| options[:prefix] = p end end # Parse the options argv = parse_options(opts) return if !argv # This keeps track of if we ran our action on any machines... any_success = false # Start a batch action that sends all the logs to stdout. This # will parallelize, if enabled, across all containers that are # chosen. @env.batch do |batch| with_target_vms(argv) do |machine| if machine.provider_name != :docker machine.ui.output(I18n.t("docker_provider.not_docker_provider")) next end state = machine.state.id if state == :host_state_unknown machine.ui.output(I18n.t("docker_provider.logs_host_state_unknown")) next elsif state == :not_created machine.ui.output(I18n.t("docker_provider.not_created_skip")) next end # At least one was run! any_success = true batch.custom(machine) do |m| execute_single(m, options) end end end # If we didn't run on any machines, then exit status 1 return any_success ? 0 : 1 end protected # Executes the "docker logs" command on a single machine and proxies # the output to our UI. def execute_single(machine, options) command = ["docker", "logs"] command << "--follow" if options[:follow] command << machine.id output_options = {} output_options[:prefix] = false if !options[:prefix] data_acc = "" machine.provider.driver.execute(*command) do |type, data| # Accumulate the data so we only output lines at a time data_acc << data # If we have a newline, then output all the lines we have so far if data_acc.include?("\n") lines = data_acc.split("\n") if !data_acc.end_with?("\n") data_acc = lines.pop.chomp else data_acc = "" end lines.each do |line| line = " " if line == "" machine.ui.output(line, **output_options) end end end # Output any remaining data machine.ui.output(data_acc, **output_options) if !data_acc.empty? end end end end end vagrant-2.0.2/plugins/providers/docker/command/run.rb000066400000000000000000000040061323370221500226670ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Command class Run < Vagrant.plugin("2", :command) def self.synopsis "run a one-off command in the context of a container" end def execute options = {} options[:detach] = false options[:pty] = false options[:rm] = true opts = OptionParser.new do |o| o.banner = "Usage: vagrant docker-run [command...]" o.separator "" o.separator "Options:" o.separator "" o.on("--[no-]detach", "Run in the background") do |d| options[:detach] = d end o.on("-t", "--[no-]tty", "Allocate a pty") do |t| options[:pty] = t end o.on("-r,", "--[no-]rm", "Remove container after execution") do |r| options[:rm] = r end end # Parse out the extra args to send to SSH, which is everything # after the "--" command = nil split_index = @argv.index("--") if split_index command = @argv.drop(split_index + 1) @argv = @argv.take(split_index) end # Parse the options argv = parse_options(opts) return if !argv # Show the error if we don't have "--" _after_ parse_options # so that "-h" and "--help" work properly. if !split_index @env.ui.error(I18n.t("docker_provider.run_command_required")) return 1 end target_opts = { provider: :docker } target_opts[:single_target] = options[:pty] with_target_vms(argv, target_opts) do |machine| # Run it! machine.action( :run_command, run_command: command, run_detach: options[:detach], run_pty: options[:pty], run_rm: options[:rm] ) end 0 end end end end end vagrant-2.0.2/plugins/providers/docker/communicator.rb000066400000000000000000000145361323370221500231560ustar00rootroot00000000000000require "digest/md5" require "tempfile" module VagrantPlugins module DockerProvider # This communicator uses the host VM as proxy to communicate to the # actual Docker container via SSH. class Communicator < Vagrant.plugin("2", :communicator) def initialize(machine) @machine = machine @host_vm = machine.provider.host_vm # We only work on the Docker provider if machine.provider_name != :docker raise Errors::CommunicatorNotDocker end end #------------------------------------------------------------------- # Communicator Methods #------------------------------------------------------------------- def ready? # We can't be ready if we can't talk to the host VM return false if !@host_vm.communicate.ready? # We're ready if we can establish an SSH connection to the container command = container_ssh_command return false if !command @host_vm.communicate.test("#{command} exit") end def download(from, to) # Same process as upload, but in reverse # First, we use `cat` to copy that file from the Docker container. temp = "/tmp/docker_d#{Time.now.to_i}_#{rand(100000)}" @host_vm.communicate.execute("#{container_ssh_command} 'cat #{from}' >#{temp}") # Then, we download this from the host VM. @host_vm.communicate.download(temp, to) # Remove the temporary file @host_vm.communicate.execute("rm -f #{temp}", error_check: false) end def execute(command, **opts, &block) fence = {} fence[:stderr] = "VAGRANT FENCE: #{Time.now.to_i} #{rand(100000)}" fence[:stdout] = "VAGRANT FENCE: #{Time.now.to_i} #{rand(100000)}" # We want to emulate how the SSH communicator actually executes # things, so we build up the list of commands to execute in a # giant shell script. tf = Tempfile.new("vagrant") tf.binmode tf.write("export TERM=vt100\n") tf.write("echo #{fence[:stdout]}\n") tf.write("echo #{fence[:stderr]} >&2\n") tf.write("#{command}\n") tf.write("exit\n") tf.close # Upload the temp file to the remote machine remote_temp = "/tmp/docker_#{Time.now.to_i}_#{rand(100000)}" @host_vm.communicate.upload(tf.path, remote_temp) # Determine the shell to execute. Prefer the explicitly passed in shell # over the default configured shell. If we are using `sudo` then we # need to wrap the shell in a `sudo` call. shell_cmd = @machine.config.ssh.shell shell_cmd = opts[:shell] if opts[:shell] shell_cmd = "sudo -E -H #{shell_cmd}" if opts[:sudo] acc = {} fenced = {} result = @host_vm.communicate.execute( "#{container_ssh_command} '#{shell_cmd}' <#{remote_temp}", opts) do |type, data| # If we don't have a block, we don't care about the data next if !block # We only care about stdout and stderr output next if ![:stdout, :stderr].include?(type) # If we reached our fence, then just output if fenced[type] block.call(type, data) next end # Otherwise, accumulate acc[type] = data # Look for the fence index = acc[type].index(fence[type]) next if !index fenced[type] = true index += fence[type].length data = acc[type][index..-1].chomp acc[type] = "" block.call(type, data) end @host_vm.communicate.execute("rm -f #{remote_temp}", error_check: false) return result end def sudo(command, **opts, &block) opts = { sudo: true }.merge(opts) execute(command, opts, &block) end def test(command, **opts) opts = { error_check: false }.merge(opts) execute(command, opts) == 0 end def upload(from, to) # First, we upload this to the host VM to some temporary directory. to_temp = "/tmp/docker_#{Time.now.to_i}_#{rand(100000)}" @host_vm.communicate.upload(from, to_temp) # Then, we use `cat` to get that file into the Docker container. @host_vm.communicate.execute( "#{container_ssh_command} 'cat >#{to}' <#{to_temp}") # Remove the temporary file @host_vm.communicate.execute("rm -f #{to_temp}", error_check: false) end #------------------------------------------------------------------- # Other Methods #------------------------------------------------------------------- # This returns the raw SSH command string that can be used to # connect via SSH to the container if you're on the same machine # as the container. # # @return [String] def container_ssh_command # Get the container's SSH info info = @machine.ssh_info return nil if !info info[:port] ||= 22 # Make sure our private keys are synced over to the host VM ssh_args = sync_private_keys(info).map do |path| "-i #{path}" end # Use ad-hoc SSH options for the hop on the docker proxy if info[:forward_agent] ssh_args << "-o ForwardAgent=yes" end ssh_args.concat(["-o Compression=yes", "-o ConnectTimeout=5", "-o StrictHostKeyChecking=no", "-o UserKnownHostsFile=/dev/null"]) # Build the SSH command "ssh #{info[:username]}@#{info[:host]} -p#{info[:port]} #{ssh_args.join(" ")}" end protected def sync_private_keys(info) @keys ||= {} id = Digest::MD5.hexdigest( @machine.env.root_path.to_s + @machine.name.to_s) result = [] info[:private_key_path].each do |path| if !@keys[path.to_s] # We haven't seen this before, upload it! guest_path = "/tmp/key_#{id}_#{Digest::MD5.hexdigest(path.to_s)}" @host_vm.communicate.upload(path.to_s, guest_path) # Make sure it has the proper chmod @host_vm.communicate.execute("chmod 0600 #{guest_path}") # Set it @keys[path.to_s] = guest_path end result << @keys[path.to_s] end result end end end end vagrant-2.0.2/plugins/providers/docker/config.rb000066400000000000000000000243251323370221500217200ustar00rootroot00000000000000require "pathname" require_relative "../../../lib/vagrant/util/platform" module VagrantPlugins module DockerProvider class Config < Vagrant.plugin("2", :config) attr_accessor :image, :cmd, :ports, :volumes, :privileged # Additional arguments to pass to `docker build` when creating # an image using the build dir setting. # # @return [Array] attr_accessor :build_args # The directory with a Dockerfile to build and use as the basis # for this container. If this is set, "image" should not be set. # # @return [String] attr_accessor :build_dir # Use docker-compose to manage the lifecycle and environment for # containers instead of using docker directly. # # @return [Boolean] attr_accessor :compose # Configuration Hash used for build the docker-compose composition # file. This can be used for adding networks or volumes. # # @return [Hash] attr_accessor :compose_configuration # An optional file name of a Dockerfile to be used when building # the image. This requires Docker >1.5.0. # # @return [String] attr_accessor :dockerfile # Additional arguments to pass to `docker run` when creating # the container for the first time. This is an array of args. # # @return [Array] attr_accessor :create_args # Environmental variables to set in the container. # # @return [Hash] attr_accessor :env # Ports to expose from the container but not to the host machine. # This is useful for links. # # @return [Array] attr_accessor :expose # Force using a proxy VM, even on Linux hosts. # # @return [Boolean] attr_accessor :force_host_vm # True if the Docker container exposes SSH access. If this is true, # then Vagrant can do a bunch more things like setting the hostname, # provisioning, etc. attr_accessor :has_ssh # Options for the build dir synced folder if a host VM is in use. # # @return [Hash] attr_accessor :host_vm_build_dir_options # The name for the container. This must be unique for all containers # on the proxy machine if it is made. # # @return [String] attr_accessor :name # If true, the image will be pulled on every `up` and `reload` # to ensure the latest image. # # @return [Bool] attr_accessor :pull # True if the docker container is meant to stay in the "running" # state (is a long running process). By default this is true. # # @return [Boolean] attr_accessor :remains_running # The time to wait before sending a SIGTERM to the container # when it is stopped. # # @return [Integer] attr_accessor :stop_timeout # The name of the machine in the Vagrantfile set with # "vagrant_vagrantfile" that will be the docker host. Defaults # to "default" # # See the "vagrant_vagrantfile" docs for more info. # # @return [String] attr_accessor :vagrant_machine # The path to the Vagrantfile that contains a VM that will be # started as the Docker host if needed (Windows, OS X, Linux # without container support). # # Defaults to a built-in Vagrantfile that will load boot2docker. # # NOTE: This only has an effect if Vagrant needs a Docker host. # Vagrant determines this automatically based on the environment # it is running in. # # @return [String] attr_accessor :vagrant_vagrantfile #-------------------------------------------------------------- # Auth Settings #-------------------------------------------------------------- # Server to authenticate to. If blank, will use the default # Docker authentication endpoint (which is the Docker Hub at the # time of this comment). # # @return [String] attr_accessor :auth_server # Email for logging in to a remote Docker server. # # @return [String] attr_accessor :email # Email for logging in to a remote Docker server. # # @return [String] attr_accessor :username # Password for logging in to a remote Docker server. If this is # not blank, then Vagrant will run `docker login` prior to any # Docker runs. # # The presence of auth will also force the Docker environments to # serialize on `up` so that different users/passwords don't overlap. # # @return [String] attr_accessor :password def initialize @build_args = [] @build_dir = UNSET_VALUE @cmd = UNSET_VALUE @compose = UNSET_VALUE @compose_configuration = {} @create_args = UNSET_VALUE @dockerfile = UNSET_VALUE @env = {} @expose = [] @force_host_vm = UNSET_VALUE @has_ssh = UNSET_VALUE @host_vm_build_dir_options = UNSET_VALUE @image = UNSET_VALUE @name = UNSET_VALUE @links = [] @pull = UNSET_VALUE @ports = UNSET_VALUE @privileged = UNSET_VALUE @remains_running = UNSET_VALUE @stop_timeout = UNSET_VALUE @volumes = [] @vagrant_machine = UNSET_VALUE @vagrant_vagrantfile = UNSET_VALUE @auth_server = UNSET_VALUE @email = UNSET_VALUE @username = UNSET_VALUE @password = UNSET_VALUE end def link(name) @links << name end def merge(other) super.tap do |result| # This is a bit confusing. The tests explain the purpose of this # better than the code lets on, I believe. if (other.image != UNSET_VALUE || other.build_dir != UNSET_VALUE) && (other.image == UNSET_VALUE || other.build_dir == UNSET_VALUE) if other.image != UNSET_VALUE && @build_dir != UNSET_VALUE result.build_dir = nil end if other.build_dir != UNSET_VALUE && @image != UNSET_VALUE result.image = nil end end env = {} env.merge!(@env) if @env env.merge!(other.env) if other.env result.env = env expose = self.expose.dup expose += other.expose result.instance_variable_set(:@expose, expose) links = _links.dup links += other._links result.instance_variable_set(:@links, links) end end def finalize! @build_args = [] if @build_args == UNSET_VALUE @build_dir = nil if @build_dir == UNSET_VALUE @cmd = [] if @cmd == UNSET_VALUE @compose = false if @compose == UNSET_VALUE @create_args = [] if @create_args == UNSET_VALUE @dockerfile = nil if @dockerfile == UNSET_VALUE @env ||= {} @has_ssh = false if @has_ssh == UNSET_VALUE @image = nil if @image == UNSET_VALUE @name = nil if @name == UNSET_VALUE @pull = false if @pull == UNSET_VALUE @ports = [] if @ports == UNSET_VALUE @privileged = false if @privileged == UNSET_VALUE @remains_running = true if @remains_running == UNSET_VALUE @stop_timeout = 1 if @stop_timeout == UNSET_VALUE @vagrant_machine = nil if @vagrant_machine == UNSET_VALUE @vagrant_vagrantfile = nil if @vagrant_vagrantfile == UNSET_VALUE @auth_server = nil if @auth_server == UNSET_VALUE @email = "" if @email == UNSET_VALUE @username = "" if @username == UNSET_VALUE @password = "" if @password == UNSET_VALUE if @host_vm_build_dir_options == UNSET_VALUE @host_vm_build_dir_options = nil end # On non-linux platforms (where there is no native docker), force the # host VM. Other users can optionally disable this by setting the # value explicitly to false in their Vagrantfile. if @force_host_vm == UNSET_VALUE @force_host_vm = !Vagrant::Util::Platform.linux? && !Vagrant::Util::Platform.darwin? && !Vagrant::Util::Platform.windows? end # The machine name must be a symbol @vagrant_machine = @vagrant_machine.to_sym if @vagrant_machine @expose.uniq! if @compose_configuration.is_a?(Hash) # Ensures configuration is using basic types @compose_configuration = JSON.parse(@compose_configuration.to_json) end end def validate(machine) errors = _detected_errors if @build_dir && @image errors << I18n.t("docker_provider.errors.config.both_build_and_image") end if !@build_dir && !@image errors << I18n.t("docker_provider.errors.config.build_dir_or_image") end if @build_dir build_dir_pn = Pathname.new(@build_dir) if !build_dir_pn.directory? errors << I18n.t("docker_provider.errors.config.build_dir_invalid") end end if !@compose_configuration.is_a?(Hash) errors << I18n.t("docker_provider.errors.config.compose_configuration_hash") end if @compose && @force_host_vm errors << I18n.t("docker_provider.errors.config.compose_force_vm") end if !@create_args.is_a?(Array) errors << I18n.t("docker_provider.errors.config.create_args_array") end @links.each do |link| parts = link.split(":") if parts.length != 2 || parts[0] == "" || parts[1] == "" errors << I18n.t( "docker_provider.errors.config.invalid_link", link: link) end end if @vagrant_vagrantfile vf_pn = Pathname.new(@vagrant_vagrantfile) if !vf_pn.file? errors << I18n.t("docker_provider.errors.config.invalid_vagrantfile") end end { "docker provider" => errors } end #-------------------------------------------------------------- # Functions below should not be called by config files #-------------------------------------------------------------- def _links @links end end end end vagrant-2.0.2/plugins/providers/docker/driver.rb000066400000000000000000000123221323370221500217400ustar00rootroot00000000000000require "json" require "log4r" require_relative "./driver/compose" module VagrantPlugins module DockerProvider class Driver # The executor is responsible for actually executing Docker commands. # This is set by the provider, but defaults to local execution. attr_accessor :executor def initialize @logger = Log4r::Logger.new("vagrant::docker::driver") @executor = Executor::Local.new end def build(dir, **opts, &block) args = Array(opts[:extra_args]) args << dir result = execute('docker', 'build', *args, &block) matches = result.scan(/Successfully built (.+)$/i) if matches.empty? # This will cause a stack trace in Vagrant, but it is a bug # if this happens anyways. raise "UNKNOWN OUTPUT: #{result}" end # Return the last match, and the capture of it matches[-1][0] end def create(params, **opts, &block) image = params.fetch(:image) links = params.fetch(:links) ports = Array(params[:ports]) volumes = Array(params[:volumes]) name = params.fetch(:name) cmd = Array(params.fetch(:cmd)) env = params.fetch(:env) expose = Array(params[:expose]) run_cmd = %W(docker run --name #{name}) run_cmd << "-d" if params[:detach] run_cmd += env.map { |k,v| ['-e', "#{k}=#{v}"] } run_cmd += expose.map { |p| ['--expose', "#{p}"] } run_cmd += links.map { |k, v| ['--link', "#{k}:#{v}"] } run_cmd += ports.map { |p| ['-p', p.to_s] } run_cmd += volumes.map { |v| v = v.to_s if v.include?(":") && @executor.windows? host, guest = v.split(":", 2) host = Vagrant::Util::Platform.windows_path(host) # NOTE: Docker does not support UNC style paths (which also # means that there's no long path support). Hopefully this # will be fixed someday and the gsub below can be removed. host.gsub!(/^[^A-Za-z]+/, "") v = [host, guest].join(":") end ['-v', v.to_s] } run_cmd += %W(--privileged) if params[:privileged] run_cmd += %W(-h #{params[:hostname]}) if params[:hostname] run_cmd << "-t" if params[:pty] run_cmd << "--rm=true" if params[:rm] run_cmd += params[:extra_args] if params[:extra_args] run_cmd += [image, cmd] execute(*run_cmd.flatten, **opts, &block).chomp.lines.last end def state(cid) case when running?(cid) :running when created?(cid) :stopped else :not_created end end def created?(cid) result = execute('docker', 'ps', '-a', '-q', '--no-trunc').to_s result =~ /^#{Regexp.escape cid}$/ end def image?(id) result = execute('docker', 'images', '-q').to_s result =~ /^#{Regexp.escape(id)}$/ end def running?(cid) result = execute('docker', 'ps', '-q', '--no-trunc') result =~ /^#{Regexp.escape cid}$/m end def privileged?(cid) inspect_container(cid)['HostConfig']['Privileged'] end def login(email, username, password, server) cmd = %W(docker login) cmd += ["-e", email] if email != "" cmd += ["-u", username] if username != "" cmd += ["-p", password] if password != "" cmd << server if server && server != "" execute(*cmd.flatten) end def logout(server) cmd = %W(docker logout) cmd << server if server && server != "" execute(*cmd.flatten) end def pull(image) execute('docker', 'pull', image) end def start(cid) if !running?(cid) execute('docker', 'start', cid) # This resets the cached information we have around, allowing `vagrant reload`s # to work properly @data = nil end end def stop(cid, timeout) if running?(cid) execute('docker', 'stop', '-t', timeout.to_s, cid) end end def rm(cid) if created?(cid) execute('docker', 'rm', '-f', '-v', cid) end end def rmi(id) execute('docker', 'rmi', id) return true rescue Exception => e return false if e.to_s.include?("is using it") raise if !e.to_s.include?("No such image") end 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 end def all_containers execute('docker', 'ps', '-a', '-q', '--no-trunc').to_s.split end def docker_bridge_ip output = execute('/sbin/ip', '-4', 'addr', 'show', 'scope', 'global', 'docker0') if output =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/ return $1.to_s else # TODO: Raise an user friendly message raise 'Unable to fetch docker bridge IP!' end end def execute(*cmd, **opts, &block) @executor.execute(*cmd, **opts, &block) end end end end vagrant-2.0.2/plugins/providers/docker/driver/000077500000000000000000000000001323370221500214135ustar00rootroot00000000000000vagrant-2.0.2/plugins/providers/docker/driver/compose.rb000066400000000000000000000246551323370221500234210ustar00rootroot00000000000000require "json" require "log4r" module VagrantPlugins module DockerProvider class Driver class Compose < Driver # @return [Integer] Maximum number of seconds to wait for lock LOCK_TIMEOUT = 60 # @return [String] Compose file format version COMPOSE_VERSION = "2".freeze # @return [Pathname] data directory to store composition attr_reader :data_directory # @return [Vagrant::Machine] attr_reader :machine # Create a new driver instance # # @param [Vagrant::Machine] machine Machine instance for this driver def initialize(machine) if !Vagrant::Util::Which.which("docker-compose") raise Errors::DockerComposeNotInstalledError end super() @machine = machine @data_directory = Pathname.new(machine.env.local_data_path). join("docker-compose") @data_directory.mkpath @logger = Log4r::Logger.new("vagrant::docker::driver::compose") @compose_lock = Mutex.new @logger.debug("Docker compose driver initialize for machine `#{@machine.name}` (`#{@machine.id}`)") @logger.debug("Data directory for composition file `#{@data_directory}`") end def build(dir, **opts, &block) name = machine.name.to_s @logger.debug("Applying build for `#{name}` using `#{dir}` directory.") begin update_composition do |composition| services = composition["services"] ||= {} services[name] ||= {} services[name]["build"] = {"context" => dir} # Extract custom dockerfile location if set if opts[:extra_args] && opts[:extra_args].include?("--file") 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] when Array if opts[:build_args].include?("--build-arg") idx = 0 build_args = {} while(idx < opts[:build_args].size) arg_value = opts[:build_args][idx] idx += 1 if arg_value.start_with?("--build-arg") if !arg_value.include?("=") arg_value = opts[:build_args][idx] idx += 1 end key, val = arg_value.to_s.split("=", 2).to_s.split("=") build_args[key] = val end end end when Hash services[name]["build"]["args"] = opts[:build_args] end end rescue => error @logger.error("Failed to apply build using `#{dir}` directory: #{error.class} - #{error}") update_composition do |composition| composition["services"].delete(name) end raise end end def create(params, **opts, &block) # NOTE: Use the direct machine name as we don't # need to worry about uniqueness with compose name = machine.name.to_s image = params.fetch(:image) links = Array(params.fetch(:links, [])).map do |link| case link when Array link else link.to_s.split(":") end end ports = Array(params[:ports]) volumes = Array(params[:volumes]).map do |v| v = v.to_s host, guest = v.split(":", 2) if v.include?(":") && (Vagrant::Util::Platform.windows? || Vagrant::Util::Platform.wsl?) host = Vagrant::Util::Platform.windows_path(host) # NOTE: Docker does not support UNC style paths (which also # means that there's no long path support). Hopefully this # 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 "#{host}:#{guest}" end cmd = Array(params.fetch(:cmd)) env = Hash[*params.fetch(:env).flatten.map(&:to_s)] expose = Array(params[:expose]) @logger.debug("Creating container `#{name}`") begin update_args = [:apply] update_args.push(:detach) if params[:detach] update_args << block update_composition(*update_args) do |composition| services = composition["services"] ||= {} services[name] ||= {} if params[:extra_args].is_a?(Hash) services[name].merge!( Hash[ params[:extra_args].map{ |k, v| [k.to_s, v] } ] ) end services[name].merge!( "environment" => env, "expose" => expose, "ports" => ports, "volumes" => volumes, "links" => links, "command" => cmd ) services[name]["image"] = image if image services[name]["hostname"] = params[:hostname] if params[:hostname] services[name]["privileged"] = true if params[:privileged] services[name]["pty"] = true if params[:pty] end rescue => error @logger.error("Failed to create container `#{name}`: #{error.class} - #{error}") update_composition do |composition| composition["services"].delete(name) end raise end get_container_id(name) end def rm(cid) if created?(cid) destroy = false synchronized do compose_execute("rm", "-f", machine.name.to_s) update_composition do |composition| if composition["services"] && composition["services"].key?(machine.name.to_s) @logger.info("Removing container `#{machine.name}`") if composition["services"].size > 1 composition["services"].delete(machine.name.to_s) else destroy = true end end end if destroy @logger.info("No containers remain. Destroying full environment.") compose_execute("down", "--volumes", "--rmi", "local") @logger.info("Deleting composition path `#{composition_path}`") composition_path.delete end end end end def rmi(*_) true end def created?(cid) result = super if !result composition = get_composition if composition["services"] && composition["services"].has_key?(machine.name.to_s) result = true end end result end private # Lookup the ID for the container with the given name # # @param [String] name Name of container # @return [String] Container ID def get_container_id(name) compose_execute("ps", "-q", name).chomp end # Execute a `docker-compose` command def compose_execute(*cmd, **opts, &block) synchronized do execute("docker-compose", "-f", composition_path.to_s, "-p", machine.env.cwd.basename.to_s, *cmd, **opts, &block) end end # Apply any changes made to the composition def apply_composition!(*args) block = args.detect{|arg| arg.is_a?(Proc) } execute_args = ["up", "--remove-orphans"] if args.include?(:detach) execute_args << "-d" end machine.env.lock("compose", retry: true) do if block compose_execute(*execute_args, &block) else compose_execute(*execute_args) end end end # Update the composition and apply changes if requested # # @param [Boolean] apply Apply composition changes def update_composition(*args) synchronized do machine.env.lock("compose", retry: true) do composition = get_composition result = yield composition write_composition(composition) if args.include?(:apply) || (args.include?(:conditional) && result) apply_composition!(*args) end end end end # @return [Hash] current composition contents def get_composition composition = {"version" => COMPOSE_VERSION.dup} if composition_path.exist? composition = Vagrant::Util::DeepMerge.deep_merge(composition, YAML.load(composition_path.read)) end composition = Vagrant::Util::DeepMerge.deep_merge(composition, machine.provider_config.compose_configuration.dup) @logger.debug("Fetched composition with provider configuration applied: #{composition}") composition end # Save the composition # # @param [Hash] composition New composition def write_composition(composition) @logger.debug("Saving composition to `#{composition_path}`: #{composition}") tmp_file = Tempfile.new("vagrant-docker-compose") tmp_file.write(composition.to_yaml) tmp_file.close synchronized do FileUtils.mv(tmp_file.path, composition_path.to_s) end end # @return [Pathname] path to the docker-compose.yml file def composition_path data_directory.join("docker-compose.yml") end def synchronized if !@compose_lock.owned? timeout = LOCK_TIMEOUT.to_f until @compose_lock.owned? if @compose_lock.try_lock if timeout > 0 timeout -= sleep(1) else raise Errors::ComposeLockTimeoutError end end end got_lock = true end begin result = yield ensure @compose_lock.unlock if got_lock end result end end end end end vagrant-2.0.2/plugins/providers/docker/errors.rb000066400000000000000000000034361323370221500217670ustar00rootroot00000000000000module VagrantPlugins module DockerProvider module Errors class DockerError < Vagrant::Errors::VagrantError error_namespace("docker_provider.errors") end class CommunicatorNonDocker < DockerError error_key(:communicator_non_docker) end class ComposeLockTimeoutError < DockerError error_key(:compose_lock_timeout) end class ContainerNotRunningError < DockerError error_key(:not_running) end class ContainerNotCreatedError < DockerError error_key(:not_created) end class DockerComposeNotInstalledError < DockerError error_key(:docker_compose_not_installed) end class ExecuteError < DockerError error_key(:execute_error) end class ExecCommandRequired < DockerError error_key(:exec_command_required) end class HostVMCommunicatorNotReady < DockerError error_key(:host_vm_communicator_not_ready) end class ImageNotConfiguredError < DockerError error_key(:docker_provider_image_not_configured) end class NfsWithoutPrivilegedError < DockerError error_key(:docker_provider_nfs_without_privileged) end class PackageNotSupported < DockerError error_key(:package_not_supported) end class StateNotRunning < DockerError error_key(:state_not_running) end class StateStopped < DockerError error_key(:state_stopped) end class SuspendNotSupported < DockerError error_key(:suspend_not_supported) end class SyncedFolderNonDocker < DockerError error_key(:synced_folder_non_docker) end class VagrantfileNotFound < DockerError error_key(:vagrantfile_not_found) end end end end vagrant-2.0.2/plugins/providers/docker/executor/000077500000000000000000000000001323370221500217565ustar00rootroot00000000000000vagrant-2.0.2/plugins/providers/docker/executor/local.rb000066400000000000000000000020621323370221500233750ustar00rootroot00000000000000require "vagrant/util/busy" require "vagrant/util/subprocess" module VagrantPlugins module DockerProvider module Executor # The Local executor executes a Docker client that is running # locally. class Local def execute(*cmd, **opts, &block) # Append in the options for subprocess cmd << { notify: [:stdout, :stderr] } interrupted = false int_callback = ->{ interrupted = true } result = ::Vagrant::Util::Busy.busy(int_callback) do ::Vagrant::Util::Subprocess.execute(*cmd, &block) end result.stderr.gsub!("\r\n", "\n") result.stdout.gsub!("\r\n", "\n") if result.exit_code != 0 && !interrupted raise Errors::ExecuteError, command: cmd.inspect, stderr: result.stderr, stdout: result.stdout end result.stdout end def windows? ::Vagrant::Util::Platform.windows? || ::Vagrant::Util::Platform.wsl? end end end end end vagrant-2.0.2/plugins/providers/docker/executor/vagrant.rb000066400000000000000000000046061323370221500237530ustar00rootroot00000000000000require "vagrant/util/shell_quote" module VagrantPlugins module DockerProvider module Executor # The Vagrant executor runs Docker over SSH against the given # Vagrant-managed machine. class Vagrant def initialize(host_machine) @host_machine = host_machine end def execute(*cmd, **opts, &block) quote = '"' cmd = cmd.map do |a| "#{quote}#{::Vagrant::Util::ShellQuote.escape(a, quote)}#{quote}" end.join(" ") # If we want stdin, we just run in a full subprocess return ssh_run(cmd) if opts[:stdin] # Add a start fence so we know when to start reading output. # We have to do this because boot2docker outputs a login shell # boot2docker version that we get otherwise and messes up output. start_fence = "========== VAGRANT DOCKER BEGIN ==========" ssh_cmd = "echo -n \"#{start_fence}\"; #{cmd}" stderr = "" stdout = "" fenced = false comm = @host_machine.communicate code = comm.execute(ssh_cmd, error_check: false) do |type, data| next if ![:stdout, :stderr].include?(type) stderr << data if type == :stderr stdout << data if type == :stdout if !fenced index = stdout.index(start_fence) if index fenced = true index += start_fence.length stdout = stdout[index..-1] stdout.chomp! # We're now fenced, send all the data through if block block.call(:stdout, stdout) if stdout != "" block.call(:stderr, stderr) if stderr != "" end end else # If we're already fenced, just send the data through. block.call(type, data) if block && fenced end end if code != 0 raise Errors::ExecuteError, command: cmd, stderr: stderr.chomp, stdout: stdout.chomp end stdout.chomp end def windows? false end protected def ssh_run(cmd) @host_machine.action( :ssh_run, ssh_run_command: cmd, ) "" end end end end end vagrant-2.0.2/plugins/providers/docker/hostmachine/000077500000000000000000000000001323370221500224225ustar00rootroot00000000000000vagrant-2.0.2/plugins/providers/docker/hostmachine/Vagrantfile000066400000000000000000000001211323370221500246010ustar00rootroot00000000000000Vagrant.configure("2") do |config| config.vm.box = "hashicorp/boot2docker" end vagrant-2.0.2/plugins/providers/docker/plugin.rb000066400000000000000000000040201323370221500217370ustar00rootroot00000000000000module VagrantPlugins module DockerProvider autoload :Action, File.expand_path("../action", __FILE__) autoload :Driver, File.expand_path("../driver", __FILE__) autoload :Errors, File.expand_path("../errors", __FILE__) module Executor autoload :Local, File.expand_path("../executor/local", __FILE__) autoload :Vagrant, File.expand_path("../executor/vagrant", __FILE__) end class Plugin < Vagrant.plugin("2") name "docker-provider" description <<-EOF The Docker provider allows Vagrant to manage and control Docker containers. EOF provider(:docker, box_optional: true, parallel: true, defaultable: false) do require_relative 'provider' init! Provider end command("docker-exec", primary: false) do require_relative "command/exec" init! Command::Exec end command("docker-logs", primary: false) do require_relative "command/logs" init! Command::Logs end command("docker-run", primary: false) do require_relative "command/run" init! Command::Run end communicator(:docker_hostvm) do require_relative "communicator" init! Communicator end config(:docker, :provider) do require_relative 'config' init! Config end synced_folder(:docker) do require File.expand_path("../synced_folder", __FILE__) SyncedFolder end provider_capability("docker", "public_address") do require_relative "cap/public_address" Cap::PublicAddress end provider_capability("docker", "proxy_machine") do require_relative "cap/proxy_machine" Cap::ProxyMachine end protected def self.init! return if defined?(@_init) I18n.load_path << File.expand_path( "templates/locales/providers_docker.yml", Vagrant.source_root) I18n.reload! @_init = true end end end end vagrant-2.0.2/plugins/providers/docker/provider.rb000066400000000000000000000146621323370221500223100ustar00rootroot00000000000000require "digest/md5" require "fileutils" require "thread" require "log4r" require "vagrant/util/silence_warnings" module VagrantPlugins module DockerProvider class Provider < Vagrant.plugin("2", :provider) @@host_vm_mutex = Mutex.new def initialize(machine) @logger = Log4r::Logger.new("vagrant::provider::docker") @machine = machine if host_vm? # We need to use a special communicator that proxies our # SSH requests over our host VM to the container itself. @machine.config.vm.communicator = :docker_hostvm end end # @see Vagrant::Plugin::V2::Provider#action def action(name) action_method = "action_#{name}" return Action.send(action_method) if Action.respond_to?(action_method) nil end # Returns the driver instance for this provider. def driver if !@driver if @machine.provider_config.compose @driver = Driver::Compose.new(@machine) else @driver = Driver.new end end if host_vm? @driver.executor = Executor::Vagrant.new(host_vm) end @driver end # This returns the {Vagrant::Machine} that is our host machine. # It does not perform any action on the machine or verify it is # running. # # @return [Vagrant::Machine] def host_vm return @host_vm if @host_vm vf_path = @machine.provider_config.vagrant_vagrantfile host_machine_name = @machine.provider_config.vagrant_machine || :default if !vf_path # We don't have a Vagrantfile path set, so we're going to use # the default but we need to copy it into the data dir so that # we don't write into our installation dir (we can't). default_path = File.expand_path("../hostmachine/Vagrantfile", __FILE__) vf_path = @machine.env.data_dir.join("docker-host", "Vagrantfile") begin @machine.env.lock("docker-provider-hostvm") do vf_path.dirname.mkpath FileUtils.cp(default_path, vf_path) end rescue Vagrant::Errors::EnvironmentLockedError # Lock contention, just retry retry end # Set the machine name since we hardcode that for the default host_machine_name = :default end # Expand it so that the home directories and so on get processed # properly. vf_path = File.expand_path(vf_path, @machine.env.root_path) vf_file = File.basename(vf_path) vf_path = File.dirname(vf_path) # Create the env to manage this machine @host_vm = Vagrant::Util::SilenceWarnings.silence! do host_env = Vagrant::Environment.new( cwd: vf_path, home_path: @machine.env.home_path, ui_class: @machine.env.ui_class, vagrantfile_name: vf_file, ) # If there is no root path, then the Vagrantfile wasn't found # and it is an error... raise Errors::VagrantfileNotFound if !host_env.root_path host_env.machine( host_machine_name, host_env.default_provider( exclude: [:docker], force_default: false, )) end @host_vm end # This acquires a lock on the host VM. def host_vm_lock hash = Digest::MD5.hexdigest(host_vm.data_dir.to_s) # We do a process-level mutex on the outside, since we can # wait for that a short amount of time. Then, we do a process lock # on the inside, which will raise an exception if locked. host_vm_mutex.synchronize do @machine.env.lock(hash) do return yield end end end # This is a process-local mutex that can be used by parallel # providers to lock the host VM access. def host_vm_mutex @@host_vm_mutex end # This says whether or not Docker will be running within a VM # rather than directly on our system. Docker needs to run in a VM # when we're not on Linux, or not on a Linux that supports Docker. def host_vm? @machine.provider_config.force_host_vm end # Returns the SSH info for accessing the Container. def ssh_info # If the container isn't running, we can't SSH into it return nil if state.id != :running port_name = "#{@machine.config.ssh.guest_port}/tcp" network = driver.inspect_container(@machine.id)['NetworkSettings'] if network["Ports"][port_name].respond_to?(:first) port_info = network["Ports"][port_name].first else ip = network["IPAddress"] port = @machine.config.ssh.guest_port if !ip.to_s.empty? port_info = { "HostIp" => ip, "HostPort" => port } end end # If we were not able to identify the container's IP, we return nil # here and we let Vagrant core deal with it ;) return nil if port_info.nil? || port_info.empty? { host: port_info['HostIp'], port: port_info['HostPort'] } end def state state_id = nil state_id = :not_created if !@machine.id begin state_id = :host_state_unknown if !state_id && \ host_vm? && !host_vm.communicate.ready? rescue Errors::VagrantfileNotFound state_id = :host_state_unknown end state_id = :not_created if !state_id && \ (!@machine.id || !driver.created?(@machine.id)) state_id = driver.state(@machine.id) if @machine.id && !state_id state_id = :unknown if !state_id # This is a special pseudo-state so that we don't set the # NOT_CREATED_ID while we're setting up the machine. This avoids # clearing the data dir. state_id = :preparing if @machine.id == "preparing" short = state_id.to_s.gsub("_", " ") long = I18n.t("docker_provider.status.#{state_id}") # If we're not created, then specify the special ID flag if state_id == :not_created state_id = Vagrant::MachineState::NOT_CREATED_ID end Vagrant::MachineState.new(state_id, short, long) end def to_s id = @machine.id ? @machine.id : "new container" "Docker (#{id})" end end end end vagrant-2.0.2/plugins/providers/docker/synced_folder.rb000066400000000000000000000014001323370221500232600ustar00rootroot00000000000000module VagrantPlugins module DockerProvider class SyncedFolder < Vagrant.plugin("2", :synced_folder) def usable?(machine, raise_error=false) # These synced folders only work if the provider is Docker if machine.provider_name != :docker if raise_error raise Errors::SyncedFolderNonDocker, provider: machine.provider_name.to_s end return false end true end def prepare(machine, folders, _opts) folders.each do |id, data| next if data[:ignore] host_path = data[:hostpath] guest_path = data[:guestpath] machine.provider_config.volumes << "#{host_path}:#{guest_path}" end end end end end vagrant-2.0.2/plugins/providers/hyperv/000077500000000000000000000000001323370221500201665ustar00rootroot00000000000000vagrant-2.0.2/plugins/providers/hyperv/action.rb000066400000000000000000000220141323370221500217670ustar00rootroot00000000000000require "pathname" require "vagrant/action/builder" module VagrantPlugins module HyperV module Action # Include the built-in modules so we can use them as top-level things. include Vagrant::Action::Builtin def self.action_reload Vagrant::Action::Builder.new.tap do |b| b.use ConfigValidate b.use Call, IsState, :not_created do |env, b2| if env[:result] b2.use Message, I18n.t("vagrant_hyperv.message_not_created") next end b2.use action_halt b2.use action_start end end end def self.action_destroy Vagrant::Action::Builder.new.tap do |b| b.use Call, IsState, :not_created do |env1, b1| if env1[:result] b1.use Message, I18n.t("vagrant_hyperv.message_not_created") next end b1.use Call, DestroyConfirm do |env2, b2| if !env2[:result] b2.use MessageWillNotDestroy next end b2.use ConfigValidate b2.use ProvisionerCleanup, :before b2.use StopInstance b2.use DeleteVM b2.use SyncedFolderCleanup end end end end def self.action_halt Vagrant::Action::Builder.new.tap do |b| b.use ConfigValidate b.use Call, IsState, :not_created do |env, b2| if env[:result] b2.use Message, I18n.t("vagrant_hyperv.message_not_created") next end b2.use Call, GracefulHalt, :off, :running do |env2, b3| if !env2[:result] b3.use StopInstance end end end end end # This action packages the virtual machine into a single box file. def self.action_package Vagrant::Action::Builder.new.tap do |b| b.use CheckEnabled b.use Call, IsState, :not_created do |env1, b2| if env1[:result] b2.use Message, I18n.t("vagrant_hyperv.message_not_created") next end b2.use PackageSetupFolders b2.use PackageSetupFiles b2.use action_halt b2.use SyncedFolderCleanup b2.use Package b2.use PackageVagrantfile b2.use PackageMetadataJson b2.use Export end end end def self.action_provision Vagrant::Action::Builder.new.tap do |b| b.use ConfigValidate b.use Call, IsState, :not_created do |env, b2| if env[:result] b2.use Message, I18n.t("vagrant_hyperv.message_not_created") next end b2.use Call, IsState, :running do |env1, b3| if !env1[:result] b3.use Message, I18n.t("vagrant_hyperv.message_not_running") next end b3.use Provision end end end end def self.action_resume Vagrant::Action::Builder.new.tap do |b| b.use HandleBox b.use ConfigValidate b.use Call, IsState, :not_created do |env, b1| if env[:result] b1.use Message, I18n.t("vagrant_hyperv.message_not_created") next end b1.use ResumeVM b1.use WaitForIPAddress b1.use WaitForCommunicator, [:running] end end end def self.action_start Vagrant::Action::Builder.new.tap do |b| b.use Call, IsState, :running do |env1, b1| if env1[:result] b1.use action_provision next end b1.use Call, IsState, :paused do |env2, b2| if env2[:result] b2.use action_resume next end b2.use Provision b2.use NetSetVLan b2.use NetSetMac b2.use StartInstance b2.use WaitForIPAddress b2.use WaitForCommunicator, [:running] b2.use SyncedFolderCleanup b2.use SyncedFolders b2.use SetHostname end end end end def self.action_up Vagrant::Action::Builder.new.tap do |b| b.use CheckEnabled b.use HandleBox b.use ConfigValidate b.use Call, IsState, :not_created do |env1, b1| if env1[:result] b1.use Import end b1.use action_start end end end def self.action_read_state Vagrant::Action::Builder.new.tap do |b| b.use ConfigValidate b.use ReadState end end def self.action_ssh Vagrant::Action::Builder.new.tap do |b| b.use ConfigValidate b.use Call, IsState, :not_created do |env, b2| if env[:result] raise Vagrant::Errors::VMNotCreatedError end b2.use Call, IsState, :running do |env1, b3| if !env1[:result] raise Vagrant::Errors::VMNotRunningError end b3.use SSHExec end end end end def self.action_ssh_run Vagrant::Action::Builder.new.tap do |b| b.use ConfigValidate b.use Call, IsState, :not_created do |env, b2| if env[:result] raise Vagrant::Errors::VMNotCreatedError end b2.use Call, IsState, :running do |env1, b3| if !env1[:result] raise Vagrant::Errors::VMNotRunningError end b3.use SSHRun end end end end def self.action_suspend Vagrant::Action::Builder.new.tap do |b| b.use ConfigValidate b.use Call, IsState, :not_created do |env, b2| if env[:result] b2.use Message, I18n.t("vagrant_hyperv.message_not_created") next end b2.use SuspendVM end end end def self.action_snapshot_delete Vagrant::Action::Builder.new.tap do |b| b.use ConfigValidate b.use Call, IsState, :not_created do |env, b2| if env[:result] b2.use Message, I18n.t("vagrant_hyperv.message_not_created") next end b2.use SnapshotDelete end end end def self.action_snapshot_restore Vagrant::Action::Builder.new.tap do |b| b.use ConfigValidate b.use Call, IsState, :not_created do |env, b2| if env[:result] b2.use Message, I18n.t("vagrant_hyperv.message_not_created") next end b2.use action_halt b2.use SnapshotRestore b2.use Call, IsEnvSet, :snapshot_delete do |env2, b3| if env2[:result] b3.use action_snapshot_delete end end b2.use action_start end end end def self.action_snapshot_save Vagrant::Action::Builder.new.tap do |b| b.use ConfigValidate b.use Call, IsState, :not_created do |env, b2| if env[:result] b2.use Message, I18n.t("vagrant_hyperv.message_not_created") next end b2.use SnapshotSave end end end # The autoload farm action_root = Pathname.new(File.expand_path("../action", __FILE__)) autoload :PackageSetupFolders, action_root.join("package_setup_folders") autoload :PackageSetupFiles, action_root.join("package_setup_files") autoload :PackageVagrantfile, action_root.join("package_vagrantfile") autoload :PackageMetadataJson, action_root.join("package_metadata_json") autoload :Export, action_root.join("export") autoload :CheckEnabled, action_root.join("check_enabled") autoload :DeleteVM, action_root.join("delete_vm") autoload :Import, action_root.join("import") autoload :Package, action_root.join("package") autoload :IsWindows, action_root.join("is_windows") autoload :ReadState, action_root.join("read_state") autoload :ResumeVM, action_root.join("resume_vm") autoload :StartInstance, action_root.join('start_instance') autoload :StopInstance, action_root.join('stop_instance') autoload :SuspendVM, action_root.join("suspend_vm") autoload :WaitForIPAddress, action_root.join("wait_for_ip_address") autoload :NetSetVLan, action_root.join("net_set_vlan") autoload :NetSetMac, action_root.join("net_set_mac") autoload :MessageWillNotDestroy, action_root.join("message_will_not_destroy") autoload :SnapshotDelete, action_root.join("snapshot_delete") autoload :SnapshotRestore, action_root.join("snapshot_restore") autoload :SnapshotSave, action_root.join("snapshot_save") end end end vagrant-2.0.2/plugins/providers/hyperv/action/000077500000000000000000000000001323370221500214435ustar00rootroot00000000000000vagrant-2.0.2/plugins/providers/hyperv/action/check_enabled.rb000066400000000000000000000007531323370221500245240ustar00rootroot00000000000000require "fileutils" require "log4r" module VagrantPlugins module HyperV module Action class CheckEnabled def initialize(app, env) @app = app end def call(env) env[:ui].output("Verifying Hyper-V is enabled...") result = env[:machine].provider.driver.execute("check_hyperv.ps1", {}) raise Errors::PowerShellFeaturesDisabled if !result["result"] @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/delete_vm.rb000066400000000000000000000005141323370221500237340ustar00rootroot00000000000000module VagrantPlugins module HyperV module Action class DeleteVM def initialize(app, env) @app = app end def call(env) env[:ui].info("Deleting the machine...") env[:machine].provider.driver.delete_vm @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/export.rb000066400000000000000000000015631323370221500233160ustar00rootroot00000000000000require "fileutils" module VagrantPlugins module HyperV module Action class Export def initialize(app, env) @app = app end def call(env) @env = env @env[:ui].info @env[:machine].state.id.to_s raise Vagrant::Errors::VMPowerOffToPackage if @env[:machine].state.id != :off export @app.call(env) end def export @env[:ui].info I18n.t("vagrant.actions.vm.export.exporting") @env[:machine].provider.driver.export(@env["export.temp_dir"]) do |progress| @env[:ui].clear_line @env[:ui].report_progress(progress.percent, 100, false) end # Clear the line a final time so the next data can appear # alone on the line. @env[:ui].clear_line end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/import.rb000066400000000000000000000156771323370221500233220ustar00rootroot00000000000000require "fileutils" require "log4r" module VagrantPlugins module HyperV module Action class Import def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::hyperv::import") end def call(env) vm_dir = env[:machine].box.directory.join("Virtual Machines") hd_dir = env[:machine].box.directory.join("Virtual Hard Disks") memory = env[:machine].provider_config.memory maxmemory = env[:machine].provider_config.maxmemory cpus = env[:machine].provider_config.cpus vmname = env[:machine].provider_config.vmname differencing_disk = env[:machine].provider_config.differencing_disk auto_start_action = env[:machine].provider_config.auto_start_action auto_stop_action = env[:machine].provider_config.auto_stop_action enable_virtualization_extensions = env[:machine].provider_config.enable_virtualization_extensions vm_integration_services = env[:machine].provider_config.vm_integration_services env[:ui].output("Configured Dynamic memory allocation, maxmemory is #{maxmemory}") if maxmemory env[:ui].output("Configured startup memory is #{memory}") if memory env[:ui].output("Configured cpus number is #{cpus}") if cpus env[:ui].output("Configured enable virtualization extensions is #{enable_virtualization_extensions}") if enable_virtualization_extensions env[:ui].output("Configured vmname is #{vmname}") if vmname env[:ui].output("Configured differencing disk instead of cloning") if differencing_disk env[:ui].output("Configured automatic start action is #{auto_start_action}") if auto_start_action env[:ui].output("Configured automatic stop action is #{auto_stop_action}") if auto_stop_action if !vm_dir.directory? || !hd_dir.directory? raise Errors::BoxInvalid end config_path = nil config_type = nil vm_dir.each_child do |f| if f.extname.downcase == '.xml' @logger.debug("Found XML config...") config_path = f config_type = 'xml' break end end vmcx_support = env[:machine].provider.driver.execute("has_vmcx_support.ps1", {})['result'] if vmcx_support vm_dir.each_child do |f| if f.extname.downcase == '.vmcx' @logger.debug("Found VMCX config and support...") config_path = f config_type = 'vmcx' break end end end image_path = nil image_ext = nil image_filename = nil hd_dir.each_child do |f| if %w{.vhd .vhdx}.include?(f.extname.downcase) image_path = f image_ext = f.extname.downcase image_filename = File.basename(f, image_ext) break end end if !config_path || !image_path raise Errors::BoxInvalid end env[:ui].output("Importing a Hyper-V instance") switches = env[:machine].provider.driver.execute("get_switches.ps1", {}) raise Errors::NoSwitches if switches.empty? switch = nil env[:machine].config.vm.networks.each do |type, opts| next if type != :public_network && type != :private_network switchToFind = opts[:bridge] if switchToFind puts "Looking for switch with name: #{switchToFind}" switch = switches.find { |s| s["Name"].downcase == switchToFind.downcase }["Name"] puts "Found switch: #{switch}" end end if switch.nil? if switches.length > 1 env[:ui].detail(I18n.t("vagrant_hyperv.choose_switch") + "\n ") switches.each_index do |i| switch = switches[i] env[:ui].detail("#{i+1}) #{switch["Name"]}") end env[:ui].detail(" ") switch = nil while !switch switch = env[:ui].ask("What switch would you like to use? ") next if !switch switch = switch.to_i - 1 switch = nil if switch < 0 || switch >= switches.length end switch = switches[switch]["Name"] else switch = switches[0]["Name"] end end env[:ui].detail("Cloning virtual hard drive...") source_path = image_path.to_s dest_path = env[:machine].data_dir.join("Virtual Hard Disks").join("#{image_filename}#{image_ext}").to_s # Still hard copy the disk of old XML configurations if config_type == 'xml' if differencing_disk env[:machine].provider.driver.execute("clone_vhd.ps1", {Source: source_path, Destination: dest_path}) else FileUtils.mkdir_p(env[:machine].data_dir.join("Virtual Hard Disks")) FileUtils.cp(source_path, dest_path) end end image_path = dest_path # We have to normalize the paths to be Windows paths since # we're executing PowerShell. options = { vm_config_file: config_path.to_s.gsub("/", "\\"), vm_config_type: config_type, source_path: source_path.to_s, dest_path: dest_path, data_path: env[:machine].data_dir.to_s.gsub("/", "\\") } options[:switchname] = switch if switch options[:memory] = memory if memory options[:maxmemory] = maxmemory if maxmemory options[:cpus] = cpus if cpus options[:vmname] = vmname if vmname options[:auto_start_action] = auto_start_action if auto_start_action options[:auto_stop_action] = auto_stop_action if auto_stop_action options[:differencing_disk] = differencing_disk if differencing_disk options[:enable_virtualization_extensions] = "True" if enable_virtualization_extensions and enable_virtualization_extensions == true env[:ui].detail("Creating and registering the VM...") server = env[:machine].provider.driver.import(options) env[:ui].detail("Setting VM Integration Services") vm_integration_services.each do |key, value| state = false if value === true state = "enabled" elsif value === false state = "disabled" end env[:ui].output("#{key} is #{state}") if state end vm_integration_services[:VmId] = server["id"] env[:machine].provider.driver.set_vm_integration_services(vm_integration_services) env[:ui].detail("Successfully imported a VM with name: #{server['name']}") env[:machine].id = server["id"] @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/is_windows.rb000066400000000000000000000004561323370221500241620ustar00rootroot00000000000000module VagrantPlugins module HyperV module Action class IsWindows def initialize(app, env) @app = app end def call(env) env[:result] = env[:machine].config.vm.guest == :windows @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/message_will_not_destroy.rb000066400000000000000000000005701323370221500270760ustar00rootroot00000000000000module VagrantPlugins module HyperV module Action class MessageWillNotDestroy def initialize(app, env) @app = app end def call(env) env[:ui].info I18n.t("vagrant.commands.destroy.will_not_destroy", name: env[:machine].name) @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/net_set_mac.rb000066400000000000000000000007171323370221500242560ustar00rootroot00000000000000module VagrantPlugins module HyperV module Action class NetSetMac def initialize(app, env) @app = app end def call(env) mac = env[:machine].provider_config.mac if mac env[:ui].info("[Settings] [Network Adapter] Setting MAC address to: #{mac}") env[:machine].provider.driver.net_set_mac(mac) end @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/net_set_vlan.rb000066400000000000000000000007421323370221500244540ustar00rootroot00000000000000module VagrantPlugins module HyperV module Action class NetSetVLan def initialize(app, env) @app = app end def call(env) vlan_id = env[:machine].provider_config.vlan_id if vlan_id env[:ui].info("[Settings] [Network Adapter] Setting Vlan ID to: #{vlan_id}") env[:machine].provider.driver.net_set_vlan(vlan_id) end @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/package.rb000066400000000000000000000006401323370221500233630ustar00rootroot00000000000000require_relative "../../../../lib/vagrant/action/general/package" module VagrantPlugins module HyperV module Action class Package < Vagrant::Action::General::Package # Doing this so that we can test that the parent is properly # called in the unit tests. alias_method :general_call, :call def call(env) general_call(env) end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/package_metadata_json.rb000066400000000000000000000013361323370221500262570ustar00rootroot00000000000000require "json" #require 'vagrant/util/template_renderer' module VagrantPlugins module HyperV module Action class PackageMetadataJson # For TemplateRenderer include Vagrant::Util def initialize(app, env) @app = app end def call(env) @env = env create_metadata @app.call(env) end # This method creates a metadata.json file to tell vagrant this is a # Hyper V box def create_metadata File.open(File.join(@env["export.temp_dir"], "metadata.json"), "w") do |f| f.write(JSON.generate({ provider: "hyperv" })) end end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/package_setup_files.rb000066400000000000000000000007001323370221500257620ustar00rootroot00000000000000require_relative "../../../../lib/vagrant/action/general/package_setup_files" module VagrantPlugins module HyperV module Action class PackageSetupFiles < Vagrant::Action::General::PackageSetupFiles # Doing this so that we can test that the parent is properly # called in the unit tests. alias_method :general_call, :call def call(env) general_call(env) end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/package_setup_folders.rb000066400000000000000000000007331323370221500263240ustar00rootroot00000000000000require "fileutils" require_relative "../../../../lib/vagrant/action/general/package_setup_folders" module VagrantPlugins module HyperV module Action class PackageSetupFolders < Vagrant::Action::General::PackageSetupFolders # Doing this so that we can test that the parent is properly # called in the unit tests. alias_method :general_call, :call def call(env) general_call(env) end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/package_vagrantfile.rb000066400000000000000000000016461323370221500257540ustar00rootroot00000000000000require 'vagrant/util/template_renderer' module VagrantPlugins module HyperV module Action class PackageVagrantfile # For TemplateRenderer include Vagrant::Util def initialize(app, env) @app = app end def call(env) @env = env create_vagrantfile @app.call(env) end # This method creates the auto-generated Vagrantfile at the root of the # box. This Vagrantfile contains the MAC address so that the user doesn't # have to worry about it. def create_vagrantfile File.open(File.join(@env["export.temp_dir"], "Vagrantfile"), "w") do |f| mac_address = @env[:machine].provider.driver.read_mac_address f.write(TemplateRenderer.render("package_Vagrantfile", { base_mac: mac_address["mac"] })) end end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/read_guest_ip.rb000066400000000000000000000022751323370221500246100ustar00rootroot00000000000000require "log4r" require "timeout" module VagrantPlugins module HyperV module Action # This action reads the SSH info for the machine and puts it into the # `:machine_ssh_info` key in the environment. class ReadGuestIP def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::hyperv::connection") end def call(env) env[:machine_ssh_info] = read_host_ip(env) @app.call(env) end def read_host_ip(env) return nil if env[:machine].id.nil? # Get Network details from WMI Provider # Wait for 120 sec By then the machine should be ready host_ip = nil begin Timeout.timeout(120) do begin network_info = env[:machine].provider.driver.read_guest_ip host_ip = network_info["ip"] sleep 10 if host_ip.empty? end while host_ip.empty? end rescue Timeout::Error @logger.info("Cannot find the IP address of the virtual machine") end return { host: host_ip } unless host_ip.nil? end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/read_state.rb000066400000000000000000000014311323370221500241020ustar00rootroot00000000000000 require "log4r" module VagrantPlugins module HyperV module Action class ReadState def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::hyperv::connection") end def call(env) if env[:machine].id response = env[:machine].provider.driver.get_current_state env[:machine_state_id] = response["state"].downcase.to_sym # If the machine isn't created, then our ID is stale, so just # mark it as not created. if env[:machine_state_id] == :not_created env[:machine].id = nil end else env[:machine_state_id] = :not_created end @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/resume_vm.rb000066400000000000000000000005321323370221500237720ustar00rootroot00000000000000module VagrantPlugins module HyperV module Action class ResumeVM def initialize(app, env) @app = app end def call(env) env[:ui].info("Resuming the machine...") env[:machine].provider.driver.resume @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/snapshot_delete.rb000066400000000000000000000010741323370221500251530ustar00rootroot00000000000000module VagrantPlugins module HyperV module Action class SnapshotDelete def initialize(app, env) @app = app end def call(env) env[:ui].info(I18n.t( "vagrant.actions.vm.snapshot.deleting", name: env[:snapshot_name])) env[:machine].provider.driver.delete_snapshot(env[:snapshot_name]) env[:ui].success(I18n.t( "vagrant.actions.vm.snapshot.deleted", name: env[:snapshot_name])) @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/snapshot_restore.rb000066400000000000000000000007001323370221500253670ustar00rootroot00000000000000module VagrantPlugins module HyperV module Action class SnapshotRestore def initialize(app, env) @app = app end def call(env) env[:ui].info(I18n.t( "vagrant.actions.vm.snapshot.restoring", name: env[:snapshot_name])) env[:machine].provider.driver.restore_snapshot(env[:snapshot_name]) @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/snapshot_save.rb000066400000000000000000000010661323370221500246500ustar00rootroot00000000000000module VagrantPlugins module HyperV module Action class SnapshotSave def initialize(app, env) @app = app end def call(env) env[:ui].info(I18n.t( "vagrant.actions.vm.snapshot.saving", name: env[:snapshot_name])) env[:machine].provider.driver.create_snapshot(env[:snapshot_name]) env[:ui].success(I18n.t( "vagrant.actions.vm.snapshot.saved", name: env[:snapshot_name])) @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/start_instance.rb000066400000000000000000000005171323370221500250140ustar00rootroot00000000000000module VagrantPlugins module HyperV module Action class StartInstance def initialize(app, env) @app = app end def call(env) env[:ui].output('Starting the machine...') env[:machine].provider.driver.start @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/stop_instance.rb000066400000000000000000000005161323370221500246430ustar00rootroot00000000000000module VagrantPlugins module HyperV module Action class StopInstance def initialize(app, env) @app = app end def call(env) env[:ui].info("Stopping the machine...") env[:machine].provider.driver.stop @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/suspend_vm.rb000066400000000000000000000005361323370221500241570ustar00rootroot00000000000000module VagrantPlugins module HyperV module Action class SuspendVM def initialize(app, env) @app = app end def call(env) env[:ui].info("Suspending the machine...") env[:machine].provider.driver.suspend @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/hyperv/action/wait_for_ip_address.rb000066400000000000000000000032141323370221500257770ustar00rootroot00000000000000require "ipaddr" require "timeout" module VagrantPlugins module HyperV module Action class WaitForIPAddress def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::hyperv::wait_for_ip_addr") end def call(env) timeout = env[:machine].provider_config.ip_address_timeout env[:ui].output("Waiting for the machine to report its IP address...") env[:ui].detail("Timeout: #{timeout} seconds") guest_ip = nil Timeout.timeout(timeout) do while true # If a ctrl-c came through, break out return if env[:interrupted] # Try to get the IP begin network_info = env[:machine].provider.driver.read_guest_ip guest_ip = network_info["ip"] if guest_ip begin IPAddr.new(guest_ip) break rescue IPAddr::InvalidAddressError # Ignore, continue looking. @logger.warn("Invalid IP address returned: #{guest_ip}") end end rescue Errors::PowerShellError # Ignore, continue looking. @logger.warn("Failed to read guest IP.") end sleep 1 end end # If we were interrupted then return now return if env[:interrupted] env[:ui].detail("IP: #{guest_ip}") @app.call(env) rescue Timeout::Error raise Errors::IPAddrTimeout end end end end end vagrant-2.0.2/plugins/providers/hyperv/cap/000077500000000000000000000000001323370221500207315ustar00rootroot00000000000000vagrant-2.0.2/plugins/providers/hyperv/cap/public_address.rb000066400000000000000000000004711323370221500242430ustar00rootroot00000000000000module VagrantPlugins module HyperV module Cap module PublicAddress def self.public_address(machine) return nil if machine.state.id != :running ssh_info = machine.ssh_info return nil if !ssh_info ssh_info[:host] end end end end end vagrant-2.0.2/plugins/providers/hyperv/cap/snapshot_list.rb000066400000000000000000000003201323370221500241430ustar00rootroot00000000000000module VagrantPlugins module HyperV module Cap module SnapshotList def self.snapshot_list(machine) machine.provider.driver.list_snapshots end end end end end vagrant-2.0.2/plugins/providers/hyperv/config.rb000066400000000000000000000060421323370221500217620ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HyperV class Config < Vagrant.plugin("2", :config) attr_accessor :ip_address_timeout # Time to wait for an IP address when booting, in seconds @return [Integer] attr_accessor :memory # Memory size in mb @return [Integer] attr_accessor :maxmemory # Maximal memory size in mb enables dynamical memory allocation @return [Integer] attr_accessor :cpus # Number of cpu's @return [Integer] attr_accessor :vmname # Name that will be shoen in Hyperv Manager @return [String] attr_accessor :vlan_id # VLAN ID for network interface for the virtual machine. @return [Integer] attr_accessor :mac # MAC address for network interface for the virtual machine. @return [String] attr_accessor :differencing_disk # Create differencing disk instead of cloning whole VHD [Boolean] attr_accessor :auto_start_action #action on automatic start of VM. Values: Nothing, StartIfRunning, Start attr_accessor :auto_stop_action #action on automatic stop of VM. Values: ShutDown, TurnOff, Save attr_accessor :enable_virtualization_extensions # Enable virtualization extensions (nested virtualization). Values: true, false attr_accessor :vm_integration_services # Options for VMServiceIntegration [Hash] def initialize @ip_address_timeout = UNSET_VALUE @memory = UNSET_VALUE @maxmemory = UNSET_VALUE @cpus = UNSET_VALUE @vmname = UNSET_VALUE @vlan_id = UNSET_VALUE @mac = UNSET_VALUE @differencing_disk = UNSET_VALUE @auto_start_action = UNSET_VALUE @auto_stop_action = UNSET_VALUE @enable_virtualization_extensions = UNSET_VALUE @vm_integration_services = { guest_service_interface: UNSET_VALUE, heartbeat: UNSET_VALUE, key_value_pair_exchange: UNSET_VALUE, shutdown: UNSET_VALUE, time_synchronization: UNSET_VALUE, vss: UNSET_VALUE } end def finalize! if @ip_address_timeout == UNSET_VALUE @ip_address_timeout = 120 end @memory = nil if @memory == UNSET_VALUE @maxmemory = nil if @maxmemory == UNSET_VALUE @cpus = nil if @cpus == UNSET_VALUE @vmname = nil if @vmname == UNSET_VALUE @vlan_id = nil if @vlan_id == UNSET_VALUE @mac = nil if @mac == UNSET_VALUE @differencing_disk = false if @differencing_disk == UNSET_VALUE @auto_start_action = nil if @auto_start_action == UNSET_VALUE @auto_stop_action = nil if @auto_stop_action == UNSET_VALUE @enable_virtualization_extensions = false if @enable_virtualization_extensions == UNSET_VALUE # TODO will this work? @vm_integration_services.each { |key, value| @vm_integration_services[key] = nil if value == UNSET_VALUE } @vm_integration_services = nil if @vm_integration_services.length == 0 end def validate(machine) errors = _detected_errors {"Hyper-V" => errors} end end end end vagrant-2.0.2/plugins/providers/hyperv/driver.rb000066400000000000000000000074061323370221500220150ustar00rootroot00000000000000require "json" require "vagrant/util/powershell" require_relative "plugin" module VagrantPlugins module HyperV class Driver ERROR_REGEXP = /===Begin-Error===(.+?)===End-Error===/m OUTPUT_REGEXP = /===Begin-Output===(.+?)===End-Output===/m attr_reader :vm_id def initialize(id) @vm_id = id end def execute(path, options) r = execute_powershell(path, options) if r.exit_code != 0 raise Errors::PowerShellError, script: path, stderr: r.stderr end # We only want unix-style line endings within Vagrant r.stdout.gsub!("\r\n", "\n") r.stderr.gsub!("\r\n", "\n") error_match = ERROR_REGEXP.match(r.stdout) output_match = OUTPUT_REGEXP.match(r.stdout) if error_match data = JSON.parse(error_match[1]) # We have some error data. raise Errors::PowerShellError, script: path, stderr: data["error"] end # Nothing return nil if !output_match return JSON.parse(output_match[1]) end def get_current_state execute('get_vm_status.ps1', { VmId: vm_id }) end def delete_vm execute('delete_vm.ps1', { VmId: vm_id }) end def export(path) execute('export_vm.ps1', {VmId: vm_id, Path: path}) end def read_guest_ip execute('get_network_config.ps1', { VmId: vm_id }) end def read_mac_address execute('get_network_mac.ps1', { VmId: vm_id }) end def resume execute('resume_vm.ps1', { VmId: vm_id }) end def start execute('start_vm.ps1', { VmId: vm_id }) end def stop execute('stop_vm.ps1', { VmId: vm_id }) end def suspend execute("suspend_vm.ps1", { VmId: vm_id }) end def import(options) config_type = options.delete(:vm_config_type) if config_type === "vmcx" execute('import_vm_vmcx.ps1', options) else options.delete(:data_path) options.delete(:source_path) options.delete(:differencing_disk) execute('import_vm_xml.ps1', options) end end def net_set_vlan(vlan_id) execute("set_network_vlan.ps1", { VmId: vm_id, VlanId: vlan_id }) end def net_set_mac(mac_addr) execute("set_network_mac.ps1", { VmId: vm_id, Mac: mac_addr }) end def create_snapshot(snapshot_name) execute("create_snapshot.ps1", { VmId: vm_id, SnapName: (snapshot_name) } ) end def restore_snapshot(snapshot_name) execute("restore_snapshot.ps1", { VmId: vm_id, SnapName: (snapshot_name) } ) end def list_snapshots() snaps = execute("list_snapshots.ps1", { VmID: vm_id } ) snaps.map { |s| s['Name'] } end def delete_snapshot(snapshot_name) execute("delete_snapshot.ps1", {VmID: vm_id, SnapName: snapshot_name}) end def set_vm_integration_services(config) execute("set_vm_integration_services.ps1", config) end protected def execute_powershell(path, options, &block) lib_path = Pathname.new(File.expand_path("../scripts", __FILE__)) path = lib_path.join(path).to_s.gsub("/", "\\") options = options || {} ps_options = [] options.each do |key, value| ps_options << "-#{key}" ps_options << "'#{value}'" end # Always have a stop error action for failures ps_options << "-ErrorAction" << "Stop" opts = { notify: [:stdout, :stderr, :stdin] } Vagrant::Util::PowerShell.execute(path, *ps_options, **opts, &block) end end end end vagrant-2.0.2/plugins/providers/hyperv/errors.rb000066400000000000000000000017031323370221500220300ustar00rootroot00000000000000module VagrantPlugins module HyperV module Errors # A convenient superclass for all our errors. class HyperVError < Vagrant::Errors::VagrantError error_namespace("vagrant_hyperv.errors") end class AdminRequired < HyperVError error_key(:admin_required) end class BoxInvalid < HyperVError error_key(:box_invalid) end class IPAddrTimeout < HyperVError error_key(:ip_addr_timeout) end class NoSwitches < HyperVError error_key(:no_switches) end class PowerShellFeaturesDisabled < HyperVError error_key(:powershell_features_disabled) end class PowerShellError < HyperVError error_key(:powershell_error) end class PowerShellRequired < HyperVError error_key(:powershell_required) end class WindowsRequired < HyperVError error_key(:windows_required) end end end end vagrant-2.0.2/plugins/providers/hyperv/plugin.rb000066400000000000000000000021551323370221500220140ustar00rootroot00000000000000module VagrantPlugins module HyperV autoload :Action, File.expand_path("../action", __FILE__) autoload :Errors, File.expand_path("../errors", __FILE__) class Plugin < Vagrant.plugin("2") name "Hyper-V provider" description <<-DESC This plugin installs a provider that allows Vagrant to manage machines in Hyper-V. DESC provider(:hyperv, priority: 4) do require_relative "provider" init! Provider end config(:hyperv, :provider) do require_relative "config" init! Config end provider_capability("hyperv", "public_address") do require_relative "cap/public_address" Cap::PublicAddress end provider_capability("hyperv", "snapshot_list") do require_relative "cap/snapshot_list" Cap::SnapshotList end protected def self.init! return if defined?(@_init) I18n.load_path << File.expand_path( "templates/locales/providers_hyperv.yml", Vagrant.source_root) I18n.reload! @_init = true end end end end vagrant-2.0.2/plugins/providers/hyperv/provider.rb000066400000000000000000000050421323370221500223460ustar00rootroot00000000000000require "log4r" require_relative "driver" require_relative "plugin" require "vagrant/util/platform" require "vagrant/util/powershell" module VagrantPlugins module HyperV class Provider < Vagrant.plugin("2", :provider) attr_reader :driver def self.usable?(raise_error=false) if !Vagrant::Util::Platform.windows? raise Errors::WindowsRequired end if !Vagrant::Util::Platform.windows_admin? and !Vagrant::Util::Platform.windows_hyperv_admin? raise Errors::AdminRequired end if !Vagrant::Util::PowerShell.available? raise Errors::PowerShellRequired end true rescue Errors::HyperVError raise if raise_error return false end def initialize(machine) @machine = machine # This method will load in our driver, so we call it now to # initialize it. machine_id_changed end def action(name) # Attempt to get the action method from the Action class if it # exists, otherwise return nil to show that we don't support the # given action. action_method = "action_#{name}" return Action.send(action_method) if Action.respond_to?(action_method) nil end def machine_id_changed @driver = Driver.new(@machine.id) end def state state_id = nil state_id = :not_created if !@machine.id if !state_id # Run a custom action we define called "read_state" which does # what it says. It puts the state in the `:machine_state_id` # key in the environment. env = @machine.action(:read_state) state_id = env[:machine_state_id] end # Get the short and long description short = state_id.to_s long = "" # If we're not created, then specify the special ID flag if state_id == :not_created state_id = Vagrant::MachineState::NOT_CREATED_ID end # Return the MachineState object Vagrant::MachineState.new(state_id, short, long) end def to_s id = @machine.id.nil? ? "new" : @machine.id "Hyper-V (#{id})" end def ssh_info # We can only SSH into a running machine return nil if state.id != :running # Read the IP of the machine using Hyper-V APIs network = @driver.read_guest_ip return nil if !network["ip"] { host: network["ip"], port: 22, } end end end end vagrant-2.0.2/plugins/providers/hyperv/scripts/000077500000000000000000000000001323370221500216555ustar00rootroot00000000000000vagrant-2.0.2/plugins/providers/hyperv/scripts/check_hyperv.ps1000066400000000000000000000005171323370221500247570ustar00rootroot00000000000000# Include the following modules $Dir = Split-Path $script:MyInvocation.MyCommand.Path . ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) $check = $(-Not (-Not (Get-Command "Hyper-V\Get-VMSwitch" -errorAction SilentlyContinue))) $result = @{ result = $check } Write-Output-Message $(ConvertTo-Json $result) vagrant-2.0.2/plugins/providers/hyperv/scripts/clone_vhd.ps1000066400000000000000000000003041323370221500242400ustar00rootroot00000000000000Param( [Parameter(Mandatory=$true)] [string]$Source, [Parameter(Mandatory=$true)] [string]$Destination ) Hyper-V\New-VHD -Path $Destination -ParentPath $Source -ErrorAction Stop vagrant-2.0.2/plugins/providers/hyperv/scripts/create_snapshot.ps1000066400000000000000000000002721323370221500254650ustar00rootroot00000000000000Param( [Parameter(Mandatory=$true)] [string]$VmId, [string]$SnapName ) $VM = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop" Hyper-V\Checkpoint-VM $VM -SnapshotName $SnapName vagrant-2.0.2/plugins/providers/hyperv/scripts/delete_snapshot.ps1000066400000000000000000000002661323370221500254670ustar00rootroot00000000000000Param( [Parameter(Mandatory=$true)] [string]$VmId, [string]$SnapName ) $VM = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop" Hyper-V\Remove-VMSnapshot $VM -Name $SnapName vagrant-2.0.2/plugins/providers/hyperv/scripts/delete_vm.ps1000066400000000000000000000002151323370221500242440ustar00rootroot00000000000000Param( [Parameter(Mandatory=$true)] [string]$VmId ) $VM = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop" Hyper-V\Remove-VM $VM -Force vagrant-2.0.2/plugins/providers/hyperv/scripts/export_vm.ps1000066400000000000000000000005731323370221500243320ustar00rootroot00000000000000Param( [Parameter(Mandatory=$true)] [string]$VmId, [Parameter(Mandatory=$true)] [string]$Path ) $vm = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop" $vm | Hyper-V\Export-VM -Path $Path # Prepare directory structure for box import $name = $vm.Name Move-Item $Path/$name/* $Path Remove-Item -Path $Path/Snapshots -Force -Recurse Remove-Item -Path $Path/$name -Forcevagrant-2.0.2/plugins/providers/hyperv/scripts/file_sync.ps1000066400000000000000000000104231323370221500242550ustar00rootroot00000000000000#------------------------------------------------------------------------- # Copyright (c) Microsoft Open Technologies, Inc. # All Rights Reserved. Licensed under the MIT License. #-------------------------------------------------------------------------- param ( [string]$vm_id = $(throw "-vm_id is required."), [string]$guest_ip = $(throw "-guest_ip is required."), [string]$username = $(throw "-guest_username is required."), [string]$password = $(throw "-guest_password is required."), [string]$host_path = $(throw "-host_path is required."), [string]$guest_path = $(throw "-guest_path is required.") ) # Include the following modules $presentDir = Split-Path -parent $PSCommandPath $modules = @() $modules += $presentDir + "\utils\write_messages.ps1" forEach ($module in $modules) { . $module } function Get-file-hash($source_path, $delimiter) { $source_files = @() (Get-ChildItem $source_path -rec | ForEach-Object -Process { Get-FileHash -Path $_.FullName -Algorithm MD5 } ) | ForEach-Object -Process { $source_files += $_.Path.Replace($source_path, "") + $delimiter + $_.Hash } $source_files } function Get-Remote-Session($guest_ip, $username, $password) { $secstr = convertto-securestring -AsPlainText -Force -String $password $cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $secstr New-PSSession -ComputerName $guest_ip -Credential $cred } function Get-remote-file-hash($source_path, $delimiter, $session) { Invoke-Command -Session $session -ScriptBlock ${function:Get-file-hash} -ArgumentList $source_path, $delimiter # TODO: # Check if remote PS Scripting errors } function Sync-Remote-Machine($machine, $remove_files, $copy_files, $host_path, $guest_path) { ForEach ($item in $copy_files) { $from = $host_path + $item $to = $guest_path + $item # Copy VM can also take a VM object Hyper-V\Copy-VMFile -VM $machine -SourcePath $from -DestinationPath $to -CreateFullPath -FileSource Host -Force } } function Create-Remote-Folders($empty_source_folders, $guest_path) { ForEach ($item in $empty_source_folders) { $new_name = $guest_path + $item New-Item "$new_name" -type directory -Force } } function Get-Empty-folders-From-Source($host_path) { Get-ChildItem $host_path -recurse | Where-Object {$_.PSIsContainer -eq $True} | Where-Object {$_.GetFiles().Count -eq 0} | Select-Object FullName | ForEach-Object -Process { $empty_source_folders += ($_.FullName.Replace($host_path, "")) } } $delimiter = " || " $machine = Hyper-V\Get-VM -Id $vm_id # FIXME: PowerShell guys please fix this. # The below script checks for all VMIntegrationService which are not enabled # and will enable this. # When when all the services are enabled this throws an error. # Enable VMIntegrationService to true try { Hyper-V\Get-VM -Id $vm_id | Hyper-V\Get-VMIntegrationService -Name "Guest Service Interface" | Hyper-V\Enable-VMIntegrationService -Passthru } catch { } $session = Get-Remote-Session $guest_ip $username $password $source_files = Get-file-hash $host_path $delimiter $destination_files = Get-remote-file-hash $guest_path $delimiter $session if (!$destination_files) { $destination_files = @() } if (!$source_files) { $source_files = @() } # Compare source and destination files $remove_files = @() $copy_files = @() Compare-Object -ReferenceObject $source_files -DifferenceObject $destination_files | ForEach-Object { if ($_.SideIndicator -eq '=>') { $remove_files += $_.InputObject.Split($delimiter)[0] } else { $copy_files += $_.InputObject.Split($delimiter)[0] } } # Update the files to remote machine Sync-Remote-Machine $machine $remove_files $copy_files $host_path $guest_path # Create any empty folders which missed to sync to remote machine $empty_source_folders = @() $directories = Get-Empty-folders-From-Source $host_path $result = Invoke-Command -Session $session -ScriptBlock ${function:Create-Remote-Folders} -ArgumentList $empty_source_folders, $guest_path # Always remove the connection after Use Remove-PSSession -Id $session.Id $resultHash = @{ message = "OK" } $result = ConvertTo-Json $resultHash Write-Output-Message $result vagrant-2.0.2/plugins/providers/hyperv/scripts/get_network_config.ps1000066400000000000000000000022651323370221500261640ustar00rootroot00000000000000Param( [Parameter(Mandatory=$true)] [string]$VmId ) # Include the following modules $Dir = Split-Path $script:MyInvocation.MyCommand.Path . ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) $vm = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop" $networks = Hyper-V\Get-VMNetworkAdapter -VM $vm foreach ($network in $networks) { if ($network.IpAddresses.Length -gt 0) { foreach ($ip_address in $network.IpAddresses) { if ($ip_address.Contains(".")) { $ip4_address = $ip_address } elseif ($ip_address.Contains(":")) { $ip6_address = $ip_address } if (-Not ([string]::IsNullOrEmpty($ip4_address)) -Or -Not ([string]::IsNullOrEmpty($ip6_address))) { # We found our IP address! break } } } } if (-Not ([string]::IsNullOrEmpty($ip4_address))) { $guest_ipaddress = $ip4_address } elseif (-Not ([string]::IsNullOrEmpty($ip6_address))) { $guest_ipaddress = $ip6_address } if (-Not ([string]::IsNullOrEmpty($guest_ipaddress))) { $resultHash = @{ ip = $guest_ipaddress } $result = ConvertTo-Json $resultHash Write-Output-Message $result } else { Write-Error-Message "Failed to determine IP address" } vagrant-2.0.2/plugins/providers/hyperv/scripts/get_network_mac.ps1000066400000000000000000000012271323370221500254540ustar00rootroot00000000000000Param( [Parameter(Mandatory=$true)] [string]$VmId ) # Include the following modules $Dir = Split-Path $script:MyInvocation.MyCommand.Path . ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) $ip_address = "" $vm = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop" $networks = Hyper-V\Get-VMNetworkAdapter -VM $vm foreach ($network in $networks) { if ($network.MacAddress -gt 0) { $mac_address = $network.MacAddress if (-Not ([string]::IsNullOrEmpty($mac_address))) { # We found our mac address! break } } } $resultHash = @{ mac = "$mac_address" } $result = ConvertTo-Json $resultHash Write-Output-Message $resultvagrant-2.0.2/plugins/providers/hyperv/scripts/get_switches.ps1000066400000000000000000000006531323370221500247760ustar00rootroot00000000000000# This will have a SwitchType property. As far as I know the values are: # # 0 - Private # 1 - Internal # # Include the following modules $Dir = Split-Path $script:MyInvocation.MyCommand.Path . ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) $Switches = @(Hyper-V\Get-VMSwitch ` | Select-Object Name,SwitchType,NetAdapterInterfaceDescription) Write-Output-Message $(ConvertTo-JSON $Switches) vagrant-2.0.2/plugins/providers/hyperv/scripts/get_vm_status.ps1000066400000000000000000000033541323370221500251730ustar00rootroot00000000000000Param( [Parameter(Mandatory=$true)] [string]$VmId ) # Include the following modules $Dir = Split-Path $script:MyInvocation.MyCommand.Path . ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) # Make sure the exception type is loaded try { # Microsoft.HyperV.PowerShell is present on all versions of Windows with HyperV [void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.HyperV.PowerShell, Culture=neutral, PublicKeyToken=31bf3856ad364e35') # Microsoft.HyperV.PowerShell.Objects is only present on Windows >= 10.0, so this will fail, and we ignore it since the needed exception # type was loaded in Microsoft.HyperV.PowerShell [void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.HyperV.PowerShell.Objects, Culture=neutral, PublicKeyToken=31bf3856ad364e35') } catch { # Empty catch ok, since if we didn't load the types, we will fail in the next block } $VmmsPath = if ([environment]::Is64BitProcess) { "$($env:SystemRoot)\System32\vmms.exe" } else { "$($env:SystemRoot)\Sysnative\vmms.exe" } $HyperVVersion = [version](Get-Item $VmmsPath).VersionInfo.ProductVersion if($HyperVVersion -lt ([version]'10.0')) { $ExceptionType = [Microsoft.HyperV.PowerShell.VirtualizationOperationFailedException] } else { $ExceptionType = [Microsoft.HyperV.PowerShell.VirtualizationException] } try { $VM = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop" $State = $VM.state $Status = $VM.status } catch [Exception] { if($_.Exception.GetType() -eq $ExceptionType) { $State = "not_created" $Status = $State } else { throw; } } $resultHash = @{ state = "$State" status = "$Status" } $result = ConvertTo-Json $resultHash Write-Output-Message $result vagrant-2.0.2/plugins/providers/hyperv/scripts/has_vmcx_support.ps1000066400000000000000000000005431323370221500257100ustar00rootroot00000000000000# Include the following modules $Dir = Split-Path $script:MyInvocation.MyCommand.Path . ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) # Windows version 10 and up have support for binary format $check = [System.Environment]::OSVersion.Version.Major -ge 10 $result = @{ result = $check } Write-Output-Message $(ConvertTo-Json $result) vagrant-2.0.2/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1000066400000000000000000000117451323370221500253630ustar00rootroot00000000000000Param( [Parameter(Mandatory=$true)] [string]$vm_config_file, [Parameter(Mandatory=$true)] [string]$source_path, [Parameter(Mandatory=$true)] [string]$dest_path, [Parameter(Mandatory=$true)] [string]$data_path, [string]$switchname=$null, [string]$memory=$null, [string]$maxmemory=$null, [string]$cpus=$null, [string]$vmname=$null, [string]$auto_start_action=$null, [string]$auto_stop_action=$null, [string]$differencing_disk=$null, [string]$enable_virtualization_extensions=$False ) # Include the following modules $Dir = Split-Path $script:MyInvocation.MyCommand.Path . ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) $VmProperties = @{ Path = $vm_config_file SnapshotFilePath = Join-Path $data_path 'Snapshots' VhdDestinationPath = Join-Path $data_path 'Virtual Hard Disks' VirtualMachinePath = $data_path } $vmConfig = (Hyper-V\Compare-VM -Copy -GenerateNewID @VmProperties) $generation = $vmConfig.VM.Generation if (!$vmname) { # Get the name of the vm $vm_name = $vmconfig.VM.VMName } else { $vm_name = $vmname } if (!$cpus) { # Get the processorcount of the VM $processors = (Hyper-V\Get-VMProcessor -VM $vmConfig.VM).Count }else { $processors = $cpus } function GetUniqueName($name) { Hyper-V\Get-VM | ForEach-Object -Process { if ($name -eq $_.Name) { $name = $name + "_1" } } return $name } do { $name = $vm_name $vm_name = GetUniqueName $name } while ($vm_name -ne $name) if (!$memory) { $configMemory = Hyper-V\Get-VMMemory -VM $vmConfig.VM $dynamicmemory = $configMemory.DynamicMemoryEnabled $MemoryMaximumBytes = ($configMemory.Maximum) $MemoryStartupBytes = ($configMemory.Startup) $MemoryMinimumBytes = ($configMemory.Minimum) } else { if (!$maxmemory){ $dynamicmemory = $False $MemoryMaximumBytes = ($memory -as [int]) * 1MB $MemoryStartupBytes = ($memory -as [int]) * 1MB $MemoryMinimumBytes = ($memory -as [int]) * 1MB } else { $dynamicmemory = $True $MemoryMaximumBytes = ($maxmemory -as [int]) * 1MB $MemoryStartupBytes = ($memory -as [int]) * 1MB $MemoryMinimumBytes = ($memory -as [int]) * 1MB } } if (!$switchname) { $switchname = (Hyper-V\Get-VMNetworkAdapter -VM $vmConfig.VM).SwitchName } # Enable nested virtualization if configured if ($enable_virtualization_extensions -eq "True") { Hyper-V\Set-VMProcessor -VM $vmConfig.VM -ExposeVirtualizationExtensions $true } $vmNetworkAdapter = Hyper-V\Get-VMNetworkAdapter -VM $vmConfig.VM Hyper-V\Connect-VMNetworkAdapter -VMNetworkAdapter $vmNetworkAdapter -SwitchName $switchname Hyper-V\Set-VM -VM $vmConfig.VM -NewVMName $vm_name Hyper-V\Set-VM -VM $vmConfig.VM -ErrorAction "Stop" Hyper-V\Set-VM -VM $vmConfig.VM -ProcessorCount $processors if ($dynamicmemory) { Hyper-V\Set-VM -VM $vmConfig.VM -DynamicMemory Hyper-V\Set-VM -VM $vmConfig.VM -MemoryMinimumBytes $MemoryMinimumBytes -MemoryMaximumBytes $MemoryMaximumBytes -MemoryStartupBytes $MemoryStartupBytes } else { Hyper-V\Set-VM -VM $vmConfig.VM -StaticMemory Hyper-V\Set-VM -VM $vmConfig.VM -MemoryStartupBytes $MemoryStartupBytes } if ($notes) { Hyper-V\Set-VM -VM $vmConfig.VM -Notes $notes } if ($auto_start_action) { Hyper-V\Set-VM -VM $vmConfig.VM -AutomaticStartAction $auto_start_action } if ($auto_stop_action) { Hyper-V\Set-VM -VM $vmConfig.VM -AutomaticStopAction $auto_stop_action } # Only set EFI secure boot for Gen 2 machines, not gen 1 if ($generation -ne 1) { Hyper-V\Set-VMFirmware -VM $vmConfig.VM -EnableSecureBoot (Hyper-V\Get-VMFirmware -VM $vmConfig.VM).SecureBoot } $report = Hyper-V\Compare-VM -CompatibilityReport $vmConfig # Stop if there are incompatibilities if($report.Incompatibilities.Length -gt 0){ Write-Error-Message $(ConvertTo-Json $($report.Incompatibilities | Select -ExpandProperty Message)) exit 0 } if($differencing_disk){ # Get all controller on the VM, first scsi, then IDE if it is a Gen 1 device $controllers = Hyper-V\Get-VMScsiController -VM $vmConfig.VM if($generation -eq 1){ $controllers = @($controllers) + @(Hyper-V\Get-VMIdeController -VM $vmConfig.VM) } foreach($controller in $controllers){ foreach($drive in $controller.Drives){ if([System.IO.Path]::GetFileName($drive.Path) -eq [System.IO.Path]::GetFileName($source_path)){ # Remove the old disk and replace it with a differencing version $path = $drive.Path Hyper-V\Remove-VMHardDiskDrive $drive Hyper-V\New-VHD -Path $dest_path -ParentPath $source_path -ErrorAction Stop Hyper-V\Add-VMHardDiskDrive -VM $vmConfig.VM -Path $dest_path } } } } Hyper-V\Import-VM -CompatibilityReport $vmConfig $vm_id = (Hyper-V\Get-VM $vm_name).id.guid $resultHash = @{ name = $vm_name id = $vm_id } $result = ConvertTo-Json $resultHash Write-Output-Message $result vagrant-2.0.2/plugins/providers/hyperv/scripts/import_vm_xml.ps1000066400000000000000000000141201323370221500251740ustar00rootroot00000000000000Param( [Parameter(Mandatory=$true)] [string]$vm_config_file, [Parameter(Mandatory=$true)] [string]$dest_path, [string]$switchname=$null, [string]$memory=$null, [string]$maxmemory=$null, [string]$cpus=$null, [string]$vmname=$null, [string]$auto_start_action=$null, [string]$auto_stop_action=$null, [string]$enable_virtualization_extensions=$False ) # Include the following modules $Dir = Split-Path $script:MyInvocation.MyCommand.Path . ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) [xml]$vmconfig = Get-Content -Path $vm_config_file $generation = [int]($vmconfig.configuration.properties.subtype.'#text')+1 if (!$vmname) { # Get the name of the vm $vm_name = $vmconfig.configuration.properties.name.'#text' }else { $vm_name = $vmname } if (!$cpus) { # Get the name of the vm $processors = $vmconfig.configuration.settings.processors.count.'#text' }else { $processors = $cpus } function GetUniqueName($name) { Hyper-V\Get-VM | ForEach-Object -Process { if ($name -eq $_.Name) { $name = $name + "_1" } } return $name } do { $name = $vm_name $vm_name = GetUniqueName $name } while ($vm_name -ne $name) if (!$memory) { $xmlmemory = (Select-Xml -xml $vmconfig -XPath "//memory").node.Bank if ($xmlmemory.dynamic_memory_enabled."#text" -eq "True") { $dynamicmemory = $True } else { $dynamicmemory = $False } # Memory values need to be in bytes $MemoryMaximumBytes = ($xmlmemory.limit."#text" -as [int]) * 1MB $MemoryStartupBytes = ($xmlmemory.size."#text" -as [int]) * 1MB $MemoryMinimumBytes = ($xmlmemory.reservation."#text" -as [int]) * 1MB } else { if (!$maxmemory){ $dynamicmemory = $False $MemoryMaximumBytes = ($memory -as [int]) * 1MB $MemoryStartupBytes = ($memory -as [int]) * 1MB $MemoryMinimumBytes = ($memory -as [int]) * 1MB } else { $dynamicmemory = $True $MemoryMaximumBytes = ($maxmemory -as [int]) * 1MB $MemoryStartupBytes = ($memory -as [int]) * 1MB $MemoryMinimumBytes = ($memory -as [int]) * 1MB } } if (!$switchname) { # Get the name of the virtual switch $switchname = (Select-Xml -xml $vmconfig -XPath "//AltSwitchName").node."#text" } if ($generation -eq 1) { # Determine boot device Switch ((Select-Xml -xml $vmconfig -XPath "//boot").node.device0."#text") { "Floppy" { $bootdevice = "Floppy" } "HardDrive" { $bootdevice = "IDE" } "Optical" { $bootdevice = "CD" } "Network" { $bootdevice = "LegacyNetworkAdapter" } "Default" { $bootdevice = "IDE" } } #switch } else { # Determine boot device Switch ((Select-Xml -xml $vmconfig -XPath "//boot").node.device0."#text") { "HardDrive" { $bootdevice = "VHD" } "Optical" { $bootdevice = "CD" } "Network" { $bootdevice = "NetworkAdapter" } "Default" { $bootdevice = "VHD" } } #switch } # Determine secure boot options $secure_boot_enabled = (Select-Xml -xml $vmconfig -XPath "//secure_boot_enabled").Node."#text" # Define a hash map of parameter values for New-VM $vm_params = @{ Name = $vm_name NoVHD = $True MemoryStartupBytes = $MemoryStartupBytes SwitchName = $switchname BootDevice = $bootdevice ErrorAction = "Stop" } # Generation parameter was added in ps v4 if((get-command Hyper-V\New-VM).Parameters.Keys.Contains("generation")) { $vm_params.Generation = $generation } # Create the VM using the values in the hash map $vm = Hyper-V\New-VM @vm_params $notes = (Select-Xml -xml $vmconfig -XPath "//notes").node.'#text' # Set-VM parameters to configure new VM with old values $more_vm_params = @{ ProcessorCount = $processors MemoryStartupBytes = $MemoryStartupBytes } If ($dynamicmemory) { $more_vm_params.Add("DynamicMemory",$True) $more_vm_params.Add("MemoryMinimumBytes",$MemoryMinimumBytes) $more_vm_params.Add("MemoryMaximumBytes", $MemoryMaximumBytes) } else { $more_vm_params.Add("StaticMemory",$True) } if ($notes) { $more_vm_params.Add("Notes",$notes) } if ($auto_start_action) { $more_vm_params.Add("AutomaticStartAction",$auto_start_action) } if ($auto_stop_action) { $more_vm_params.Add("AutomaticStopAction",$auto_stop_action) } # Set the values on the VM $vm | Hyper-V\Set-VM @more_vm_params -Passthru # Add drives to the virtual machine $controllers = Select-Xml -xml $vmconfig -xpath "//*[starts-with(name(.),'controller')]" # Only set EFI secure boot for Gen 2 machines, not gen 1 if ($generation -ne 1) { # Set EFI secure boot if ($secure_boot_enabled -eq "True") { Hyper-V\Set-VMFirmware -VM $vm -EnableSecureBoot On } else { Hyper-V\Set-VMFirmware -VM $vm -EnableSecureBoot Off } } # Enable nested virtualization if configured if ($enable_virtualization_extensions -eq "True") { Hyper-V\Set-VMProcessor -VM $vm -ExposeVirtualizationExtensions $true } # A regular expression pattern to pull the number from controllers [regex]$rx="\d" foreach ($controller in $controllers) { $node = $controller.Node # Check for SCSI if ($node.ParentNode.ChannelInstanceGuid) { $ControllerType = "SCSI" } else { $ControllerType = "IDE" } $drives = $node.ChildNodes | where {$_.pathname."#text"} foreach ($drive in $drives) { #if drive type is ISO then set DVD Drive accordingly $driveType = $drive.type."#text" $addDriveParam = @{ ControllerNumber = $rx.Match($controller.node.name).value Path = $dest_path } if ($drive.pool_id."#text") { $ResourcePoolName = $drive.pool_id."#text" $addDriveParam.Add("ResourcePoolname",$ResourcePoolName) } if ($drivetype -eq 'VHD') { $addDriveParam.add("ControllerType",$ControllerType) $vm | Hyper-V\Add-VMHardDiskDrive @AddDriveparam } } } $vm_id = (Hyper-V\Get-VM $vm_name).id.guid $resultHash = @{ name = $vm_name id = $vm_id } $result = ConvertTo-Json $resultHash Write-Output-Message $result vagrant-2.0.2/plugins/providers/hyperv/scripts/list_snapshots.ps1000066400000000000000000000004461323370221500253630ustar00rootroot00000000000000Param( [Parameter(Mandatory=$true)] [string]$VmId ) $VM = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop" $Snapshots = @(Hyper-V\Get-VMSnapshot $VM | Select-Object Name) $result = ConvertTo-json $Snapshots Write-Host "===Begin-Output===" Write-Host $result Write-Host "===End-Output===" vagrant-2.0.2/plugins/providers/hyperv/scripts/restore_snapshot.ps1000066400000000000000000000003061323370221500257030ustar00rootroot00000000000000Param( [Parameter(Mandatory=$true)] [string]$VmId, [string]$SnapName ) $VM = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop" Hyper-V\Restore-VMSnapshot $VM -Name $SnapName -Confirm:$false vagrant-2.0.2/plugins/providers/hyperv/scripts/resume_vm.ps1000066400000000000000000000002151323370221500243020ustar00rootroot00000000000000Param( [Parameter(Mandatory=$true)] [string]$VmId ) $VM = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop" Hyper-V\Resume-VM $VM vagrant-2.0.2/plugins/providers/hyperv/scripts/set_network_mac.ps1000066400000000000000000000007641323370221500254750ustar00rootroot00000000000000param ( [string]$VmId = $(throw "-VmId is required."), [string]$Mac = $(throw "-Mac ") ) # Include the following modules $presentDir = Split-Path -parent $PSCommandPath $modules = @() $modules += $presentDir + "\utils\write_messages.ps1" forEach ($module in $modules) { . $module } try { $vm = Hyper-V\Get-VM -Id $VmId -ErrorAction "stop" Hyper-V\Set-VMNetworkAdapter $vm -StaticMacAddress $Mac -ErrorAction "stop" } catch { Write-Error-Message "Failed to set VM's MAC address $_" } vagrant-2.0.2/plugins/providers/hyperv/scripts/set_network_vlan.ps1000066400000000000000000000007441323370221500256730ustar00rootroot00000000000000param ( [string]$VmId = $(throw "-VmId is required."), [int]$VlanId = $(throw "-VlanId ") ) # Include the following modules $presentDir = Split-Path -parent $PSCommandPath $modules = @() $modules += $presentDir + "\utils\write_messages.ps1" forEach ($module in $modules) { . $module } try { $vm = Hyper-V\Get-VM -Id $VmId -ErrorAction "stop" Hyper-V\Set-VMNetworkAdapterVlan $vm -Access -Vlanid $VlanId } catch { Write-Error-Message "Failed to set VM's Vlan ID $_" } vagrant-2.0.2/plugins/providers/hyperv/scripts/set_vm_integration_services.ps1000066400000000000000000000023741323370221500301130ustar00rootroot00000000000000param ( [string] $VmId, [string] $guest_service_interface = $null, [string] $heartbeat = $null, [string] $key_value_pair_exchange = $null, [string] $shutdown = $null, [string] $time_synchronization = $null, [string] $vss = $null ) # Include the following modules $Dir = Split-Path $script:MyInvocation.MyCommand.Path . ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) $vm = Hyper-V\Get-VM -Id $VmId -ErrorAction "stop" # Set the service based on value function VmSetService { param ([string] $Name, [string] $Value, [Microsoft.HyperV.PowerShell.VirtualMachine] $Vm) if ($Value -ne $null){ if($Value -eq "true"){ Hyper-V\Enable-VMIntegrationService -VM $Vm -Name $Name } if($Value -eq "false"){ Hyper-V\Disable-VMIntegrationService -VM $Vm -Name $Name } } } VmSetService -Name "Guest Service Interface" -Value $guest_service_interface -Vm $vm VmSetService -Name "Heartbeat" -Value $heartbeat -Vm $vm VmSetService -Name "Key-Value Pair Exchange" -Value $key_value_pair_exchange -Vm $vm VmSetService -Name "Shutdown" -Value $shutdown -Vm $vm VmSetService -Name "Time Synchronization" -Value $time_synchronization -Vm $vm VmSetService -Name "VSS" -Value $vss -Vm $vmvagrant-2.0.2/plugins/providers/hyperv/scripts/start_vm.ps1000066400000000000000000000011741323370221500241440ustar00rootroot00000000000000param ( [string]$VmId = $(throw "-VmId is required.") ) # Include the following modules $presentDir = Split-Path -parent $PSCommandPath $modules = @() $modules += $presentDir + "\utils\write_messages.ps1" forEach ($module in $modules) { . $module } try { $vm = Hyper-V\Get-VM -Id $VmId -ErrorAction "stop" Hyper-V\Start-VM $vm -ErrorAction "stop" $state = $vm.state $status = $vm.status $name = $vm.name $resultHash = @{ state = "$state" status = "$status" name = "$name" } $result = ConvertTo-Json $resultHash Write-Output-Message $result } catch { Write-Error-Message "Failed to start a VM $_" }vagrant-2.0.2/plugins/providers/hyperv/scripts/stop_vm.ps1000066400000000000000000000003231323370221500237670ustar00rootroot00000000000000Param( [Parameter(Mandatory=$true)] [string]$VmId ) # Shuts down virtual machine regardless of any unsaved application data $VM = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop" Hyper-V\Stop-VM $VM -Force vagrant-2.0.2/plugins/providers/hyperv/scripts/suspend_vm.ps1000066400000000000000000000002161323370221500244640ustar00rootroot00000000000000Param( [Parameter(Mandatory=$true)] [string]$VmId ) $VM = Hyper-V\Get-VM -Id $VmId -ErrorAction "Stop" Hyper-V\Suspend-VM $VM vagrant-2.0.2/plugins/providers/hyperv/scripts/utils/000077500000000000000000000000001323370221500230155ustar00rootroot00000000000000vagrant-2.0.2/plugins/providers/hyperv/scripts/utils/create_session.ps1000066400000000000000000000021211323370221500264440ustar00rootroot00000000000000#------------------------------------------------------------------------- # Copyright (c) Microsoft Open Technologies, Inc. # All Rights Reserved. Licensed under the MIT License. #-------------------------------------------------------------------------- function Get-Remote-Session($guest_ip, $username, $password) { $secstr = convertto-securestring -AsPlainText -Force -String $password $cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $secstr New-PSSession -ComputerName $guest_ip -Credential $cred -ErrorAction "stop" } function Create-Remote-Session($guest_ip, $username, $password) { $count = 0 $session_error = "" $session = "" do { $count++ try { $session = Get-Remote-Session $guest_ip $username $password $session_error = "" } catch { Start-Sleep -s 1 $session_error = $_ $session = "" } } while (!$session -and $count -lt 20) return @{ session = $session error = $session_error } } vagrant-2.0.2/plugins/providers/hyperv/scripts/utils/write_messages.ps1000066400000000000000000000011421323370221500264610ustar00rootroot00000000000000#------------------------------------------------------------------------- # Copyright (c) Microsoft Open Technologies, Inc. # All Rights Reserved. Licensed under the MIT License. #-------------------------------------------------------------------------- function Write-Error-Message($message) { $error_message = @{ error = "$message" } Write-Host "===Begin-Error===" $result = ConvertTo-json $error_message Write-Host $result Write-Host "===End-Error===" } function Write-Output-Message($message) { Write-Host "===Begin-Output===" Write-Host $message Write-Host "===End-Output===" } vagrant-2.0.2/plugins/providers/virtualbox/000077500000000000000000000000001323370221500210505ustar00rootroot00000000000000vagrant-2.0.2/plugins/providers/virtualbox/action.rb000066400000000000000000000352151323370221500226600ustar00rootroot00000000000000require "vagrant/action/builder" module VagrantPlugins module ProviderVirtualBox module Action autoload :Boot, File.expand_path("../action/boot", __FILE__) autoload :CheckAccessible, File.expand_path("../action/check_accessible", __FILE__) autoload :CheckCreated, File.expand_path("../action/check_created", __FILE__) autoload :CheckGuestAdditions, File.expand_path("../action/check_guest_additions", __FILE__) autoload :CheckRunning, File.expand_path("../action/check_running", __FILE__) autoload :CheckVirtualbox, File.expand_path("../action/check_virtualbox", __FILE__) autoload :CleanMachineFolder, File.expand_path("../action/clean_machine_folder", __FILE__) autoload :ClearForwardedPorts, File.expand_path("../action/clear_forwarded_ports", __FILE__) autoload :ClearNetworkInterfaces, File.expand_path("../action/clear_network_interfaces", __FILE__) autoload :Created, File.expand_path("../action/created", __FILE__) autoload :Customize, File.expand_path("../action/customize", __FILE__) autoload :Destroy, File.expand_path("../action/destroy", __FILE__) autoload :DestroyUnusedNetworkInterfaces, File.expand_path("../action/destroy_unused_network_interfaces", __FILE__) autoload :DiscardState, File.expand_path("../action/discard_state", __FILE__) autoload :Export, File.expand_path("../action/export", __FILE__) autoload :ForcedHalt, File.expand_path("../action/forced_halt", __FILE__) autoload :ForwardPorts, File.expand_path("../action/forward_ports", __FILE__) autoload :Import, File.expand_path("../action/import", __FILE__) autoload :ImportMaster, File.expand_path("../action/import_master", __FILE__) autoload :IsPaused, File.expand_path("../action/is_paused", __FILE__) autoload :IsRunning, File.expand_path("../action/is_running", __FILE__) autoload :IsSaved, File.expand_path("../action/is_saved", __FILE__) autoload :MatchMACAddress, File.expand_path("../action/match_mac_address", __FILE__) autoload :MessageAlreadyRunning, File.expand_path("../action/message_already_running", __FILE__) autoload :MessageNotCreated, File.expand_path("../action/message_not_created", __FILE__) autoload :MessageNotRunning, File.expand_path("../action/message_not_running", __FILE__) autoload :MessageWillNotDestroy, File.expand_path("../action/message_will_not_destroy", __FILE__) autoload :Network, File.expand_path("../action/network", __FILE__) autoload :NetworkFixIPv6, File.expand_path("../action/network_fix_ipv6", __FILE__) autoload :Package, File.expand_path("../action/package", __FILE__) autoload :PackageSetupFiles, File.expand_path("../action/package_setup_files", __FILE__) autoload :PackageSetupFolders, File.expand_path("../action/package_setup_folders", __FILE__) autoload :PackageVagrantfile, File.expand_path("../action/package_vagrantfile", __FILE__) autoload :PrepareCloneSnapshot, File.expand_path("../action/prepare_clone_snapshot", __FILE__) autoload :PrepareNFSSettings, File.expand_path("../action/prepare_nfs_settings", __FILE__) autoload :PrepareNFSValidIds, File.expand_path("../action/prepare_nfs_valid_ids", __FILE__) autoload :PrepareForwardedPortCollisionParams, File.expand_path("../action/prepare_forwarded_port_collision_params", __FILE__) autoload :Resume, File.expand_path("../action/resume", __FILE__) autoload :SaneDefaults, File.expand_path("../action/sane_defaults", __FILE__) autoload :SetName, File.expand_path("../action/set_name", __FILE__) autoload :SnapshotDelete, File.expand_path("../action/snapshot_delete", __FILE__) autoload :SnapshotRestore, File.expand_path("../action/snapshot_restore", __FILE__) autoload :SnapshotSave, File.expand_path("../action/snapshot_save", __FILE__) autoload :Suspend, File.expand_path("../action/suspend", __FILE__) # @deprecated use {PackageSetupFiles} instead autoload :SetupPackageFiles, File.expand_path("../action/setup_package_files", __FILE__) # Include the built-in modules so that we can use them as top-level # things. include Vagrant::Action::Builtin # This action boots the VM, assuming the VM is in a state that requires # a bootup (i.e. not saved). def self.action_boot Vagrant::Action::Builder.new.tap do |b| b.use CheckAccessible b.use CleanMachineFolder b.use SetName b.use ClearForwardedPorts b.use Provision b.use EnvSet, port_collision_repair: true b.use PrepareForwardedPortCollisionParams b.use HandleForwardedPortCollisions b.use PrepareNFSValidIds b.use SyncedFolderCleanup b.use SyncedFolders b.use PrepareNFSSettings b.use ClearNetworkInterfaces b.use Network b.use NetworkFixIPv6 b.use ForwardPorts b.use SetHostname b.use SaneDefaults b.use Customize, "pre-boot" b.use Boot b.use Customize, "post-boot" b.use WaitForCommunicator, [:starting, :running] b.use Customize, "post-comm" b.use CheckGuestAdditions end end # This is the action that is primarily responsible for completely # freeing the resources of the underlying virtual machine. def self.action_destroy Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use Call, Created do |env1, b2| if !env1[:result] b2.use MessageNotCreated next end b2.use Call, DestroyConfirm do |env2, b3| if env2[:result] b3.use ConfigValidate b3.use ProvisionerCleanup, :before b3.use CheckAccessible b3.use EnvSet, force_halt: true b3.use action_halt b3.use Destroy b3.use CleanMachineFolder b3.use DestroyUnusedNetworkInterfaces b3.use PrepareNFSValidIds b3.use SyncedFolderCleanup else b3.use MessageWillNotDestroy end end end end end # This is the action that is primarily responsible for halting # the virtual machine, gracefully or by force. def self.action_halt Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use Call, Created do |env, b2| if env[:result] b2.use CheckAccessible b2.use DiscardState b2.use Call, IsPaused do |env2, b3| next if !env2[:result] b3.use Resume end b2.use Call, GracefulHalt, :poweroff, :running do |env2, b3| if !env2[:result] b3.use ForcedHalt end end else b2.use MessageNotCreated end end end end # This action packages the virtual machine into a single box file. def self.action_package Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use Call, Created do |env1, b2| if !env1[:result] b2.use MessageNotCreated next end b2.use PackageSetupFolders b2.use PackageSetupFiles b2.use CheckAccessible b2.use action_halt b2.use ClearForwardedPorts b2.use PrepareNFSValidIds b2.use SyncedFolderCleanup b2.use Package b2.use Export b2.use PackageVagrantfile end end end # This action just runs the provisioners on the machine. def self.action_provision Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use ConfigValidate b.use Call, Created do |env1, b2| if !env1[:result] b2.use MessageNotCreated next end b2.use Call, IsRunning do |env2, b3| if !env2[:result] b3.use MessageNotRunning next end b3.use CheckAccessible b3.use Provision end end end end # This action is responsible for reloading the machine, which # brings it down, sucks in new configuration, and brings the # machine back up with the new configuration. def self.action_reload Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use Call, Created do |env1, b2| if !env1[:result] b2.use MessageNotCreated next end b2.use ConfigValidate b2.use action_halt b2.use action_start end end end # This is the action that is primarily responsible for resuming # suspended machines. def self.action_resume Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use Call, Created do |env, b2| if env[:result] b2.use CheckAccessible b2.use EnvSet, port_collision_repair: false b2.use PrepareForwardedPortCollisionParams b2.use HandleForwardedPortCollisions b2.use Resume b2.use Provision b2.use WaitForCommunicator, [:restoring, :running] else b2.use MessageNotCreated end end end end def self.action_snapshot_delete Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use Call, Created do |env, b2| if env[:result] b2.use SnapshotDelete else b2.use MessageNotCreated end end end end # This is the action that is primarily responsible for restoring a snapshot def self.action_snapshot_restore Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use Call, Created do |env, b2| if !env[:result] raise Vagrant::Errors::VMNotCreatedError end b2.use CheckAccessible b2.use EnvSet, force_halt: true b2.use action_halt b2.use SnapshotRestore b2.use Call, IsEnvSet, :snapshot_delete do |env2, b3| if env2[:result] b3.use action_snapshot_delete end end b2.use action_start end end end # This is the action that is primarily responsible for saving a snapshot def self.action_snapshot_save Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use Call, Created do |env, b2| if env[:result] b2.use SnapshotSave else b2.use MessageNotCreated end end end end # This is the action that will exec into an SSH shell. def self.action_ssh Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use CheckCreated b.use CheckAccessible b.use CheckRunning b.use SSHExec end end # This is the action that will run a single SSH command. def self.action_ssh_run Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use CheckCreated b.use CheckAccessible b.use CheckRunning b.use SSHRun end end # This action starts a VM, assuming it is already imported and exists. # A precondition of this action is that the VM exists. def self.action_start Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use ConfigValidate b.use BoxCheckOutdated b.use Call, IsRunning do |env, b2| # If the VM is running, run the necessary provisioners if env[:result] b2.use action_provision next end b2.use Call, IsSaved do |env2, b3| if env2[:result] # The VM is saved, so just resume it b3.use action_resume next end b3.use Call, IsPaused do |env3, b4| if env3[:result] b4.use Resume next end # The VM is not saved, so we must have to boot it up # like normal. Boot! b4.use action_boot end end end end end # This is the action that is primarily responsible for suspending # the virtual machine. def self.action_suspend Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use Call, Created do |env, b2| if env[:result] b2.use CheckAccessible b2.use Suspend else b2.use MessageNotCreated end end end end # This is the action that is called to sync folders to a running # machine without a reboot. def self.action_sync_folders Vagrant::Action::Builder.new.tap do |b| b.use PrepareNFSValidIds b.use SyncedFolders b.use PrepareNFSSettings end end # This action brings the machine up from nothing, including importing # the box, configuring metadata, and booting. def self.action_up Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox # Handle box_url downloading early so that if the Vagrantfile # references any files in the box or something it all just # works fine. b.use Call, Created do |env, b2| if !env[:result] b2.use HandleBox end end b.use ConfigValidate b.use Call, Created do |env, b2| # If the VM is NOT created yet, then do the setup steps if !env[:result] b2.use CheckAccessible b2.use Customize, "pre-import" if env[:machine].provider_config.linked_clone # We are cloning from the box b2.use ImportMaster end b2.use PrepareClone b2.use PrepareCloneSnapshot b2.use Import b2.use DiscardState b2.use MatchMACAddress end end b.use action_start end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/000077500000000000000000000000001323370221500223255ustar00rootroot00000000000000vagrant-2.0.2/plugins/providers/virtualbox/action/boot.rb000066400000000000000000000010051323370221500236110ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class Boot def initialize(app, env) @app = app end def call(env) @env = env boot_mode = @env[:machine].provider_config.gui ? "gui" : "headless" # Start up the VM and wait for it to boot. env[:ui].info I18n.t("vagrant.actions.vm.boot.booting") env[:machine].provider.driver.start(boot_mode) @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/check_accessible.rb000066400000000000000000000012021323370221500260770ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class CheckAccessible def initialize(app, env) @app = app end def call(env) if env[:machine].state.id == :inaccessible # The VM we are attempting to manipulate is inaccessible. This # is a very bad situation and can only be fixed by the user. It # also prohibits us from actually doing anything with the virtual # machine, so we raise an error. raise Vagrant::Errors::VMInaccessible end @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/check_created.rb000066400000000000000000000010021323370221500254070ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action # This middleware checks that the VM is created, and raises an exception # if it is not, notifying the user that creation is required. class CheckCreated def initialize(app, env) @app = app end def call(env) if env[:machine].state.id == :not_created raise Vagrant::Errors::VMNotCreatedError end @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/check_guest_additions.rb000066400000000000000000000034471323370221500272040ustar00rootroot00000000000000require "log4r" module VagrantPlugins module ProviderVirtualBox module Action class CheckGuestAdditions def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::plugins::virtualbox::check_guest_additions") end def call(env) if !env[:machine].provider_config.check_guest_additions @logger.info("Not checking guest additions because configuration") return @app.call(env) end env[:ui].output(I18n.t("vagrant.virtualbox.checking_guest_additions")) # Use the raw interface for now, while the virtualbox gem # doesn't support guest properties (due to cross platform issues) version = env[:machine].provider.driver.read_guest_additions_version if !version env[:ui].detail(I18n.t("vagrant.actions.vm.check_guest_additions.not_detected")) else # Read the versions versions = [version, env[:machine].provider.driver.version] # Strip of any -OSE or _OSE and read only the first two parts # of the version such as "4.2" in "4.2.0" versions.map! do |v| v = v.gsub(/[-_]ose/i, '') match = /^(\d+\.\d+)\.(\d+)/.match(v) v = match[1] if match v end guest_version = versions[0] vb_version = versions[1] if guest_version != vb_version env[:ui].detail(I18n.t("vagrant.actions.vm.check_guest_additions.version_mismatch", guest_version: version, virtualbox_version: vb_version)) end end # Continue @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/check_running.rb000066400000000000000000000010001323370221500254560ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action # This middleware checks that the VM is running, and raises an exception # if it is not, notifying the user that the VM must be running. class CheckRunning def initialize(app, env) @app = app end def call(env) if env[:machine].state.id != :running raise Vagrant::Errors::VMNotRunningError end @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/check_virtualbox.rb000066400000000000000000000011241323370221500262040ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action # Checks that VirtualBox is installed and ready to be used. class CheckVirtualbox def initialize(app, env) @app = app end def call(env) # This verifies that VirtualBox is installed and the driver is # ready to function. If not, then an exception will be raised # which will break us out of execution of the middleware sequence. Driver::Meta.new.verify! # Carry on. @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/clean_machine_folder.rb000066400000000000000000000025161323370221500267570ustar00rootroot00000000000000require "fileutils" module VagrantPlugins module ProviderVirtualBox module Action # Cleans up the VirtualBox machine folder for any ".xml-prev" # files which VirtualBox may have left over. This is a bug in # VirtualBox. As soon as this is fixed, this middleware can and # will be removed. class CleanMachineFolder def initialize(app, env) @app = app end def call(env) clean_machine_folder(env[:machine].provider.driver.read_machine_folder) @app.call(env) end def clean_machine_folder(machine_folder) folder = File.join(machine_folder, "*") # Small safeguard against potentially unwanted rm-rf, since the default # machine folder will typically always be greater than 10 characters long. # For users with it < 10, out of luck? return if folder.length < 10 Dir[folder].each do |f| next unless File.directory?(f) keep = Dir["#{f}/**/*"].find do |d| # Find a file that doesn't have ".xml-prev" as the suffix, # which signals that we want to keep this folder File.file?(d) && !(File.basename(d) =~ /\.vbox-prev$/) end FileUtils.rm_rf(f) if !keep end end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/clear_forwarded_ports.rb000066400000000000000000000007511323370221500272270ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class ClearForwardedPorts def initialize(app, env) @app = app end def call(env) if !env[:machine].provider.driver.read_forwarded_ports.empty? env[:ui].info I18n.t("vagrant.actions.vm.clear_forward_ports.deleting") env[:machine].provider.driver.clear_forwarded_ports end @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/clear_network_interfaces.rb000066400000000000000000000016221323370221500277150ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class ClearNetworkInterfaces def initialize(app, env) @app = app end def call(env) # Create the adapters array to make all adapters nothing. # We do adapters 2 to 8 because that is every built-in adapter # excluding the NAT adapter on port 1 which Vagrant always # expects to exist. adapters = [] 2.upto(env[:machine].provider.driver.max_network_adapters).each do |i| adapters << { adapter: i, type: :none } end # "Enable" all the adapters we setup. env[:ui].info I18n.t("vagrant.actions.vm.clear_network_interfaces.deleting") env[:machine].provider.driver.enable_adapters(adapters) @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/created.rb000066400000000000000000000010021323370221500242520ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class Created def initialize(app, env) @app = app end def call(env) # Set the result to be true if the machine is created. env[:result] = env[:machine].state.id != :not_created # Call the next if we have one (but we shouldn't, since this # middleware is built to run with the Call-type middlewares) @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/customize.rb000066400000000000000000000023631323370221500247000ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class Customize def initialize(app, env, event) @app = app @event = event end def call(env) customizations = [] env[:machine].provider_config.customizations.each do |event, command| if event == @event customizations << command end end if !customizations.empty? env[:ui].info I18n.t("vagrant.actions.vm.customize.running", event: @event) # Execute each customization command. customizations.each do |command| processed_command = command.collect do |arg| arg = env[:machine].id if arg == :id arg.to_s end begin env[:machine].provider.driver.execute_command( processed_command + [retryable: true]) rescue Vagrant::Errors::VBoxManageError => e raise Vagrant::Errors::VMCustomizationFailed, { command: command, error: e.inspect } end end end @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/destroy.rb000066400000000000000000000006131323370221500243430ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class Destroy def initialize(app, env) @app = app end def call(env) env[:ui].info I18n.t("vagrant.actions.vm.destroy.destroying") env[:machine].provider.driver.delete env[:machine].id = nil @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/destroy_unused_network_interfaces.rb000066400000000000000000000011421323370221500317000ustar00rootroot00000000000000require "log4r" module VagrantPlugins module ProviderVirtualBox module Action class DestroyUnusedNetworkInterfaces def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::plugins::virtualbox::destroy_unused_netifs") end def call(env) if env[:machine].provider_config.destroy_unused_network_interfaces @logger.info("Destroying unused network interfaces...") env[:machine].provider.driver.delete_unused_host_only_networks end @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/discard_state.rb000066400000000000000000000007031323370221500254630ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class DiscardState def initialize(app, env) @app = app end def call(env) if env[:machine].state.id == :saved env[:ui].info I18n.t("vagrant.actions.vm.discard_state.discarding") env[:machine].provider.driver.discard_saved_state end @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/export.rb000066400000000000000000000016311323370221500241740ustar00rootroot00000000000000require "fileutils" module VagrantPlugins module ProviderVirtualBox module Action class Export def initialize(app, env) @app = app end def call(env) @env = env raise Vagrant::Errors::VMPowerOffToPackage if \ @env[:machine].state.id != :poweroff export @app.call(env) end def export @env[:ui].info I18n.t("vagrant.actions.vm.export.exporting") @env[:machine].provider.driver.export(ovf_path) do |progress| @env[:ui].clear_line @env[:ui].report_progress(progress.percent, 100, false) end # Clear the line a final time so the next data can appear # alone on the line. @env[:ui].clear_line end def ovf_path File.join(@env["export.temp_dir"], "box.ovf") end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/forced_halt.rb000066400000000000000000000012121323370221500251200ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class ForcedHalt def initialize(app, env) @app = app end def call(env) current_state = env[:machine].state.id if current_state == :running || current_state == :gurumeditation env[:ui].info I18n.t("vagrant.actions.vm.halt.force") env[:machine].provider.driver.halt end # Sleep for a second to verify that the VM properly # cleans itself up. Silly VirtualBox. sleep 1 if !env["vagrant.test"] @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/forward_ports.rb000066400000000000000000000057361323370221500255600ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class ForwardPorts include Util::CompileForwardedPorts def initialize(app, env) @app = app end #-------------------------------------------------------------- # Execution #-------------------------------------------------------------- def call(env) @env = env # Get the ports we're forwarding env[:forwarded_ports] ||= compile_forwarded_ports(env[:machine].config) # Warn if we're port forwarding to any privileged ports... env[:forwarded_ports].each do |fp| if fp.host_port <= 1024 env[:ui].warn I18n.t("vagrant.actions.vm.forward_ports.privileged_ports") break end end env[:ui].output(I18n.t("vagrant.actions.vm.forward_ports.forwarding")) forward_ports @app.call(env) end def forward_ports ports = [] interfaces = @env[:machine].provider.driver.read_network_interfaces @env[:forwarded_ports].each do |fp| message_attributes = { adapter: fp.adapter, guest_port: fp.guest_port, host_port: fp.host_port } # Assuming the only reason to establish port forwarding is # because the VM is using Virtualbox NAT networking. Host-only # bridged networking don't require port-forwarding and establishing # forwarded ports on these attachment types has uncertain behaviour. @env[:ui].detail(I18n.t("vagrant.actions.vm.forward_ports.forwarding_entry", message_attributes)) # Verify we have the network interface to attach to if !interfaces[fp.adapter] raise Vagrant::Errors::ForwardPortAdapterNotFound, adapter: fp.adapter.to_s, guest: fp.guest_port.to_s, host: fp.host_port.to_s end # Port forwarding requires the network interface to be a NAT interface, # so verify that that is the case. if interfaces[fp.adapter][:type] != :nat @env[:ui].detail(I18n.t("vagrant.actions.vm.forward_ports.non_nat", message_attributes)) next end # Add the options to the ports array to send to the driver later ports << { adapter: fp.adapter, guestip: fp.guest_ip, guestport: fp.guest_port, hostip: fp.host_ip, hostport: fp.host_port, name: fp.id, protocol: fp.protocol } end if !ports.empty? # We only need to forward ports if there are any to forward @env[:machine].provider.driver.forward_ports(ports) end end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/import.rb000066400000000000000000000061271323370221500241720ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class Import def initialize(app, env) @app = app end def call(env) if env[:clone_id] clone(env) else import(env) end end def clone(env) # Do the actual clone env[:ui].info I18n.t("vagrant.actions.vm.clone.creating") env[:machine].id = env[:machine].provider.driver.clonevm( env[:clone_id], env[:clone_snapshot]) do |progress| env[:ui].clear_line env[:ui].report_progress(progress, 100, false) end # Clear the line one last time since the progress meter doesn't # disappear immediately. env[:ui].clear_line # Flag as erroneous and return if clone failed raise Vagrant::Errors::VMCloneFailure if !env[:machine].id # Copy the SSH key from the clone machine if we can if env[:clone_machine] key_path = env[:clone_machine].data_dir.join("private_key") if key_path.file? FileUtils.cp( key_path, env[:machine].data_dir.join("private_key")) end end # Continue @app.call(env) end def import(env) env[:ui].info I18n.t("vagrant.actions.vm.import.importing", name: env[:machine].box.name) # Import the virtual machine ovf_file = env[:machine].box.directory.join("box.ovf").to_s id = env[:machine].provider.driver.import(ovf_file) do |progress| env[:ui].clear_line env[:ui].report_progress(progress, 100, false) end # Set the machine ID env[:machine_id] = id env[:machine].id = id if !env[:skip_machine] # Clear the line one last time since the progress meter doesn't disappear # immediately. env[:ui].clear_line # If we got interrupted, then the import could have been # interrupted and its not a big deal. Just return out. return if env[:interrupted] # Flag as erroneous and return if import failed raise Vagrant::Errors::VMImportFailure if !id # Import completed successfully. Continue the chain @app.call(env) end def recover(env) if env[:machine] && env[:machine].state.id != :not_created return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError) # If we're not supposed to destroy on error then just return return if !env[:destroy_on_error] # Interrupted, destroy the VM. We note that we don't want to # validate the configuration here, and we don't want to confirm # we want to destroy. destroy_env = env.clone destroy_env[:config_validate] = false destroy_env[:force_confirm_destroy] = true env[:action_runner].run(Action.action_destroy, destroy_env) end end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/import_master.rb000066400000000000000000000053141323370221500255420ustar00rootroot00000000000000require "log4r" require "digest/md5" module VagrantPlugins module ProviderVirtualBox module Action class ImportMaster def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::vm::create_master") end def call(env) # If we don't have a box, nothing to do if !env[:machine].box return @app.call(env) end # Do the import while locked so that nobody else imports # a master at the same time. This is a no-op if we already # have a master that exists. lock_key = Digest::MD5.hexdigest(env[:machine].box.name) env[:machine].env.lock(lock_key, retry: true) do import_master(env) end # If we got interrupted, then the import could have been # interrupted and its not a big deal. Just return out. if env[:interrupted] @logger.info("Import of master VM was interrupted -> exiting.") return end # Import completed successfully. Continue the chain @app.call(env) end protected def import_master(env) master_id_file = env[:machine].box.directory.join("master_id") # Read the master ID if we have it in the file. env[:clone_id] = master_id_file.read.chomp if master_id_file.file? # If we have the ID and the VM exists already, then we # have nothing to do. Success! if env[:clone_id] && env[:machine].provider.driver.vm_exists?(env[:clone_id]) @logger.info( "Master VM for '#{env[:machine].box.name}' already exists " + " (id=#{env[:clone_id]}) - skipping import step.") return else if env.delete(:clone_id) @logger.info("Deleting previous reference for master VM" + "'#{env[:machine].box.name}' (id=#{env[:clone_id]}) - " + "maybe it was removed manually") end end env[:ui].info(I18n.t("vagrant.actions.vm.clone.setup_master")) env[:ui].detail(I18n.t("vagrant.actions.vm.clone.setup_master_detail")) # Import the virtual machine import_env = env[:action_runner].run(Import, env.dup.merge(skip_machine: true)) env[:clone_id] = import_env[:machine_id] @logger.info( "Imported box #{env[:machine].box.name} as master vm " + "with id #{env[:clone_id]}") @logger.debug("Writing id of master VM '#{env[:clone_id]}' to #{master_id_file}") master_id_file.open("w+") do |f| f.write(env[:clone_id]) end end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/is_paused.rb000066400000000000000000000007751323370221500246370ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class IsPaused def initialize(app, env) @app = app end def call(env) # Set the result to be true if the machine is paused. env[:result] = env[:machine].state.id == :paused # Call the next if we have one (but we shouldn't, since this # middleware is built to run with the Call-type middlewares) @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/is_running.rb000066400000000000000000000010001323370221500250140ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class IsRunning def initialize(app, env) @app = app end def call(env) # Set the result to be true if the machine is running. env[:result] = env[:machine].state.id == :running # Call the next if we have one (but we shouldn't, since this # middleware is built to run with the Call-type middlewares) @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/is_saved.rb000066400000000000000000000007721323370221500244550ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class IsSaved def initialize(app, env) @app = app end def call(env) # Set the result to be true if the machine is saved. env[:result] = env[:machine].state.id == :saved # Call the next if we have one (but we shouldn't, since this # middleware is built to run with the Call-type middlewares) @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/match_mac_address.rb000066400000000000000000000013221323370221500262710ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class MatchMACAddress def initialize(app, env) @app = app end def call(env) # If we cloned, we don't need a base mac, it is already set! return @app.call(env) if env[:machine].config.vm.clone raise Vagrant::Errors::VMBaseMacNotSpecified if !env[:machine].config.vm.base_mac # Create the proc which we want to use to modify the virtual machine env[:ui].info I18n.t("vagrant.actions.vm.match_mac.matching") env[:machine].provider.driver.set_mac_address(env[:machine].config.vm.base_mac) @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/message_already_running.rb000066400000000000000000000005151323370221500275400ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class MessageAlreadyRunning def initialize(app, env) @app = app end def call(env) env[:ui].info I18n.t("vagrant.commands.common.vm_already_running") @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/message_not_created.rb000066400000000000000000000005051323370221500266450ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class MessageNotCreated def initialize(app, env) @app = app end def call(env) env[:ui].info I18n.t("vagrant.commands.common.vm_not_created") @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/message_not_running.rb000066400000000000000000000005051323370221500267160ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class MessageNotRunning def initialize(app, env) @app = app end def call(env) env[:ui].info I18n.t("vagrant.commands.common.vm_not_running") @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/message_will_not_destroy.rb000066400000000000000000000006041323370221500277560ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class MessageWillNotDestroy def initialize(app, env) @app = app end def call(env) env[:ui].info I18n.t("vagrant.commands.destroy.will_not_destroy", name: env[:machine].name) @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/network.rb000066400000000000000000000506521323370221500243530ustar00rootroot00000000000000require "ipaddr" require "resolv" require "set" require "log4r" require "vagrant/util/network_ip" require "vagrant/util/scoped_hash_override" module VagrantPlugins module ProviderVirtualBox module Action # This middleware class sets up all networking for the VirtualBox # instance. This includes host only networks, bridged networking, # forwarded ports, etc. # # This handles all the `config.vm.network` configurations. class Network include Vagrant::Util::NetworkIP include Vagrant::Util::ScopedHashOverride def initialize(app, env) @logger = Log4r::Logger.new("vagrant::plugins::virtualbox::network") @app = app end def call(env) # TODO: Validate network configuration prior to anything below @env = env # Get the list of network adapters from the configuration network_adapters_config = env[:machine].provider_config.network_adapters.dup # Assign the adapter slot for each high-level network available_slots = Set.new(1..36) network_adapters_config.each do |slot, _data| available_slots.delete(slot) end @logger.debug("Available slots for high-level adapters: #{available_slots.inspect}") @logger.info("Determining network adapters required for high-level configuration...") available_slots = available_slots.to_a.sort env[:machine].config.vm.networks.each do |type, options| # We only handle private and public networks next if type != :private_network && type != :public_network options = scoped_hash_override(options, :virtualbox) # Figure out the slot that this adapter will go into slot = options[:adapter] if !slot if available_slots.empty? raise Vagrant::Errors::VirtualBoxNoRoomForHighLevelNetwork end slot = available_slots.shift end # Internal network is a special type if type == :private_network && options[:intnet] type = :internal_network end # Configure it data = nil if type == :private_network # private_network = hostonly data = [:hostonly, options] elsif type == :public_network # public_network = bridged data = [:bridged, options] elsif type == :internal_network data = [:intnet, options] end # Store it! @logger.info(" -- Slot #{slot}: #{data[0]}") network_adapters_config[slot] = data end @logger.info("Determining adapters and compiling network configuration...") adapters = [] networks = [] network_adapters_config.each do |slot, data| type = data[0] options = data[1] @logger.info("Network slot #{slot}. Type: #{type}.") # Get the normalized configuration for this type config = send("#{type}_config", options) config[:adapter] = slot @logger.debug("Normalized configuration: #{config.inspect}") # Get the VirtualBox adapter configuration adapter = send("#{type}_adapter", config) adapters << adapter @logger.debug("Adapter configuration: #{adapter.inspect}") # Get the network configuration network = send("#{type}_network_config", config) network[:auto_config] = config[:auto_config] networks << network end if !adapters.empty? # Enable the adapters @logger.info("Enabling adapters...") env[:ui].output(I18n.t("vagrant.actions.vm.network.preparing")) adapters.each do |adapter| env[:ui].detail(I18n.t( "vagrant.virtualbox.network_adapter", adapter: adapter[:adapter].to_s, type: adapter[:type].to_s, extra: "", )) end env[:machine].provider.driver.enable_adapters(adapters) end # Continue the middleware chain. @app.call(env) # If we have networks to configure, then we configure it now, since # that requires the machine to be up and running. if !adapters.empty? && !networks.empty? assign_interface_numbers(networks, adapters) # Only configure the networks the user requested us to configure networks_to_configure = networks.select { |n| n[:auto_config] } if !networks_to_configure.empty? env[:ui].info I18n.t("vagrant.actions.vm.network.configuring") env[:machine].guest.capability(:configure_networks, networks_to_configure) end end end def bridged_config(options) return { auto_config: true, bridge: nil, mac: nil, nic_type: nil, use_dhcp_assigned_default_route: false }.merge(options || {}) end def bridged_adapter(config) # Find the bridged interfaces that are available bridgedifs = @env[:machine].provider.driver.read_bridged_interfaces bridgedifs.delete_if { |interface| interface[:status] == "Down" || interface[:status] == "Unknown" } # The name of the chosen bridge interface will be assigned to this # variable. chosen_bridge = nil if config[:bridge] @logger.debug("Bridge was directly specified in config, searching for: #{config[:bridge]}") # Search for a matching bridged interface Array(config[:bridge]).each do |bridge| bridge = bridge.downcase if bridge.respond_to?(:downcase) bridgedifs.each do |interface| if bridge === interface[:name].downcase @logger.debug("Specific bridge found as configured in the Vagrantfile. Using it.") chosen_bridge = interface[:name] break end end break if chosen_bridge end # If one wasn't found, then we notify the user here. if !chosen_bridge @env[:ui].info I18n.t("vagrant.actions.vm.bridged_networking.specific_not_found", bridge: config[:bridge]) end end # If we still don't have a bridge chosen (this means that one wasn't # specified in the Vagrantfile, or the bridge specified in the Vagrantfile # wasn't found), then we fall back to the normal means of searchign for a # bridged network. if !chosen_bridge if bridgedifs.length == 1 # One bridgable interface? Just use it. chosen_bridge = bridgedifs[0][:name] @logger.debug("Only one bridged interface available. Using it by default.") else # More than one bridgable interface requires a user decision, so # show options to choose from. @env[:ui].info I18n.t( "vagrant.actions.vm.bridged_networking.available", prefix: false) bridgedifs.each_index do |index| interface = bridgedifs[index] @env[:ui].info("#{index + 1}) #{interface[:name]}", prefix: false) end @env[:ui].info(I18n.t( "vagrant.actions.vm.bridged_networking.choice_help")+"\n") # The range of valid choices valid = Range.new(1, bridgedifs.length) # The choice that the user has chosen as the bridging interface choice = nil while !valid.include?(choice) choice = @env[:ui].ask( "Which interface should the network bridge to? ") choice = choice.to_i end chosen_bridge = bridgedifs[choice - 1][:name] end end @logger.info("Bridging adapter #{config[:adapter]} to #{chosen_bridge}") # Given the choice we can now define the adapter we're using return { adapter: config[:adapter], type: :bridged, bridge: chosen_bridge, mac_address: config[:mac], nic_type: config[:nic_type] } end def bridged_network_config(config) if config[:ip] options = { auto_config: true, mac: nil, netmask: "255.255.255.0", type: :static }.merge(config) options[:type] = options[:type].to_sym return options end return { type: :dhcp, use_dhcp_assigned_default_route: config[:use_dhcp_assigned_default_route] } end def hostonly_config(options) options = { auto_config: true, mac: nil, nic_type: nil, type: :static, }.merge(options) # Make sure the type is a symbol options[:type] = options[:type].to_sym # Default IP is in the 20-bit private network block for DHCP based networks options[:ip] = "172.28.128.1" if options[:type] == :dhcp && !options[:ip] begin ip = IPAddr.new(options[:ip]) rescue IPAddr::InvalidAddressError => e raise Vagrant::Errors::NetworkAddressInvalid, :ip => options[:ip], :error_msg => e.message end if ip.ipv4? options[:netmask] ||= "255.255.255.0" # Calculate our network address for the given IP/netmask netaddr = network_address(options[:ip], options[:netmask]) # Verify that a host-only network subnet would not collide # with a bridged networking interface. # # If the subnets overlap in any way then the host only network # will not work because the routing tables will force the # traffic onto the real interface rather than the VirtualBox # interface. @env[:machine].provider.driver.read_bridged_interfaces.each do |interface| that_netaddr = network_address(interface[:ip], interface[:netmask]) raise Vagrant::Errors::NetworkCollision if \ netaddr == that_netaddr && interface[:status] != "Down" end # Split the IP address into its components ip_parts = netaddr.split(".").map { |i| i.to_i } # Calculate the adapter IP, which we assume is the IP ".1" at # the end usually. adapter_ip = ip_parts.dup adapter_ip[3] += 1 options[:adapter_ip] ||= adapter_ip.join(".") elsif ip.ipv6? # Default subnet prefix length options[:netmask] ||= 64 # Set adapter IP to ::1 options[:adapter_ip] ||= (ip.mask(options[:netmask].to_i) | 1).to_s # Append a 6 to the end of the type options[:type] = "#{options[:type]}6".to_sym else raise "BUG: Unknown IP type: #{ip.inspect}" end dhcp_options = {} if options[:type] == :dhcp # Calculate the DHCP server IP, which is the network address # with the final octet + 2. So "172.28.0.0" turns into "172.28.0.2" dhcp_ip = ip_parts.dup dhcp_ip[3] += 2 dhcp_options[:dhcp_ip] = options[:dhcp_ip] || dhcp_ip.join(".") # Calculate the lower and upper bound for the DHCP server dhcp_lower = ip_parts.dup dhcp_lower[3] += 3 dhcp_options[:dhcp_lower] = options[:dhcp_lower] || dhcp_lower.join(".") dhcp_upper = ip_parts.dup dhcp_upper[3] = 254 dhcp_options[:dhcp_upper] = options[:dhcp_upper] || dhcp_upper.join(".") end return { adapter_ip: options[:adapter_ip], auto_config: options[:auto_config], ip: options[:ip], mac: options[:mac], name: options[:name], netmask: options[:netmask], nic_type: options[:nic_type], type: options[:type] }.merge(dhcp_options) end def hostonly_adapter(config) @logger.info("Searching for matching hostonly network: #{config[:ip]}") interface = hostonly_find_matching_network(config) if !interface @logger.info("Network not found. Creating if we can.") # It is an error if a specific host only network name was specified # but the network wasn't found. if config[:name] raise Vagrant::Errors::NetworkNotFound, name: config[:name] end # Create a new network interface = hostonly_create_network(config) @logger.info("Created network: #{interface[:name]}") end if config[:type] == :dhcp create_dhcp_server_if_necessary(interface, config) end return { adapter: config[:adapter], hostonly: interface[:name], mac_address: config[:mac], nic_type: config[:nic_type], type: :hostonly } end def hostonly_network_config(config) return { type: config[:type], adapter_ip: config[:adapter_ip], ip: config[:ip], netmask: config[:netmask] } end def intnet_config(options) return { type: "static", ip: nil, netmask: "255.255.255.0", adapter: nil, mac: nil, intnet: nil, auto_config: true }.merge(options || {}) end def intnet_adapter(config) intnet_name = config[:intnet] intnet_name = "intnet" if intnet_name == true return { adapter: config[:adapter], type: :intnet, mac_address: config[:mac], nic_type: config[:nic_type], intnet: intnet_name, } end def intnet_network_config(config) return { type: config[:type], ip: config[:ip], netmask: config[:netmask] } end def nat_config(options) return { auto_config: false } end def nat_adapter(config) return { adapter: config[:adapter], type: :nat, } end def nat_network_config(config) return {} end #----------------------------------------------------------------- # Misc. helpers #----------------------------------------------------------------- # Assigns the actual interface number of a network based on the # enabled NICs on the virtual machine. # # This interface number is used by the guest to configure the # NIC on the guest VM. # # The networks are modified in place by adding an ":interface" # field to each. def assign_interface_numbers(networks, adapters) current = 0 adapter_to_interface = {} # Make a first pass to assign interface numbers by adapter location vm_adapters = @env[:machine].provider.driver.read_network_interfaces vm_adapters.sort.each do |number, adapter| if adapter[:type] != :none # Not used, so assign the interface number and increment adapter_to_interface[number] = current current += 1 end end # Make a pass through the adapters to assign the :interface # key to each network configuration. adapters.each_index do |i| adapter = adapters[i] network = networks[i] # Figure out the interface number by simple lookup network[:interface] = adapter_to_interface[adapter[:adapter]] end end #----------------------------------------------------------------- # Hostonly Helper Functions #----------------------------------------------------------------- # This creates a host only network for the given configuration. def hostonly_create_network(config) @env[:machine].provider.driver.create_host_only_network( adapter_ip: config[:adapter_ip], netmask: config[:netmask] ) end # This finds a matching host only network for the given configuration. def hostonly_find_matching_network(config) this_netaddr = network_address(config[:ip], config[:netmask]) @env[:machine].provider.driver.read_host_only_interfaces.each do |interface| return interface if config[:name] && config[:name] == interface[:name] if interface[:ip] != "" return interface if this_netaddr == \ network_address(interface[:ip], interface[:netmask]) end if interface[:ipv6] != "" return interface if this_netaddr == \ network_address(interface[:ipv6], interface[:ipv6_prefix]) end end nil end #----------------------------------------------------------------- # DHCP Server Helper Functions #----------------------------------------------------------------- DEFAULT_DHCP_SERVER_FROM_VBOX_INSTALL = { network_name: 'HostInterfaceNetworking-vboxnet0', network: 'vboxnet0', ip: '192.168.56.100', netmask: '255.255.255.0', lower: '192.168.56.101', upper: '192.168.56.254' }.freeze # # When a host-only network of type: :dhcp is configured, # this handles the potential creation of a vbox dhcpserver to manage # it. # # @param [Hash] interface hash as returned from read_host_only_interfaces # @param [Hash] config hash as returned from hostonly_config def create_dhcp_server_if_necessary(interface, config) existing_dhcp_server = find_matching_dhcp_server(interface) if existing_dhcp_server if dhcp_server_matches_config?(existing_dhcp_server, config) @logger.debug("DHCP server already properly configured") return elsif existing_dhcp_server == DEFAULT_DHCP_SERVER_FROM_VBOX_INSTALL @env[:ui].info I18n.t("vagrant.actions.vm.network.cleanup_vbox_default_dhcp") @env[:machine].provider.driver.remove_dhcp_server(existing_dhcp_server[:network_name]) else # We have an invalid DHCP server that we're not able to # automatically clean up, so we need to give up and tell the user # to sort out their own vbox dhcpservers and hostonlyifs raise Vagrant::Errors::NetworkDHCPAlreadyAttached end end @logger.debug("Creating a DHCP server...") @env[:machine].provider.driver.create_dhcp_server(interface[:name], config) end # Detect when an existing DHCP server matches precisely the # requested config for a hostonly interface. # # @param [Hash] dhcp_server as found by read_dhcp_servers # @param [Hash] config as returned from hostonly_config # @return [Boolean] def dhcp_server_matches_config?(dhcp_server, config) dhcp_server[:ip] == config[:dhcp_ip] && dhcp_server[:lower] == config[:dhcp_lower] && dhcp_server[:upper] == config[:dhcp_upper] end # Returns the existing dhcp server, if any, that is attached to the # specified interface. # # @return [Hash] dhcp_server or nil if not found def find_matching_dhcp_server(interface) @env[:machine].provider.driver.read_dhcp_servers.detect do |dhcp_server| interface[:name] && interface[:name] == dhcp_server[:network] end end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/network_fix_ipv6.rb000066400000000000000000000047341323370221500261650ustar00rootroot00000000000000require "ipaddr" require "socket" require "log4r" require "vagrant/util/presence" require "vagrant/util/scoped_hash_override" module VagrantPlugins module ProviderVirtualBox module Action # This middleware works around a bug in VirtualBox where booting # a VM with an IPv6 host-only network will someties lose the # route to that machine. class NetworkFixIPv6 include Vagrant::Util::Presence include Vagrant::Util::ScopedHashOverride def initialize(app, env) @logger = Log4r::Logger.new("vagrant::plugins::virtualbox::network") @app = app end def call(env) @env = env # Determine if we have an IPv6 network has_v6 = false env[:machine].config.vm.networks.each do |type, options| next if type != :private_network options = scoped_hash_override(options, :virtualbox) next if options[:ip].to_s.strip == "" if IPAddr.new(options[:ip]).ipv6? has_v6 = true break end end # Call up @app.call(env) # If we have no IPv6, forget it return if !has_v6 host_only_interfaces(env).each do |interface| next if !present?(interface[:ipv6]) next if interface[:status] != "Up" ip = IPAddr.new(interface[:ipv6]) ip |= ("1" * (128 - interface[:ipv6_prefix].to_i)).to_i(2) @logger.info("testing IPv6: #{ip}") begin UDPSocket.new(Socket::AF_INET6).connect(ip.to_s, 80) rescue Errno::EHOSTUNREACH @logger.info("IPv6 host unreachable. Fixing: #{ip}") env[:machine].provider.driver.reconfig_host_only(interface) end end end # The list of interface names for host-only adapters. # @return [Array] def host_only_interface_names(env) env[:machine].provider.driver.read_network_interfaces .map { |_, i| i[:hostonly] if i[:type] == :hostonly }.compact end # The list of host_only_interfaces that are tied to a host-only adapter. # @return [Array] def host_only_interfaces(env) iface_names = self.host_only_interface_names(env) env[:machine].provider.driver.read_host_only_interfaces .select { |interface| iface_names.include?(interface[:name]) } end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/package.rb000066400000000000000000000006541323370221500242520ustar00rootroot00000000000000require_relative "../../../../lib/vagrant/action/general/package" module VagrantPlugins module ProviderVirtualBox module Action class Package < Vagrant::Action::General::Package # Doing this so that we can test that the parent is properly # called in the unit tests. alias_method :general_call, :call def call(env) general_call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/package_setup_files.rb000066400000000000000000000007141323370221500266510ustar00rootroot00000000000000require_relative "../../../../lib/vagrant/action/general/package_setup_files" module VagrantPlugins module ProviderVirtualBox module Action class PackageSetupFiles < Vagrant::Action::General::PackageSetupFiles # Doing this so that we can test that the parent is properly # called in the unit tests. alias_method :general_call, :call def call(env) general_call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/package_setup_folders.rb000066400000000000000000000007471323370221500272130ustar00rootroot00000000000000require "fileutils" require_relative "../../../../lib/vagrant/action/general/package_setup_folders" module VagrantPlugins module ProviderVirtualBox module Action class PackageSetupFolders < Vagrant::Action::General::PackageSetupFolders # Doing this so that we can test that the parent is properly # called in the unit tests. alias_method :general_call, :call def call(env) general_call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/package_vagrantfile.rb000066400000000000000000000016051323370221500266310ustar00rootroot00000000000000require 'vagrant/util/template_renderer' module VagrantPlugins module ProviderVirtualBox module Action class PackageVagrantfile # For TemplateRenderer include Vagrant::Util def initialize(app, env) @app = app end def call(env) @env = env create_vagrantfile @app.call(env) end # This method creates the auto-generated Vagrantfile at the root of the # box. This Vagrantfile contains the MAC address so that the user doesn't # have to worry about it. def create_vagrantfile File.open(File.join(@env["export.temp_dir"], "Vagrantfile"), "w") do |f| f.write(TemplateRenderer.render("package_Vagrantfile", { base_mac: @env[:machine].provider.driver.read_mac_address })) end end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/prepare_clone_snapshot.rb000066400000000000000000000035431323370221500274140ustar00rootroot00000000000000require "log4r" require "digest/md5" module VagrantPlugins module ProviderVirtualBox module Action class PrepareCloneSnapshot def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::vm::prepare_clone") end def call(env) if !env[:clone_id] @logger.info("no clone master, not preparing clone snapshot") return @app.call(env) end # If we're not doing a linked clone, snapshots don't matter if !env[:machine].provider_config.linked_clone return @app.call(env) end # We lock so that we don't snapshot in parallel lock_key = Digest::MD5.hexdigest("#{env[:clone_id]}-snapshot") env[:machine].env.lock(lock_key, retry: true) do prepare_snapshot(env) end # Continue @app.call(env) end protected def prepare_snapshot(env) name = env[:machine].provider_config.linked_clone_snapshot name_set = !!name name = "base" if !name env[:clone_snapshot] = name # Get the snapshots. We're done if it already exists snapshots = env[:machine].provider.driver.list_snapshots(env[:clone_id]) if snapshots.include?(name) @logger.info("clone snapshot already exists, doing nothing") return end # If they asked for a specific snapshot, it is an error if name_set # TODO: Error end @logger.info("Creating base snapshot for master VM.") env[:machine].provider.driver.create_snapshot( env[:clone_id], name) do |progress| env[:ui].clear_line env[:ui].report_progress(progress, 100, false) end end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/prepare_forwarded_port_collision_params.rb000066400000000000000000000020711323370221500330270ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class PrepareForwardedPortCollisionParams def initialize(app, env) @app = app end def call(env) # Get the forwarded ports used by other virtual machines and # consider those in use as well. env[:port_collision_extra_in_use] = env[:machine].provider.driver.read_used_ports # Build the remap for any existing collision detections remap = {} env[:port_collision_remap] = remap env[:machine].provider.driver.read_forwarded_ports.each do |_nic, name, hostport, _guestport| env[:machine].config.vm.networks.each do |type, options| next if type != :forwarded_port # If the ID matches the name of the forwarded port, then # remap. if options[:id] == name remap[options[:host]] = hostport break end end end @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/prepare_nfs_settings.rb000066400000000000000000000127631323370221500271070ustar00rootroot00000000000000require "ipaddr" require "vagrant/action/builtin/mixin_synced_folders" module VagrantPlugins module ProviderVirtualBox module Action class PrepareNFSSettings include Vagrant::Action::Builtin::MixinSyncedFolders include Vagrant::Util::Retryable def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::vm::nfs") end def call(env) @machine = env[:machine] @app.call(env) opts = { cached: !!env[:synced_folders_cached], config: env[:synced_folders_config], disable_usable_check: !!env[:test], } folders = synced_folders(env[:machine], **opts) if folders.key?(:nfs) @logger.info("Using NFS, preparing NFS settings by reading host IP and machine IP") add_ips_to_env!(env) end end # Extracts the proper host and guest IPs for NFS mounts and stores them # in the environment for the SyncedFolder action to use them in # mounting. # # The ! indicates that this method modifies its argument. def add_ips_to_env!(env) adapter, host_ip = find_host_only_adapter machine_ip = read_static_machine_ips if !machine_ip # No static IP, attempt to use the dynamic IP. machine_ip = read_dynamic_machine_ip(adapter) else # We have static IPs, also attempt to read any dynamic IPs. # If there is no dynamic IP on the adapter, it doesn't matter. We # already have a static IP. begin dynamic_ip = read_dynamic_machine_ip(adapter) rescue Vagrant::Errors::NFSNoGuestIP dynamic_ip = nil end # If we found a dynamic IP and we didn't include it in the # machine_ip array yet, do so. if dynamic_ip && !machine_ip.include?(dynamic_ip) machine_ip.push(dynamic_ip) end end if host_ip && !machine_ip.empty? interface = @machine.provider.driver.read_host_only_interfaces.detect do |iface| iface[:ip] == host_ip end host_ipaddr = IPAddr.new("#{host_ip}/#{interface.fetch(:netmask, "0.0.0.0")}") case machine_ip when String machine_ip = nil if !host_ipaddr.include?(machine_ip) when Array machine_ip.delete_if do |m_ip| !host_ipaddr.include?(m_ip) end machine_ip = nil if machine_ip.empty? end end raise Vagrant::Errors::NFSNoHostonlyNetwork if !host_ip || !machine_ip env[:nfs_host_ip] = host_ip env[:nfs_machine_ip] = machine_ip end # Finds first host only network adapter and returns its adapter number # and IP address # # @return [Integer, String] adapter number, ip address of found host-only adapter def find_host_only_adapter @machine.provider.driver.read_network_interfaces.each do |adapter, opts| if opts[:type] == :hostonly @machine.provider.driver.read_host_only_interfaces.each do |interface| if interface[:name] == opts[:hostonly] return adapter, interface[:ip] end end end end nil end # Returns the IP address(es) of the guest by looking for static IPs # given to host only adapters in the Vagrantfile # # @return [Array] Configured static IPs def read_static_machine_ips ips = [] @machine.config.vm.networks.each do |type, options| options = scoped_hash_override(options, :virtualbox) if type == :private_network && options[:type] != :dhcp && options[:ip].is_a?(String) ips << options[:ip] end end if ips.empty? return nil end ips end # Returns the IP address of the guest by looking at vbox guest property # for the appropriate guest adapter. # # For DHCP interfaces, the guest property will not be present until the # guest completes # # @param [Integer] adapter number to read IP for # @return [String] ip address of adapter def read_dynamic_machine_ip(adapter) return nil unless adapter # vbox guest properties are 0-indexed, while showvminfo network # interfaces are 1-indexed. go figure. guestproperty_adapter = adapter - 1 # we need to wait for the guest's IP to show up as a guest property. # retry thresholds are relatively high since we might need to wait # for DHCP, but even static IPs can take a second or two to appear. retryable(retry_options.merge(on: Vagrant::Errors::VirtualBoxGuestPropertyNotFound)) do @machine.provider.driver.read_guest_ip(guestproperty_adapter) end rescue Vagrant::Errors::VirtualBoxGuestPropertyNotFound # this error is more specific with a better error message directing # the user towards the fact that it's probably a reportable bug raise Vagrant::Errors::NFSNoGuestIP end # Separating these out so we can stub out the sleep in tests def retry_options {tries: 15, sleep: 1} end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/prepare_nfs_valid_ids.rb000066400000000000000000000006151323370221500271760ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class PrepareNFSValidIds def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::vm::nfs") end def call(env) env[:nfs_valid_ids] = env[:machine].provider.driver.read_vms.values @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/resume.rb000066400000000000000000000011571323370221500241560ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class Resume def initialize(app, env) @app = app end def call(env) current_state = env[:machine].state.id if current_state == :paused env[:ui].info I18n.t("vagrant.actions.vm.resume.unpausing") env[:machine].provider.driver.resume elsif current_state == :saved env[:ui].info I18n.t("vagrant.actions.vm.resume.resuming") env[:action_runner].run(Boot, env) end @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/sane_defaults.rb000066400000000000000000000066031323370221500254740ustar00rootroot00000000000000require "log4r" module VagrantPlugins module ProviderVirtualBox module Action class SaneDefaults def initialize(app, env) @logger = Log4r::Logger.new("vagrant::action::vm::sanedefaults") @app = app end def call(env) # Set the env on an instance variable so we can access it in # helpers. @env = env # Use rtcuseutc so that the VM sees UTC time. command = ["modifyvm", env[:machine].id, "--rtcuseutc", "on"] attempt_and_log(command, "Enabling rtcuseutc...") if env[:machine].provider_config.auto_nat_dns_proxy @logger.info("Automatically figuring out whether to enable/disable NAT DNS proxy...") # Enable/disable the NAT DNS proxy as necessary if enable_dns_proxy? command = ["modifyvm", env[:machine].id, "--natdnsproxy1", "on"] attempt_and_log(command, "Enable the NAT DNS proxy on adapter 1...") else command = ["modifyvm", env[:machine].id, "--natdnsproxy1", "off" ] attempt_and_log(command, "Disable the NAT DNS proxy on adapter 1...") command = ["modifyvm", env[:machine].id, "--natdnshostresolver1", "off" ] attempt_and_log(command, "Disable the NAT DNS resolver on adapter 1...") end else @logger.info("NOT trying to automatically manage NAT DNS proxy.") end @app.call(env) end protected # This is just a helper method that executes a single command, logs # the given string to the log, and also includes the exit status in # the log message. # # We assume every command is idempotent and pass along the `retryable` # flag. This is because VBoxManage is janky about running simultaneously # on the same box, and if we up multiple boxes at the same time, a bunch # of modifyvm commands get fired # # @param [Array] command Command to run # @param [String] log Log message to write. def attempt_and_log(command, log) begin @env[:machine].provider.driver.execute_command( command + [retryable: true]) rescue Vagrant::Errors::VBoxManageError => e @logger.info("#{log} (error = #{e.inspect})") end end # This uses some heuristics to determine if the NAT DNS proxy should # be enabled or disabled. See the comments within the function body # itself to see the checks it does. # # @return [Boolean] def enable_dns_proxy? begin contents = File.read("/etc/resolv.conf") if contents =~ /^nameserver 127\.0\.(0|1)\.1$/ # The use of both natdnsproxy and natdnshostresolver break on # Ubuntu 12.04 and 12.10 that uses resolvconf with localhost. When used # VirtualBox will give the client dns server 10.0.2.3, while # not binding to that address itself. Therefore disable this # feature if host uses the resolvconf server 127.0.0.1 or # 127.0.1.1 @logger.info("Disabling DNS proxy since resolv.conf contains 127.0.0.1 or 127.0.1.1") return false end rescue Errno::ENOENT; end return true end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/set_name.rb000066400000000000000000000033531323370221500244510ustar00rootroot00000000000000require "log4r" module VagrantPlugins module ProviderVirtualBox module Action class SetName def initialize(app, env) @logger = Log4r::Logger.new("vagrant::action::vm::setname") @app = app end def call(env) name = env[:machine].provider_config.name # If we already set the name before, then don't do anything sentinel = env[:machine].data_dir.join("action_set_name") if !name && sentinel.file? @logger.info("Default name was already set before, not doing it again.") return @app.call(env) end # If no name was manually set, then use a default if !name prefix = "#{env[:root_path].basename.to_s}_#{env[:machine].name}" prefix.gsub!(/[^-a-z0-9_]/i, "") # milliseconds + random number suffix to allow for simultaneous # `vagrant up` of the same box in different dirs name = prefix + "_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}" end # Verify the name is not taken vms = env[:machine].provider.driver.read_vms raise Vagrant::Errors::VMNameExists, name: name if \ vms.key?(name) && vms[name] != env[:machine].id if vms.key?(name) @logger.info("Not setting the name because our name is already set.") else env[:ui].info(I18n.t( "vagrant.actions.vm.set_name.setting_name", name: name)) env[:machine].provider.driver.set_name(name) end # Create the sentinel sentinel.open("w") do |f| f.write(Time.now.to_i.to_s) end @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/setup_package_files.rb000066400000000000000000000006611323370221500266520ustar00rootroot00000000000000require "log4r" require_relative "package_setup_files" module VagrantPlugins module ProviderVirtualBox module Action class SetupPackageFiles < PackageSetupFiles def initialize(*) @logger = Log4r::Logger.new("vagrant::plugins::virtualbox::setup_package_files") @logger.warn { "SetupPackageFiles has been renamed to PackageSetupFiles" } super end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/snapshot_delete.rb000066400000000000000000000015441323370221500260370ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class SnapshotDelete def initialize(app, env) @app = app end def call(env) env[:ui].info(I18n.t( "vagrant.actions.vm.snapshot.deleting", name: env[:snapshot_name])) env[:machine].provider.driver.delete_snapshot( env[:machine].id, env[:snapshot_name]) do |progress| env[:ui].clear_line env[:ui].report_progress(progress, 100, false) end # Clear the line one last time since the progress meter doesn't disappear # immediately. env[:ui].clear_line env[:ui].success(I18n.t( "vagrant.actions.vm.snapshot.deleted", name: env[:snapshot_name])) @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/snapshot_restore.rb000066400000000000000000000013501323370221500262530ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class SnapshotRestore def initialize(app, env) @app = app end def call(env) env[:ui].info(I18n.t( "vagrant.actions.vm.snapshot.restoring", name: env[:snapshot_name])) env[:machine].provider.driver.restore_snapshot( env[:machine].id, env[:snapshot_name]) do |progress| env[:ui].clear_line env[:ui].report_progress(progress, 100, false) end # Clear the line one last time since the progress meter doesn't disappear # immediately. env[:ui].clear_line @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/snapshot_save.rb000066400000000000000000000011331323370221500255250ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class SnapshotSave def initialize(app, env) @app = app end def call(env) env[:ui].info(I18n.t( "vagrant.actions.vm.snapshot.saving", name: env[:snapshot_name])) env[:machine].provider.driver.create_snapshot( env[:machine].id, env[:snapshot_name]) env[:ui].success(I18n.t( "vagrant.actions.vm.snapshot.saved", name: env[:snapshot_name])) @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/action/suspend.rb000066400000000000000000000006551323370221500243410ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class Suspend def initialize(app, env) @app = app end def call(env) if env[:machine].state.id == :running env[:ui].info I18n.t("vagrant.actions.vm.suspend.suspending") env[:machine].provider.driver.suspend end @app.call(env) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/cap.rb000066400000000000000000000022451323370221500221430ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Cap # Reads the forwarded ports that currently exist on the machine # itself. This raises an exception if the machine isn't running. # # This also may not match up with configured forwarded ports, because # Vagrant auto port collision fixing may have taken place. # # @return [Hash] Host => Guest port mappings. def self.forwarded_ports(machine) return nil if machine.state.id != :running {}.tap do |result| machine.provider.driver.read_forwarded_ports.each do |_, _, h, g| result[h] = g end end end # Reads the network interface card MAC addresses and returns them. # # @return [Hash] Adapter => MAC address def self.nic_mac_addresses(machine) machine.provider.driver.read_mac_addresses end # Returns a list of the snapshots that are taken on this machine. # # @return [Array] Snapshot Name def self.snapshot_list(machine) machine.provider.driver.list_snapshots(machine.id) end end end end vagrant-2.0.2/plugins/providers/virtualbox/cap/000077500000000000000000000000001323370221500216135ustar00rootroot00000000000000vagrant-2.0.2/plugins/providers/virtualbox/cap/public_address.rb000066400000000000000000000005051323370221500251230ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Cap module PublicAddress def self.public_address(machine) return nil if machine.state.id != :running ssh_info = machine.ssh_info return nil if !ssh_info ssh_info[:host] end end end end end vagrant-2.0.2/plugins/providers/virtualbox/config.rb000066400000000000000000000145621323370221500226520ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox class Config < Vagrant.plugin("2", :config) # Vagrant by default will make "smart" decisions to enable/disable # the NAT DNS proxy. If this is set to `true`, then the DNS proxy # will not be enabled, and it is up to the end user to do it. # # @return [Boolean] attr_accessor :auto_nat_dns_proxy # If true, will check if guest additions are installed and up to # date. By default, this is true. # # @return [Boolean] attr_accessor :check_guest_additions # An array of customizations to make on the VM prior to booting it. # # @return [Array] attr_reader :customizations # If true, unused network interfaces will automatically be deleted. # This defaults to false because the detection does not work across # multiple users, and because on Windows this operation requires # administrative privileges. # # @return [Boolean] attr_accessor :destroy_unused_network_interfaces # If set to `true`, then VirtualBox will be launched with a GUI. # # @return [Boolean] attr_accessor :gui # If set to `true`, then a linked clone is created from a master # VM generated from the specified box. # # @return [Boolean] attr_accessor :linked_clone # The snapshot to base the linked clone from. If this isn't set # a snapshot will be made with the name of "base" which will be used. # # If this is set, then the snapshot must already exist. # # @return [String] attr_accessor :linked_clone_snapshot # This should be set to the name of the machine in the VirtualBox # GUI. # # @return [String] attr_accessor :name # Whether or not this VM has a functional vboxsf filesystem module. # This defaults to true. If you set this to false, then the "virtualbox" # synced folder type won't be valid. # # @return [Boolean] attr_accessor :functional_vboxsf # The defined network adapters. # # @return [Hash] attr_reader :network_adapters def initialize @auto_nat_dns_proxy = UNSET_VALUE @check_guest_additions = UNSET_VALUE @customizations = [] @destroy_unused_network_interfaces = UNSET_VALUE @functional_vboxsf = UNSET_VALUE @name = UNSET_VALUE @network_adapters = {} @gui = UNSET_VALUE @linked_clone = UNSET_VALUE @linked_clone_snapshot = UNSET_VALUE # We require that network adapter 1 is a NAT device. network_adapter(1, :nat) end # Customize the VM by calling `VBoxManage` with the given # arguments. # # When called multiple times, the customizations will be applied # in the order given. # # The special `:name` parameter in the command will be replaced with # the unique ID or name of the virtual machine. This is useful for # parameters to `modifyvm` and the like. # # @param [Array] command An array of arguments to pass to # VBoxManage. def customize(*command) event = command.first.is_a?(String) ? command.shift : "pre-boot" command = command[0] @customizations << [event, command] end # This defines a network adapter that will be added to the VirtualBox # virtual machine in the given slot. # # @param [Integer] slot The slot for this network adapter. # @param [Symbol] type The type of adapter. def network_adapter(slot, type, **opts) @network_adapters[slot] = [type, opts] end # Shortcut for setting memory size for the virtual machine. # Calls #customize internally. # # @param size [Integer, String] the memory size in MB def memory=(size) customize("pre-boot", ["modifyvm", :id, "--memory", size.to_s]) end # Shortcut for setting CPU count for the virtual machine. # Calls #customize internally. # # @param count [Integer, String] the count of CPUs def cpus=(count) customize("pre-boot", ["modifyvm", :id, "--cpus", count.to_i]) end def merge(other) super.tap do |result| c = customizations.dup c += other.customizations result.instance_variable_set(:@customizations, c) end end # This is the hook that is called to finalize the object before it # is put into use. def finalize! # Default is to auto the DNS proxy @auto_nat_dns_proxy = true if @auto_nat_dns_proxy == UNSET_VALUE if @check_guest_additions == UNSET_VALUE @check_guest_additions = true end if @destroy_unused_network_interfaces == UNSET_VALUE @destroy_unused_network_interfaces = false end if @functional_vboxsf == UNSET_VALUE @functional_vboxsf = true end # Default is to not show a GUI @gui = false if @gui == UNSET_VALUE # Do not create linked clone by default @linked_clone = false if @linked_clone == UNSET_VALUE @linked_clone_snapshot = nil if @linked_clone_snapshot == UNSET_VALUE # The default name is just nothing, and we default it @name = nil if @name == UNSET_VALUE end def validate(machine) errors = _detected_errors valid_events = ["pre-import", "pre-boot", "post-boot", "post-comm"] @customizations.each do |event, _| if !valid_events.include?(event) errors << I18n.t( "vagrant.virtualbox.config.invalid_event", event: event.to_s, valid_events: valid_events.join(", ")) end end @customizations.each do |event, command| if event == "pre-import" && command.index(:id) errors << I18n.t("vagrant.virtualbox.config.id_in_pre_import") end end # Verify that internal networks are only on private networks. machine.config.vm.networks.each do |type, data| if data[:virtualbox__intnet] && type != :private_network errors << I18n.t("vagrant.virtualbox.config.intnet_on_bad_type") break end end { "VirtualBox Provider" => errors } end def to_s "VirtualBox" end end end end vagrant-2.0.2/plugins/providers/virtualbox/driver/000077500000000000000000000000001323370221500223435ustar00rootroot00000000000000vagrant-2.0.2/plugins/providers/virtualbox/driver/base.rb000066400000000000000000000361061323370221500236100ustar00rootroot00000000000000require 'log4r' require 'vagrant/util/busy' require 'vagrant/util/platform' require 'vagrant/util/retryable' require 'vagrant/util/subprocess' require 'vagrant/util/which' module VagrantPlugins module ProviderVirtualBox module Driver # Base class for all VirtualBox drivers. # # This class provides useful tools for things such as executing # VBoxManage and handling SIGINTs and so on. class Base # Include this so we can use `Subprocess` more easily. include Vagrant::Util::Retryable def initialize @logger = Log4r::Logger.new("vagrant::provider::virtualbox::base") # This flag is used to keep track of interrupted state (SIGINT) @interrupted = false if Vagrant::Util::Platform.windows? || Vagrant::Util::Platform.cygwin? @logger.debug("Windows, checking for VBoxManage on PATH first") @vboxmanage_path = Vagrant::Util::Which.which("VBoxManage") # On Windows, we use the VBOX_INSTALL_PATH environmental # variable to find VBoxManage. if !@vboxmanage_path && (ENV.key?("VBOX_INSTALL_PATH") || ENV.key?("VBOX_MSI_INSTALL_PATH")) @logger.debug("Windows. Trying VBOX_INSTALL_PATH for VBoxManage") # Get the path. path = ENV["VBOX_INSTALL_PATH"] || ENV["VBOX_MSI_INSTALL_PATH"] @logger.debug("VBOX_INSTALL_PATH value: #{path}") # There can actually be multiple paths in here, so we need to # split by the separator ";" and see which is a good one. path.split(";").each do |single| # Make sure it ends with a \ single += "\\" if !single.end_with?("\\") # If the executable exists, then set it as the main path # and break out vboxmanage = "#{single}VBoxManage.exe" if File.file?(vboxmanage) @vboxmanage_path = Vagrant::Util::Platform.cygwin_windows_path(vboxmanage) break end end end # If we still don't have one, try to find it using common locations drive = ENV["SYSTEMDRIVE"] || "C:" [ "#{drive}/Program Files/Oracle/VirtualBox", "#{drive}/Program Files (x86)/Oracle/VirtualBox", "#{ENV["PROGRAMFILES"]}/Oracle/VirtualBox" ].each do |maybe| path = File.join(maybe, "VBoxManage.exe") if File.file?(path) @vboxmanage_path = path break end end elsif Vagrant::Util::Platform.wsl? if !Vagrant::Util::Platform.wsl_windows_access? @logger.error("No user Windows access defined for the Windows Subsystem for Linux. This is required for VirtualBox.") raise Vagrant::Errors::WSLVirtualBoxWindowsAccessError end @logger.debug("Linux platform detected but executing within WSL. Locating VBoxManage.") @vboxmanage_path = Vagrant::Util::Which.which("VBoxManage") || Vagrant::Util::Which.which("VBoxManage.exe") if !@vboxmanage_path raise Vagrant::Errors::VBoxManageNotFoundWSLError end end # Fall back to hoping for the PATH to work out @vboxmanage_path ||= "VBoxManage" @logger.info("VBoxManage path: #{@vboxmanage_path}") end # Clears the forwarded ports that have been set on the virtual machine. def clear_forwarded_ports end # Clears the shared folders that have been set on the virtual machine. def clear_shared_folders end # Creates a DHCP server for a host only network. # # @param [String] network Name of the host-only network. # @param [Hash] options Options for the DHCP server. def create_dhcp_server(network, options) end # Creates a host only network with the given options. # # @param [Hash] options Options to create the host only network. # @return [Hash] The details of the host only network, including # keys `:name`, `:ip`, and `:netmask` def create_host_only_network(options) end # Deletes the virtual machine references by this driver. def delete end # Deletes any host only networks that aren't being used for anything. def delete_unused_host_only_networks end # Discards any saved state associated with this VM. def discard_saved_state end # Enables network adapters on the VM. # # The format of each adapter specification should be like so: # # { # type: :hostonly, # hostonly: "vboxnet0", # mac_address: "tubes" # } # # This must support setting up both host only and bridged networks. # # @param [Array] adapters Array of adapters to enable. def enable_adapters(adapters) end # Execute a raw command straight through to VBoxManage. # # Accepts a retryable: true option if the command should be retried # upon failure. # # Raises a VBoxManage error if it fails. # # @param [Array] command Command to execute. def execute_command(command) end # Exports the virtual machine to the given path. # # @param [String] path Path to the OVF file. # @yield [progress] Yields the block with the progress of the export. def export(path) end # Forwards a set of ports for a VM. # # This will not affect any previously set forwarded ports, # so be sure to delete those if you need to. # # The format of each port hash should be the following: # # { # name: "foo", # hostport: 8500, # guestport: 80, # adapter: 1, # protocol: "tcp" # } # # Note that "adapter" and "protocol" are optional and will default # to 1 and "tcp" respectively. # # @param [Array] ports An array of ports to set. See documentation # for more information on the format. def forward_ports(ports) end # Halts the virtual machine (pulls the plug). def halt end # Imports the VM from an OVF file. # # @param [String] ovf Path to the OVF file. # @return [String] UUID of the imported VM. def import(ovf) end # Returns the maximum number of network adapters. def max_network_adapters 8 end # Returns a list of forwarded ports for a VM. # # @param [String] uuid UUID of the VM to read from, or `nil` if this # VM. # @param [Boolean] active_only If true, only VMs that are running will # be checked. # @return [Array] def read_forwarded_ports(uuid=nil, active_only=false) end # Returns a list of bridged interfaces. # # @return [Hash] def read_bridged_interfaces end # Returns a list of configured DHCP servers # # Each DHCP server is represented as a Hash with the following details: # # { # :network => String, # name of the associated network interface as # # parsed from the NetworkName, e.g. "vboxnet0" # :ip => String, # IP address of the DHCP server, e.g. "172.28.128.2" # :lower => String, # lower IP address of the DHCP lease range, e.g. "172.28.128.3" # :upper => String, # upper IP address of the DHCP lease range, e.g. "172.28.128.254" # } # # @return [Array] See comment above for details def read_dhcp_servers end # Returns the guest additions version that is installed on this VM. # # @return [String] def read_guest_additions_version end # Returns the value of a guest property on the current VM. # # @param [String] property the name of the guest property to read # @return [String] value of the guest property # @raise [VirtualBoxGuestPropertyNotFound] if the guest property does not have a value def read_guest_property(property) end # Returns a list of available host only interfaces. # # Each interface is represented as a Hash with the following details: # # { # :name => String, # interface name, e.g. "vboxnet0" # :ip => String, # IP address of the interface, e.g. "172.28.128.1" # :netmask => String, # netmask associated with the interface, e.g. "255.255.255.0" # :status => String, # status of the interface, e.g. "Up", "Down" # } # # @return [Array] See comment above for details def read_host_only_interfaces end # Returns the MAC address of the first network interface. # # @return [String] def read_mac_address end # Returns the folder where VirtualBox places it's VMs. # # @return [String] def read_machine_folder end # Returns a list of network interfaces of the VM. # # @return [Hash] def read_network_interfaces end # Returns the current state of this VM. # # @return [Symbol] def read_state end # Returns a list of all forwarded ports in use by active # virtual machines. # # @return [Array] def read_used_ports end # Returns a list of all UUIDs of virtual machines currently # known by VirtualBox. # # @return [Array] def read_vms end # Reconfigure the hostonly network given by interface (the result # of read_host_only_networks). This is a sad function that only # exists to work around VirtualBox bugs. # # @return nil def reconfig_host_only(interface) end # Removes the DHCP server identified by the provided network name. # # @param [String] network_name The the full network name associated # with the DHCP server to be removed, e.g. "HostInterfaceNetworking-vboxnet0" def remove_dhcp_server(network_name) end # Sets the MAC address of the first network adapter. # # @param [String] mac MAC address without any spaces/hyphens. def set_mac_address(mac) end # Share a set of folders on this VM. # # @param [Array] folders def share_folders(folders) end # Reads the SSH port of this VM. # # @param [Integer] expected Expected guest port of SSH. def ssh_port(expected) end # Starts the virtual machine. # # @param [String] mode Mode to boot the VM. Either "headless" # or "gui" def start(mode) end # Suspend the virtual machine. def suspend end # Unshare folders. def unshare_folders(names) end # Verifies that the driver is ready to accept work. # # This should raise a VagrantError if things are not ready. def verify! end # Verifies that an image can be imported properly. # # @param [String] path Path to an OVF file. # @return [Boolean] def verify_image(path) end # Checks if a VM with the given UUID exists. # # @return [Boolean] def vm_exists?(uuid) end # Execute the given subcommand for VBoxManage and return the output. def execute(*command, &block) # Get the options hash if it exists opts = {} opts = command.pop if command.last.is_a?(Hash) tries = 0 tries = 3 if opts[:retryable] # Variable to store our execution result r = nil retryable(on: Vagrant::Errors::VBoxManageError, tries: tries, sleep: 1) do # If there is an error with VBoxManage, this gets set to true errored = false # Execute the command r = raw(*command, &block) # If the command was a failure, then raise an exception that is # nicely handled by Vagrant. if r.exit_code != 0 if @interrupted @logger.info("Exit code != 0, but interrupted. Ignoring.") elsif r.exit_code == 126 # This exit code happens if VBoxManage is on the PATH, # but another executable it tries to execute is missing. # This is usually indicative of a corrupted VirtualBox install. raise Vagrant::Errors::VBoxManageNotFoundError else errored = true end else # Sometimes, VBoxManage fails but doesn't actual return a non-zero # exit code. For this we inspect the output and determine if an error # occurred. if r.stderr =~ /failed to open \/dev\/vboxnetctl/i # This catches an error message that only shows when kernel # drivers aren't properly installed. @logger.error("Error message about unable to open vboxnetctl") raise Vagrant::Errors::VirtualBoxKernelModuleNotLoaded end if r.stderr =~ /VBoxManage([.a-z]+?): error:/ # This catches the generic VBoxManage error case. @logger.info("VBoxManage error text found, assuming error.") errored = true end end # If there was an error running VBoxManage, show the error and the # output. if errored raise Vagrant::Errors::VBoxManageError, command: command.inspect, stderr: r.stderr, stdout: r.stdout end end # Return the output, making sure to replace any Windows-style # newlines with Unix-style. r.stdout.gsub("\r\n", "\n") end # Executes a command and returns the raw result object. def raw(*command, &block) int_callback = lambda do @interrupted = true # We have to execute this in a thread due to trap contexts # and locks. Thread.new { @logger.info("Interrupted.") }.join end # Append in the options for subprocess command << { notify: [:stdout, :stderr] } Vagrant::Util::Busy.busy(int_callback) do Vagrant::Util::Subprocess.execute(@vboxmanage_path, *command, &block) end rescue Vagrant::Util::Subprocess::LaunchError => e raise Vagrant::Errors::VBoxManageLaunchError, message: e.to_s end end end end end vagrant-2.0.2/plugins/providers/virtualbox/driver/meta.rb000066400000000000000000000137501323370221500236240ustar00rootroot00000000000000require "forwardable" require "thread" require "log4r" require "vagrant/util/retryable" require File.expand_path("../base", __FILE__) module VagrantPlugins module ProviderVirtualBox module Driver class Meta < Base # This is raised if the VM is not found when initializing a driver # with a UUID. class VMNotFound < StandardError; end # We use forwardable to do all our driver forwarding extend Forwardable # We cache the read VirtualBox version here once we have one, # since during the execution of Vagrant, it likely doesn't change. @@version = nil @@version_lock = Mutex.new # The UUID of the virtual machine we represent attr_reader :uuid # The version of virtualbox that is running. attr_reader :version include Vagrant::Util::Retryable def initialize(uuid=nil) # Setup the base super() @logger = Log4r::Logger.new("vagrant::provider::virtualbox::meta") @uuid = uuid @@version_lock.synchronize do if !@@version # Read and assign the version of VirtualBox we know which # specific driver to instantiate. begin @@version = read_version rescue Vagrant::Errors::CommandUnavailable, Vagrant::Errors::CommandUnavailableWindows # This means that VirtualBox was not found, so we raise this # error here. raise Vagrant::Errors::VirtualBoxNotDetected end end end # Instantiate the proper version driver for VirtualBox @logger.debug("Finding driver for VirtualBox version: #{@@version}") driver_map = { "4.0" => Version_4_0, "4.1" => Version_4_1, "4.2" => Version_4_2, "4.3" => Version_4_3, "5.0" => Version_5_0, "5.1" => Version_5_1, "5.2" => Version_5_2, } if @@version.start_with?("4.2.14") # VirtualBox 4.2.14 just doesn't work with Vagrant, so show error raise Vagrant::Errors::VirtualBoxBrokenVersion040214 end driver_klass = nil driver_map.each do |key, klass| if @@version.start_with?(key) driver_klass = klass break end end if !driver_klass supported_versions = driver_map.keys.sort.join(", ") raise Vagrant::Errors::VirtualBoxInvalidVersion, supported_versions: supported_versions end @logger.info("Using VirtualBox driver: #{driver_klass}") @driver = driver_klass.new(@uuid) @version = @@version if @uuid # Verify the VM exists, and if it doesn't, then don't worry # about it (mark the UUID as nil) raise VMNotFound if !@driver.vm_exists?(@uuid) end end def_delegators :@driver, :clear_forwarded_ports, :clear_shared_folders, :clonevm, :create_dhcp_server, :create_host_only_network, :create_snapshot, :delete, :delete_snapshot, :delete_unused_host_only_networks, :discard_saved_state, :enable_adapters, :execute_command, :export, :forward_ports, :halt, :import, :list_snapshots, :read_forwarded_ports, :read_bridged_interfaces, :read_dhcp_servers, :read_guest_additions_version, :read_guest_ip, :read_guest_property, :read_host_only_interfaces, :read_mac_address, :read_mac_addresses, :read_machine_folder, :read_network_interfaces, :read_state, :read_used_ports, :read_vms, :reconfig_host_only, :remove_dhcp_server, :restore_snapshot, :resume, :set_mac_address, :set_name, :share_folders, :ssh_port, :start, :suspend, :verify!, :verify_image, :vm_exists? protected # This returns the version of VirtualBox that is running. # # @return [String] def read_version # The version string is usually in one of the following formats: # # * 4.1.8r1234 # * 4.1.8r1234_OSE # * 4.1.8_MacPortsr1234 # # Below accounts for all of these. # Note: We split this into multiple lines because apparently "".split("_") # is [], so we have to check for an empty array in between. output = "" retryable(on: Vagrant::Errors::VirtualBoxVersionEmpty, tries: 3, sleep: 1) do output = execute("--version") if output =~ /vboxdrv kernel module is not loaded/ || output =~ /VirtualBox kernel modules are not loaded/i raise Vagrant::Errors::VirtualBoxKernelModuleNotLoaded elsif output =~ /Please install/ # Check for installation incomplete warnings, for example: # "WARNING: The character device /dev/vboxdrv does not # exist. Please install the virtualbox-ose-dkms package and # the appropriate headers, most likely linux-headers-generic." raise Vagrant::Errors::VirtualBoxInstallIncomplete elsif output.chomp == "" # This seems to happen on Windows for uncertain reasons. # Raise an error otherwise the error is that they have an # incompatible version of VirtualBox which isn't true. raise Vagrant::Errors::VirtualBoxVersionEmpty, vboxmanage: @vboxmanage_path.to_s end end parts = output.split("_") return nil if parts.empty? parts[0].split("r")[0] end end end end end vagrant-2.0.2/plugins/providers/virtualbox/driver/version_4_0.rb000066400000000000000000000441311323370221500250220ustar00rootroot00000000000000require 'log4r' require "vagrant/util/platform" require File.expand_path("../base", __FILE__) module VagrantPlugins module ProviderVirtualBox module Driver # Driver for VirtualBox 4.0.x class Version_4_0 < Base def initialize(uuid) super() @logger = Log4r::Logger.new("vagrant::provider::virtualbox_4_0") @uuid = uuid end def clear_forwarded_ports args = [] read_forwarded_ports(@uuid).each do |nic, name, _, _| args.concat(["--natpf#{nic}", "delete", name]) end execute("modifyvm", @uuid, *args) if !args.empty? end def clear_shared_folders info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| if name = line[/^SharedFolderNameMachineMapping\d+="(.+?)"$/, 1] execute("sharedfolder", "remove", @uuid, "--name", name) end end end def create_dhcp_server(network, options) execute("dhcpserver", "add", "--ifname", network, "--ip", options[:dhcp_ip], "--netmask", options[:netmask], "--lowerip", options[:dhcp_lower], "--upperip", options[:dhcp_upper], "--enable") end def create_host_only_network(options) # Create the interface interface = execute("hostonlyif", "create") name = interface[/^Interface '(.+?)' was successfully created$/, 1] # Get the IP so we can determine v4 vs v6 ip = IPAddr.new(options[:adapter_ip]) # Configure if ip.ipv4? execute("hostonlyif", "ipconfig", name, "--ip", options[:adapter_ip], "--netmask", options[:netmask]) elsif ip.ipv6? execute("hostonlyif", "ipconfig", name, "--ipv6", options[:adapter_ip], "--netmasklengthv6", options[:netmask].to_s) end # Return the details return { name: name, ip: options[:adapter_ip], netmask: options[:netmask], dhcp: nil } end def delete execute("unregistervm", @uuid, "--delete") end def delete_unused_host_only_networks networks = [] execute("list", "hostonlyifs").split("\n").each do |line| if network_name = line[/^Name:\s+(.+?)$/, 1] networks << network_name end end execute("list", "vms").split("\n").each do |line| if vm_name = line[/^".+?"\s+\{(.+?)\}$/, 1] begin info = execute("showvminfo", vm_name, "--machinereadable", retryable: true) info.split("\n").each do |line| if network_name = line[/^hostonlyadapter\d+="(.+?)"$/, 1] networks.delete(network_name) end end rescue Vagrant::Errors::VBoxManageError => e raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND") # VirtualBox could not find the vm. It may have been deleted # by another process after we called 'vboxmanage list vms'? Ignore this error. end end end networks.each do |name| # First try to remove any DHCP servers attached. We use `raw` because # it is okay if this fails. It usually means that a DHCP server was # never attached. raw("dhcpserver", "remove", "--ifname", name) # Delete the actual host only network interface. execute("hostonlyif", "remove", name) end end def discard_saved_state execute("discardstate", @uuid) end def enable_adapters(adapters) args = [] adapters.each do |adapter| args.concat(["--nic#{adapter[:adapter]}", adapter[:type].to_s]) if adapter[:bridge] args.concat(["--bridgeadapter#{adapter[:adapter]}", adapter[:bridge], "--cableconnected#{adapter[:adapter]}", "on"]) end if adapter[:hostonly] args.concat(["--hostonlyadapter#{adapter[:adapter]}", adapter[:hostonly]]) end if adapter[:mac_address] args.concat(["--macaddress#{adapter[:adapter]}", adapter[:mac_address]]) end if adapter[:nic_type] args.concat(["--nictype#{adapter[:adapter]}", adapter[:nic_type].to_s]) end end execute("modifyvm", @uuid, *args) end def execute_command(command) execute(*command) end def export(path) execute("export", @uuid, "--output", path.to_s) end def forward_ports(ports) args = [] ports.each do |options| pf_builder = [options[:name], options[:protocol] || "tcp", options[:hostip] || "", options[:hostport], options[:guestip] || "", options[:guestport]] args.concat(["--natpf#{options[:adapter] || 1}", pf_builder.join(",")]) end execute("modifyvm", @uuid, *args) if !args.empty? end def halt execute("controlvm", @uuid, "poweroff") end def import(ovf) ovf = Vagrant::Util::Platform.cygwin_windows_path(ovf) output = "" total = "" last = 0 execute("import", ovf) do |type, data| if type == :stdout # Keep track of the stdout so that we can get the VM name output << data elsif type == :stderr # Append the data so we can see the full view total << data # Break up the lines. We can't get the progress until we see an "OK" lines = total.split("\n") if lines.include?("OK.") # The progress of the import will be in the last line. Do a greedy # regular expression to find what we're looking for. if current = lines.last[/.+(\d{2})%/, 1] current = current.to_i if current > last last = current yield current if block_given? end end end end end # Find the name of the VM name name = output[/Suggested VM name "(.+?)"/, 1] if !name @logger.error("Couldn't find VM name in the output.") return nil end output = execute("list", "vms") if existing_vm = output[/^"#{Regexp.escape(name)}" \{(.+?)\}$/, 1] return existing_vm end nil end def read_forwarded_ports(uuid=nil, active_only=false) uuid ||= @uuid @logger.debug("read_forward_ports: uuid=#{uuid} active_only=#{active_only}") results = [] current_nic = nil info = execute("showvminfo", uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| # This is how we find the nic that a FP is attached to, # since this comes first. if nic = line[/^nic(\d+)=".+?"$/, 1] current_nic = nic.to_i end # If we care about active VMs only, then we check the state # to verify the VM is running. if active_only && (state = line[/^VMState="(.+?)"$/, 1] and state != "running") return [] end # Parse out the forwarded port information if matcher = /^Forwarding.+?="(.+?),.+?,.*?,(.+?),.*?,(.+?)"$/.match(line) result = [current_nic, matcher[1], matcher[2].to_i, matcher[3].to_i] @logger.debug(" - #{result.inspect}") results << result end end results end def read_bridged_interfaces execute("list", "bridgedifs").split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if name = line[/^Name:\s+(.+?)$/, 1] info[:name] = name elsif ip = line[/^IPAddress:\s+(.+?)$/, 1] info[:ip] = ip elsif netmask = line[/^NetworkMask:\s+(.+?)$/, 1] info[:netmask] = netmask elsif status = line[/^Status:\s+(.+?)$/, 1] info[:status] = status end end # Return the info to build up the results info end end def read_dhcp_servers execute("list", "dhcpservers", retryable: true).split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if network = line[/^NetworkName:\s+HostInterfaceNetworking-(.+?)$/, 1] info[:network] = network info[:network_name] = "HostInterfaceNetworking-#{network}" elsif ip = line[/^IP:\s+(.+?)$/, 1] info[:ip] = ip elsif netmask = line[/^NetworkMask:\s+(.+?)$/, 1] info[:netmask] = netmask elsif lower = line[/^lowerIPAddress:\s+(.+?)$/, 1] info[:lower] = lower elsif upper = line[/^upperIPAddress:\s+(.+?)$/, 1] info[:upper] = upper end end info end end def read_guest_additions_version output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version", retryable: true) if value = output[/^Value: (.+?)$/, 1] # Split the version by _ since some distro versions modify it # to look like this: 4.1.2_ubuntu, and the distro part isn't # too important. return value.split("_").first end return nil end def read_guest_ip(adapter_number) ip = read_guest_property("/VirtualBox/GuestInfo/Net/#{adapter_number}/V4/IP") if !valid_ip_address?(ip) raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, guest_property: "/VirtualBox/GuestInfo/Net/#{adapter_number}/V4/IP" end return ip end def read_guest_property(property) output = execute("guestproperty", "get", @uuid, property) if output =~ /^Value: (.+?)$/ $1.to_s else raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, guest_property: property end end def read_host_only_interfaces execute("list", "hostonlyifs", retryable: true).split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if name = line[/^Name:\s+(.+?)$/, 1] info[:name] = name elsif ip = line[/^IPAddress:\s+(.+?)$/, 1] info[:ip] = ip elsif netmask = line[/^NetworkMask:\s+(.+?)$/, 1] info[:netmask] = netmask elsif line =~ /^IPV6Address:\s+(.+?)$/ info[:ipv6] = $1.to_s.strip elsif line =~ /^IPV6NetworkMaskPrefixLength:\s+(.+?)$/ info[:ipv6_prefix] = $1.to_s.strip elsif status = line[/^Status:\s+(.+?)$/, 1] info[:status] = status end end info end end def read_mac_address info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| if mac = line[/^macaddress1="(.+?)"$/, 1] return mac end end nil end def read_mac_addresses macs = {} info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| if matcher = /^macaddress(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i mac = matcher[2].to_s macs[adapter] = mac end end macs end def read_machine_folder execute("list", "systemproperties", retryable: true).split("\n").each do |line| if folder = line[/^Default machine folder:\s+(.+?)$/i, 1] return folder end end nil end def read_network_interfaces nics = {} info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| if matcher = /^nic(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i type = matcher[2].to_sym nics[adapter] ||= {} nics[adapter][:type] = type elsif matcher = /^hostonlyadapter(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i network = matcher[2].to_s nics[adapter] ||= {} nics[adapter][:hostonly] = network elsif matcher = /^bridgeadapter(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i network = matcher[2].to_s nics[adapter] ||= {} nics[adapter][:bridge] = network end end nics end def read_state output = execute("showvminfo", @uuid, "--machinereadable", retryable: true) if output =~ /^name=""$/ return :inaccessible elsif state = output[/^VMState="(.+?)"$/, 1] return state.to_sym end nil end def read_used_ports ports = [] execute("list", "vms", retryable: true).split("\n").each do |line| if uuid = line[/^".+?" \{(.+?)\}$/, 1] # Ignore our own used ports next if uuid == @uuid begin read_forwarded_ports(uuid, true).each do |_, _, hostport, _| ports << hostport end rescue Vagrant::Errors::VBoxManageError => e raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND") # VirtualBox could not find the vm. It may have been deleted # by another process after we called 'vboxmanage list vms'? Ignore this error. end end end ports end def read_vms results = {} execute("list", "vms", retryable: true).split("\n").each do |line| if line =~ /^"(.+?)" \{(.+?)\}$/ results[$1.to_s] = $2.to_s end end results end def reconfig_host_only(interface) execute("hostonlyif", "ipconfig", interface[:name], "--ipv6", interface[:ipv6]) end def remove_dhcp_server(network_name) execute("dhcpserver", "remove", "--netname", network_name) end def set_mac_address(mac) execute("modifyvm", @uuid, "--macaddress1", mac) end def set_name(name) execute("modifyvm", @uuid, "--name", name) end def share_folders(folders) folders.each do |folder| args = ["--name", folder[:name], "--hostpath", folder[:hostpath]] args << "--transient" if folder.key?(:transient) && folder[:transient] execute("sharedfolder", "add", @uuid, *args) end end def ssh_port(expected_port) @logger.debug("Searching for SSH port: #{expected_port.inspect}") # Look for the forwarded port only by comparing the guest port read_forwarded_ports.each do |_, _, hostport, guestport| return hostport if guestport == expected_port end nil end def resume @logger.debug("Resuming paused VM...") execute("controlvm", @uuid, "resume") end def start(mode) command = ["startvm", @uuid, "--type", mode.to_s] r = raw(*command) if r.exit_code == 0 || r.stdout =~ /VM ".+?" has been successfully started/ # Some systems return an exit code 1 for some reason. For that # we depend on the output. return true end # If we reached this point then it didn't work out. raise Vagrant::Errors::VBoxManageError, command: command.inspect, stderr: r.stderr end def suspend execute("controlvm", @uuid, "savestate") end def unshare_folders(names) names.each do |name| begin execute( "sharedfolder", "remove", @uuid, "--name", name, "--transient") execute( "setextradata", @uuid, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/#{name}") rescue Vagrant::Errors::VBoxManageError => e if e.extra_data[:stderr].include?("VBOX_E_FILE_ERROR") # The folder doesn't exist. ignore. else raise end end end end def valid_ip_address?(ip) # Filter out invalid IP addresses # GH-4658 VirtualBox can report an IP address of 0.0.0.0 for FreeBSD guests. if ip == "0.0.0.0" return false else return true end end def verify! # This command sometimes fails if kernel drivers aren't properly loaded # so we just run the command and verify that it succeeded. execute("list", "hostonlyifs") end def verify_image(path) r = raw("import", path.to_s, "--dry-run") return r.exit_code == 0 end def vm_exists?(uuid) raw("showvminfo", uuid).exit_code == 0 end end end end end vagrant-2.0.2/plugins/providers/virtualbox/driver/version_4_1.rb000066400000000000000000000545751323370221500250400ustar00rootroot00000000000000require 'log4r' require "vagrant/util/platform" require File.expand_path("../base", __FILE__) module VagrantPlugins module ProviderVirtualBox module Driver # Driver for VirtualBox 4.1.x class Version_4_1 < Base def initialize(uuid) super() @logger = Log4r::Logger.new("vagrant::provider::virtualbox_4_1") @uuid = uuid end def clear_forwarded_ports args = [] read_forwarded_ports(@uuid).each do |nic, name, _, _| args.concat(["--natpf#{nic}", "delete", name]) end execute("modifyvm", @uuid, *args) if !args.empty? end def clear_shared_folders info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| if folder = line[/^SharedFolderNameMachineMapping\d+="(.+?)"$/, 1] execute("sharedfolder", "remove", @uuid, "--name", folder) end end end def clonevm(master_id, snapshot_name) machine_name = "temp_clone_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}" args = ["--register", "--name", machine_name] if snapshot_name args += ["--snapshot", snapshot_name, "--options", "link"] end execute("clonevm", master_id, *args) return get_machine_id(machine_name) end def create_dhcp_server(network, options) execute("dhcpserver", "add", "--ifname", network, "--ip", options[:dhcp_ip], "--netmask", options[:netmask], "--lowerip", options[:dhcp_lower], "--upperip", options[:dhcp_upper], "--enable") end def create_host_only_network(options) # Create the interface interface = execute("hostonlyif", "create") name = interface[/^Interface '(.+?)' was successfully created$/, 1] # Get the IP so we can determine v4 vs v6 ip = IPAddr.new(options[:adapter_ip]) # Configure if ip.ipv4? execute("hostonlyif", "ipconfig", name, "--ip", options[:adapter_ip], "--netmask", options[:netmask]) elsif ip.ipv6? execute("hostonlyif", "ipconfig", name, "--ipv6", options[:adapter_ip], "--netmasklengthv6", options[:netmask].to_s) end # Return the details return { name: name, ip: options[:adapter_ip], netmask: options[:netmask], dhcp: nil } end def create_snapshot(machine_id, snapshot_name) execute("snapshot", machine_id, "take", snapshot_name) end def delete_snapshot(machine_id, snapshot_name) # Start with 0% last = 0 total = "" yield 0 if block_given? # Snapshot and report the % progress execute("snapshot", machine_id, "delete", snapshot_name) do |type, data| if type == :stderr # Append the data so we can see the full view total << data.gsub("\r", "") # Break up the lines. We can't get the progress until we see an "OK" lines = total.split("\n") # The progress of the import will be in the last line. Do a greedy # regular expression to find what we're looking for. match = /.+(\d{2})%/.match(lines.last) if match current = match[1].to_i if current > last last = current yield current if block_given? end end end end end def list_snapshots(machine_id) output = execute( "snapshot", machine_id, "list", "--machinereadable", retryable: true) result = [] output.split("\n").each do |line| if line =~ /^SnapshotName.*?="(.+?)"$/i result << $1.to_s end end result.sort rescue Vagrant::Errors::VBoxManageError => e d = e.extra_data return [] if d[:stderr].include?("does not have") || d[:stdout].include?("does not have") raise end def restore_snapshot(machine_id, snapshot_name) # Start with 0% last = 0 total = "" yield 0 if block_given? execute("snapshot", machine_id, "restore", snapshot_name) do |type, data| if type == :stderr # Append the data so we can see the full view total << data.gsub("\r", "") # Break up the lines. We can't get the progress until we see an "OK" lines = total.split("\n") # The progress of the import will be in the last line. Do a greedy # regular expression to find what we're looking for. match = /.+(\d{2})%/.match(lines.last) if match current = match[1].to_i if current > last last = current yield current if block_given? end end end end end def delete execute("unregistervm", @uuid, "--delete") end def delete_unused_host_only_networks networks = [] execute("list", "hostonlyifs").split("\n").each do |line| if network = line[/^Name:\s+(.+?)$/, 1] networks << network end end execute("list", "vms").split("\n").each do |line| if vm = line[/^".+?"\s+\{(.+?)\}$/, 1] begin info = execute("showvminfo", vm, "--machinereadable", retryable: true) info.split("\n").each do |line| if adapter = line[/^hostonlyadapter\d+="(.+?)"$/, 1] networks.delete(adapter) end end rescue Vagrant::Errors::VBoxManageError => e raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND") # VirtualBox could not find the vm. It may have been deleted # by another process after we called 'vboxmanage list vms'? Ignore this error. end end end networks.each do |name| # First try to remove any DHCP servers attached. We use `raw` because # it is okay if this fails. It usually means that a DHCP server was # never attached. raw("dhcpserver", "remove", "--ifname", name) # Delete the actual host only network interface. execute("hostonlyif", "remove", name) end end def discard_saved_state execute("discardstate", @uuid) end def enable_adapters(adapters) args = [] adapters.each do |adapter| args.concat(["--nic#{adapter[:adapter]}", adapter[:type].to_s]) if adapter[:bridge] args.concat(["--bridgeadapter#{adapter[:adapter]}", adapter[:bridge], "--cableconnected#{adapter[:adapter]}", "on"]) end if adapter[:hostonly] args.concat(["--hostonlyadapter#{adapter[:adapter]}", adapter[:hostonly]]) end if adapter[:intnet] args.concat(["--intnet#{adapter[:adapter]}", adapter[:intnet]]) end if adapter[:mac_address] args.concat(["--macaddress#{adapter[:adapter]}", adapter[:mac_address]]) end if adapter[:nic_type] args.concat(["--nictype#{adapter[:adapter]}", adapter[:nic_type].to_s]) end end execute("modifyvm", @uuid, *args) end def execute_command(command) execute(*command) end def export(path) execute("export", @uuid, "--output", path.to_s) end def forward_ports(ports) args = [] ports.each do |options| pf_builder = [options[:name], options[:protocol] || "tcp", options[:hostip] || "", options[:hostport], options[:guestip] || "", options[:guestport]] args.concat(["--natpf#{options[:adapter] || 1}", pf_builder.join(",")]) end execute("modifyvm", @uuid, *args) if !args.empty? end def get_machine_id(machine_name) output = execute("list", "vms", retryable: true) match = /^"#{Regexp.escape(machine_name)}" \{(.+?)\}$/.match(output) return match[1].to_s if match nil end def halt execute("controlvm", @uuid, "poweroff") end def import(ovf) ovf = Vagrant::Util::Platform.cygwin_windows_path(ovf) output = "" total = "" last = 0 execute("import", ovf) do |type, data| if type == :stdout # Keep track of the stdout so that we can get the VM name output << data elsif type == :stderr # Append the data so we can see the full view total << data # Break up the lines. We can't get the progress until we see an "OK" lines = total.split("\n") if lines.include?("OK.") # The progress of the import will be in the last line. Do a greedy # regular expression to find what we're looking for. if current = lines.last[/.+(\d{2})%/, 1] current = current.to_i if current > last last = current yield current if block_given? end end end end end # Find the name of the VM name name = output[/Suggested VM name "(.+?)"/, 1] if !name @logger.error("Couldn't find VM name in the output.") return nil end output = execute("list", "vms") if existing_vm = output[/^"#{Regexp.escape(name)}" \{(.+?)\}$/, 1] return existing_vm end nil end def read_forwarded_ports(uuid=nil, active_only=false) uuid ||= @uuid @logger.debug("read_forward_ports: uuid=#{uuid} active_only=#{active_only}") results = [] current_nic = nil info = execute("showvminfo", uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| # This is how we find the nic that a FP is attached to, # since this comes first. if nic = line[/^nic(\d+)=".+?"$/, 1] current_nic = nic.to_i end # If we care about active VMs only, then we check the state # to verify the VM is running. if active_only && (state = line[/^VMState="(.+?)"$/, 1] and state != "running") return [] end # Parse out the forwarded port information if matcher = /^Forwarding.+?="(.+?),.+?,.*?,(.+?),.*?,(.+?)"$/.match(line) result = [current_nic, matcher[1], matcher[2].to_i, matcher[3].to_i] @logger.debug(" - #{result.inspect}") results << result end end results end def read_bridged_interfaces execute("list", "bridgedifs").split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if name = line[/^Name:\s+(.+?)$/, 1] info[:name] = name elsif ip = line[/^IPAddress:\s+(.+?)$/, 1] info[:ip] = ip elsif netmask = line[/^NetworkMask:\s+(.+?)$/, 1] info[:netmask] = netmask elsif status = line[/^Status:\s+(.+?)$/, 1] info[:status] = status end end # Return the info to build up the results info end end def read_dhcp_servers execute("list", "dhcpservers", retryable: true).split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if network = line[/^NetworkName:\s+HostInterfaceNetworking-(.+?)$/, 1] info[:network] = network info[:network_name] = "HostInterfaceNetworking-#{network}" elsif ip = line[/^IP:\s+(.+?)$/, 1] info[:ip] = ip elsif netmask = line[/^NetworkMask:\s+(.+?)$/, 1] info[:netmask] = netmask elsif lower = line[/^lowerIPAddress:\s+(.+?)$/, 1] info[:lower] = lower elsif upper = line[/^upperIPAddress:\s+(.+?)$/, 1] info[:upper] = upper end end info end end def read_guest_additions_version output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version", retryable: true) if value = output[/^Value: (.+?)$/, 1] # Split the version by _ since some distro versions modify it # to look like this: 4.1.2_ubuntu, and the distro part isn't # too important. return value.split("_").first end return nil end def read_guest_ip(adapter_number) ip = read_guest_property("/VirtualBox/GuestInfo/Net/#{adapter_number}/V4/IP") if !valid_ip_address?(ip) raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, guest_property: "/VirtualBox/GuestInfo/Net/#{adapter_number}/V4/IP" end return ip end def read_guest_property(property) output = execute("guestproperty", "get", @uuid, property) if output =~ /^Value: (.+?)$/ $1.to_s else raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, guest_property: property end end def read_host_only_interfaces execute("list", "hostonlyifs", retryable: true).split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if name = line[/^Name:\s+(.+?)$/, 1] info[:name] = name elsif ip = line[/^IPAddress:\s+(.+?)$/, 1] info[:ip] = ip elsif netmask = line[/^NetworkMask:\s+(.+?)$/, 1] info[:netmask] = netmask elsif line =~ /^IPV6Address:\s+(.+?)$/ info[:ipv6] = $1.to_s.strip elsif line =~ /^IPV6NetworkMaskPrefixLength:\s+(.+?)$/ info[:ipv6_prefix] = $1.to_s.strip elsif status = line[/^Status:\s+(.+?)$/, 1] info[:status] = status end end info end end def read_mac_address info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| if mac = line[/^macaddress1="(.+?)"$/, 1] return mac end end nil end def read_mac_addresses macs = {} info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| if matcher = /^macaddress(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i mac = matcher[2].to_s macs[adapter] = mac end end macs end def read_machine_folder execute("list", "systemproperties", retryable: true).split("\n").each do |line| if folder = line[/^Default machine folder:\s+(.+?)$/i, 1] return folder end end nil end def read_network_interfaces nics = {} info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| if matcher = /^nic(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i type = matcher[2].to_sym nics[adapter] ||= {} nics[adapter][:type] = type elsif matcher = /^hostonlyadapter(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i network = matcher[2].to_s nics[adapter] ||= {} nics[adapter][:hostonly] = network elsif matcher = /^bridgeadapter(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i network = matcher[2].to_s nics[adapter] ||= {} nics[adapter][:bridge] = network end end nics end def read_state output = execute("showvminfo", @uuid, "--machinereadable", retryable: true) if output =~ /^name=""$/ return :inaccessible elsif state = output[/^VMState="(.+?)"$/, 1] return state.to_sym end nil end def read_used_ports ports = [] execute("list", "vms", retryable: true).split("\n").each do |line| if uuid = line[/^".+?" \{(.+?)\}$/, 1] # Ignore our own used ports next if uuid == @uuid begin read_forwarded_ports(uuid, true).each do |_, _, hostport, _| ports << hostport end rescue Vagrant::Errors::VBoxManageError => e raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND") # VirtualBox could not find the vm. It may have been deleted # by another process after we called 'vboxmanage list vms'? Ignore this error. end end end ports end def read_vms results = {} execute("list", "vms", retryable: true).split("\n").each do |line| if line =~ /^"(.+?)" \{(.+?)\}$/ results[$1.to_s] = $2.to_s end end results end def reconfig_host_only(interface) execute("hostonlyif", "ipconfig", interface[:name], "--ipv6", interface[:ipv6]) end def remove_dhcp_server(network_name) execute("dhcpserver", "remove", "--netname", network_name) end def set_mac_address(mac) execute("modifyvm", @uuid, "--macaddress1", mac) end def set_name(name) execute("modifyvm", @uuid, "--name", name) end def share_folders(folders) folders.each do |folder| args = ["--name", folder[:name], "--hostpath", folder[:hostpath]] args << "--transient" if folder.key?(:transient) && folder[:transient] if folder[:SharedFoldersEnableSymlinksCreate] # Enable symlinks on the shared folder execute("setextradata", @uuid, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/#{folder[:name]}", "1") end # Add the shared folder execute("sharedfolder", "add", @uuid, *args) end end def ssh_port(expected_port) @logger.debug("Searching for SSH port: #{expected_port.inspect}") # Look for the forwarded port only by comparing the guest port read_forwarded_ports.each do |_, _, hostport, guestport| return hostport if guestport == expected_port end nil end def resume @logger.debug("Resuming paused VM...") execute("controlvm", @uuid, "resume") end def start(mode) command = ["startvm", @uuid, "--type", mode.to_s] r = raw(*command) if r.exit_code == 0 || r.stdout =~ /VM ".+?" has been successfully started/ # Some systems return an exit code 1 for some reason. For that # we depend on the output. return true end # If we reached this point then it didn't work out. raise Vagrant::Errors::VBoxManageError, command: command.inspect, stderr: r.stderr end def suspend execute("controlvm", @uuid, "savestate") end def unshare_folders(names) names.each do |name| begin execute( "sharedfolder", "remove", @uuid, "--name", name, "--transient") execute( "setextradata", @uuid, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/#{name}") rescue Vagrant::Errors::VBoxManageError => e if e.extra_data[:stderr].include?("VBOX_E_FILE_ERROR") # The folder doesn't exist. ignore. else raise end end end end def valid_ip_address?(ip) # Filter out invalid IP addresses # GH-4658 VirtualBox can report an IP address of 0.0.0.0 for FreeBSD guests. if ip == "0.0.0.0" return false else return true end end def verify! # This command sometimes fails if kernel drivers aren't properly loaded # so we just run the command and verify that it succeeded. execute("list", "hostonlyifs") end def verify_image(path) r = raw("import", path.to_s, "--dry-run") return r.exit_code == 0 end def vm_exists?(uuid) 5.times do |i| result = raw("showvminfo", uuid) return true if result.exit_code == 0 # If vboxmanage returned VBOX_E_OBJECT_NOT_FOUND, # then the vm truly does not exist. Any other error might be transient return false if result.stderr.include?("VBOX_E_OBJECT_NOT_FOUND") # Sleep a bit though to give VirtualBox time to fix itself sleep 2 end # If we reach this point, it means that we consistently got the # failure, do a standard vboxmanage now. This will raise an # exception if it fails again. execute("showvminfo", uuid) return true end end end end end vagrant-2.0.2/plugins/providers/virtualbox/driver/version_4_2.rb000066400000000000000000000566411323370221500250350ustar00rootroot00000000000000require 'log4r' require "vagrant/util/platform" require File.expand_path("../base", __FILE__) module VagrantPlugins module ProviderVirtualBox module Driver # Driver for VirtualBox 4.2.x class Version_4_2 < Base def initialize(uuid) super() @logger = Log4r::Logger.new("vagrant::provider::virtualbox_4_2") @uuid = uuid end def clear_forwarded_ports args = [] read_forwarded_ports(@uuid).each do |nic, name, _, _| args.concat(["--natpf#{nic}", "delete", name]) end execute("modifyvm", @uuid, *args) if !args.empty? end def clear_shared_folders info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| if line =~ /^SharedFolderNameMachineMapping\d+="(.+?)"$/ execute("sharedfolder", "remove", @uuid, "--name", $1.to_s) end end end def create_dhcp_server(network, options) execute("dhcpserver", "add", "--ifname", network, "--ip", options[:dhcp_ip], "--netmask", options[:netmask], "--lowerip", options[:dhcp_lower], "--upperip", options[:dhcp_upper], "--enable") end def create_host_only_network(options) # Create the interface execute("hostonlyif", "create") =~ /^Interface '(.+?)' was successfully created$/ name = $1.to_s # Get the IP so we can determine v4 vs v6 ip = IPAddr.new(options[:adapter_ip]) # Configure if ip.ipv4? execute("hostonlyif", "ipconfig", name, "--ip", options[:adapter_ip], "--netmask", options[:netmask]) elsif ip.ipv6? execute("hostonlyif", "ipconfig", name, "--ipv6", options[:adapter_ip], "--netmasklengthv6", options[:netmask].to_s) end # Return the details return { name: name, ip: options[:adapter_ip], netmask: options[:netmask], dhcp: nil } end def delete execute("unregistervm", @uuid, "--delete") end def delete_unused_host_only_networks networks = [] execute("list", "hostonlyifs").split("\n").each do |line| networks << $1.to_s if line =~ /^Name:\s+(.+?)$/ end execute("list", "vms").split("\n").each do |line| if line =~ /^".+?"\s+\{(.+?)\}$/ begin info = execute("showvminfo", $1.to_s, "--machinereadable", retryable: true) info.split("\n").each do |inner_line| if inner_line =~ /^hostonlyadapter\d+="(.+?)"$/ networks.delete($1.to_s) end end rescue Vagrant::Errors::VBoxManageError => e raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND") # VirtualBox could not find the vm. It may have been deleted # by another process after we called 'vboxmanage list vms'? Ignore this error. end end end networks.each do |name| # First try to remove any DHCP servers attached. We use `raw` because # it is okay if this fails. It usually means that a DHCP server was # never attached. raw("dhcpserver", "remove", "--ifname", name) # Delete the actual host only network interface. execute("hostonlyif", "remove", name) end end def discard_saved_state execute("discardstate", @uuid) end def enable_adapters(adapters) args = [] adapters.each do |adapter| args.concat(["--nic#{adapter[:adapter]}", adapter[:type].to_s]) if adapter[:bridge] args.concat(["--bridgeadapter#{adapter[:adapter]}", adapter[:bridge], "--cableconnected#{adapter[:adapter]}", "on"]) end if adapter[:hostonly] args.concat(["--hostonlyadapter#{adapter[:adapter]}", adapter[:hostonly], "--cableconnected#{adapter[:adapter]}", "on"]) end if adapter[:intnet] args.concat(["--intnet#{adapter[:adapter]}", adapter[:intnet], "--cableconnected#{adapter[:adapter]}", "on"]) end if adapter[:mac_address] args.concat(["--macaddress#{adapter[:adapter]}", adapter[:mac_address]]) end if adapter[:nic_type] args.concat(["--nictype#{adapter[:adapter]}", adapter[:nic_type].to_s]) end end execute("modifyvm", @uuid, *args) end def execute_command(command) execute(*command) end def export(path) execute("export", @uuid, "--output", path.to_s) end def forward_ports(ports) args = [] ports.each do |options| pf_builder = [options[:name], options[:protocol] || "tcp", options[:hostip] || "", options[:hostport], options[:guestip] || "", options[:guestport]] args.concat(["--natpf#{options[:adapter] || 1}", pf_builder.join(",")]) end execute("modifyvm", @uuid, *args) if !args.empty? end def halt execute("controlvm", @uuid, "poweroff") end def import(ovf) ovf = Vagrant::Util::Platform.cygwin_windows_path(ovf) output = "" total = "" last = 0 output = execute("import", "-n", ovf) output =~ /Suggested VM name "(.+?)"/ suggested_name = $1.to_s specified_name = "#{suggested_name}_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}" #Millisecond + Random #Build the specified name param list name_params = Array.new name_params << "--vsys" << "0" << "--vmname" << specified_name #Extract the disks list and build the disk target params disk_params = Array.new disks = output.scan(/(\d+): Hard disk image: source image=.+, target path=(.+),/) disks.each do |unit_num, path| disk_params << "--vsys" disk_params << "0" #Derive vsys num .. do we support OVF's with multiple machines? 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 end execute("import", ovf , *name_params, *disk_params) do |type, data| if type == :stdout # Keep track of the stdout so that we can get the VM name output << data elsif type == :stderr # Append the data so we can see the full view total << data # Break up the lines. We can't get the progress until we see an "OK" lines = total.split("\n") if lines.include?("OK.") # The progress of the import will be in the last line. Do a greedy # regular expression to find what we're looking for. if lines.last =~ /.+(\d{2})%/ current = $1.to_i if current > last last = current yield current if block_given? end end end end end output = execute("list", "vms") if output =~ /^"#{Regexp.escape(specified_name)}" \{(.+?)\}$/ return $1.to_s end nil end def max_network_adapters 8 end def read_forwarded_ports(uuid=nil, active_only=false) uuid ||= @uuid @logger.debug("read_forward_ports: uuid=#{uuid} active_only=#{active_only}") results = [] current_nic = nil info = execute("showvminfo", uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| # This is how we find the nic that a FP is attached to, # since this comes first. current_nic = $1.to_i if line =~ /^nic(\d+)=".+?"$/ # If we care about active VMs only, then we check the state # to verify the VM is running. if active_only && line =~ /^VMState="(.+?)"$/ && $1.to_s != "running" return [] end # Parse out the forwarded port information if line =~ /^Forwarding.+?="(.+?),.+?,.*?,(.+?),.*?,(.+?)"$/ result = [current_nic, $1.to_s, $2.to_i, $3.to_i] @logger.debug(" - #{result.inspect}") results << result end end results end def read_bridged_interfaces execute("list", "bridgedifs").split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if line =~ /^Name:\s+(.+?)$/ info[:name] = $1.to_s elsif line =~ /^IPAddress:\s+(.+?)$/ info[:ip] = $1.to_s elsif line =~ /^NetworkMask:\s+(.+?)$/ info[:netmask] = $1.to_s elsif line =~ /^Status:\s+(.+?)$/ info[:status] = $1.to_s end end # Return the info to build up the results info end end def read_dhcp_servers execute("list", "dhcpservers", retryable: true).split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if network = line[/^NetworkName:\s+HostInterfaceNetworking-(.+?)$/, 1] info[:network] = network info[:network_name] = "HostInterfaceNetworking-#{network}" elsif ip = line[/^IP:\s+(.+?)$/, 1] info[:ip] = ip elsif netmask = line[/^NetworkMask:\s+(.+?)$/, 1] info[:netmask] = netmask elsif lower = line[/^lowerIPAddress:\s+(.+?)$/, 1] info[:lower] = lower elsif upper = line[/^upperIPAddress:\s+(.+?)$/, 1] info[:upper] = upper end end info end end def read_guest_additions_version output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version", retryable: true) if output =~ /^Value: (.+?)$/ # Split the version by _ since some distro versions modify it # to look like this: 4.1.2_ubuntu, and the distro part isn't # too important. value = $1.to_s return value.split("_").first end # If we can't get the guest additions version by guest property, try # to get it from the VM info itself. info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| return $1.to_s if line =~ /^GuestAdditionsVersion="(.+?)"$/ end return nil end def read_guest_ip(adapter_number) ip = read_guest_property("/VirtualBox/GuestInfo/Net/#{adapter_number}/V4/IP") if !valid_ip_address?(ip) raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, guest_property: "/VirtualBox/GuestInfo/Net/#{adapter_number}/V4/IP" end return ip end def read_guest_property(property) output = execute("guestproperty", "get", @uuid, property) if output =~ /^Value: (.+?)$/ $1.to_s else raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, guest_property: property end end def read_host_only_interfaces execute("list", "hostonlyifs", retryable: true).split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if line =~ /^Name:\s+(.+?)$/ info[:name] = $1.to_s elsif line =~ /^IPAddress:\s+(.+?)$/ info[:ip] = $1.to_s elsif line =~ /^NetworkMask:\s+(.+?)$/ info[:netmask] = $1.to_s elsif line =~ /^IPV6Address:\s+(.+?)$/ info[:ipv6] = $1.to_s.strip elsif line =~ /^IPV6NetworkMaskPrefixLength:\s+(.+?)$/ info[:ipv6_prefix] = $1.to_s.strip elsif line =~ /^Status:\s+(.+?)$/ info[:status] = $1.to_s end end info end end def read_mac_address info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| return $1.to_s if line =~ /^macaddress1="(.+?)"$/ end nil end def read_mac_addresses macs = {} info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| if matcher = /^macaddress(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i mac = matcher[2].to_s macs[adapter] = mac end end macs end def read_machine_folder execute("list", "systemproperties", retryable: true).split("\n").each do |line| if line =~ /^Default machine folder:\s+(.+?)$/i return $1.to_s end end nil end def read_network_interfaces nics = {} info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| if line =~ /^nic(\d+)="(.+?)"$/ adapter = $1.to_i type = $2.to_sym nics[adapter] ||= {} nics[adapter][:type] = type elsif line =~ /^hostonlyadapter(\d+)="(.+?)"$/ adapter = $1.to_i network = $2.to_s nics[adapter] ||= {} nics[adapter][:hostonly] = network elsif line =~ /^bridgeadapter(\d+)="(.+?)"$/ adapter = $1.to_i network = $2.to_s nics[adapter] ||= {} nics[adapter][:bridge] = network end end nics end def read_state output = execute("showvminfo", @uuid, "--machinereadable", retryable: true) if output =~ /^name=""$/ return :inaccessible elsif output =~ /^VMState="(.+?)"$/ return $1.to_sym end nil end def read_used_ports ports = [] execute("list", "vms", retryable: true).split("\n").each do |line| if line =~ /^".+?" \{(.+?)\}$/ uuid = $1.to_s # Ignore our own used ports next if uuid == @uuid begin read_forwarded_ports(uuid, true).each do |_, _, hostport, _| ports << hostport end rescue Vagrant::Errors::VBoxManageError => e raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND") # VirtualBox could not find the vm. It may have been deleted # by another process after we called 'vboxmanage list vms'? Ignore this error. end end end ports end def read_vms results = {} execute("list", "vms", retryable: true).split("\n").each do |line| if line =~ /^"(.+?)" \{(.+?)\}$/ results[$1.to_s] = $2.to_s end end results end def reconfig_host_only(interface) execute("hostonlyif", "ipconfig", interface[:name], "--ipv6", interface[:ipv6]) end def remove_dhcp_server(network_name) execute("dhcpserver", "remove", "--netname", network_name) end def set_mac_address(mac) execute("modifyvm", @uuid, "--macaddress1", mac) end def set_name(name) execute("modifyvm", @uuid, "--name", name, retryable: true) end def share_folders(folders) folders.each do |folder| args = ["--name", folder[:name], "--hostpath", folder[:hostpath]] args << "--transient" if folder.key?(:transient) && folder[:transient] if folder[:SharedFoldersEnableSymlinksCreate] # Enable symlinks on the shared folder execute("setextradata", @uuid, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/#{folder[:name]}", "1") end # Add the shared folder execute("sharedfolder", "add", @uuid, *args) end end def ssh_port(expected_port) @logger.debug("Searching for SSH port: #{expected_port.inspect}") # Look for the forwarded port only by comparing the guest port read_forwarded_ports.each do |_, _, hostport, guestport| return hostport if guestport == expected_port end nil end def resume @logger.debug("Resuming paused VM...") execute("controlvm", @uuid, "resume") end def start(mode) command = ["startvm", @uuid, "--type", mode.to_s] r = raw(*command) if r.exit_code == 0 || r.stdout =~ /VM ".+?" has been successfully started/ # Some systems return an exit code 1 for some reason. For that # we depend on the output. return true end # If we reached this point then it didn't work out. raise Vagrant::Errors::VBoxManageError, command: command.inspect, stderr: r.stderr end def suspend execute("controlvm", @uuid, "savestate") end def unshare_folders(names) names.each do |name| begin execute( "sharedfolder", "remove", @uuid, "--name", name, "--transient") execute( "setextradata", @uuid, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/#{name}") rescue Vagrant::Errors::VBoxManageError => e if e.extra_data[:stderr].include?("VBOX_E_FILE_ERROR") # The folder doesn't exist. ignore. else raise end end end end def valid_ip_address?(ip) # Filter out invalid IP addresses # GH-4658 VirtualBox can report an IP address of 0.0.0.0 for FreeBSD guests. if ip == "0.0.0.0" return false else return true end end def verify! # This command sometimes fails if kernel drivers aren't properly loaded # so we just run the command and verify that it succeeded. execute("list", "hostonlyifs") end def verify_image(path) r = raw("import", path.to_s, "--dry-run") return r.exit_code == 0 end def vm_exists?(uuid) 5.times do |i| result = raw("showvminfo", uuid) return true if result.exit_code == 0 # If vboxmanage returned VBOX_E_OBJECT_NOT_FOUND, # then the vm truly does not exist. Any other error might be transient return false if result.stderr.include?("VBOX_E_OBJECT_NOT_FOUND") # Sleep a bit though to give VirtualBox time to fix itself sleep 2 end # If we reach this point, it means that we consistently got the # failure, do a standard vboxmanage now. This will raise an # exception if it fails again. execute("showvminfo", uuid) return true end def create_snapshot(machine_id, snapshot_name) execute("snapshot", machine_id, "take", snapshot_name) end def delete_snapshot(machine_id, snapshot_name) # Start with 0% last = 0 total = "" yield 0 if block_given? # Snapshot and report the % progress execute("snapshot", machine_id, "delete", snapshot_name) do |type, data| if type == :stderr # Append the data so we can see the full view total << data.gsub("\r", "") # Break up the lines. We can't get the progress until we see an "OK" lines = total.split("\n") # The progress of the import will be in the last line. Do a greedy # regular expression to find what we're looking for. match = /.+(\d{2})%/.match(lines.last) if match current = match[1].to_i if current > last last = current yield current if block_given? end end end end end def list_snapshots(machine_id) output = execute( "snapshot", machine_id, "list", "--machinereadable", retryable: true) result = [] output.split("\n").each do |line| if line =~ /^SnapshotName.*?="(.+?)"$/i result << $1.to_s end end result.sort rescue Vagrant::Errors::VBoxManageError => e d = e.extra_data return [] if d[:stderr].include?("does not have") || d[:stdout].include?("does not have") raise end def restore_snapshot(machine_id, snapshot_name) # Start with 0% last = 0 total = "" yield 0 if block_given? execute("snapshot", machine_id, "restore", snapshot_name) do |type, data| if type == :stderr # Append the data so we can see the full view total << data.gsub("\r", "") # Break up the lines. We can't get the progress until we see an "OK" lines = total.split("\n") # The progress of the import will be in the last line. Do a greedy # regular expression to find what we're looking for. match = /.+(\d{2})%/.match(lines.last) if match current = match[1].to_i if current > last last = current yield current if block_given? end end end end end end end end end vagrant-2.0.2/plugins/providers/virtualbox/driver/version_4_3.rb000066400000000000000000000622701323370221500250310ustar00rootroot00000000000000require 'ipaddr' require 'log4r' require "vagrant/util/platform" require File.expand_path("../base", __FILE__) module VagrantPlugins module ProviderVirtualBox module Driver # Driver for VirtualBox 4.3.x class Version_4_3 < Base def initialize(uuid) super() @logger = Log4r::Logger.new("vagrant::provider::virtualbox_4_3") @uuid = uuid end def clear_forwarded_ports args = [] read_forwarded_ports(@uuid).each do |nic, name, _, _| args.concat(["--natpf#{nic}", "delete", name]) end execute("modifyvm", @uuid, *args) if !args.empty? end def clear_shared_folders info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| if line =~ /^SharedFolderNameMachineMapping\d+="(.+?)"$/ execute("sharedfolder", "remove", @uuid, "--name", $1.to_s) end end end def clonevm(master_id, snapshot_name) machine_name = "temp_clone_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}" args = ["--register", "--name", machine_name] if snapshot_name args += ["--snapshot", snapshot_name, "--options", "link"] end execute("clonevm", master_id, *args) return get_machine_id(machine_name) end def create_dhcp_server(network, options) execute("dhcpserver", "add", "--ifname", network, "--ip", options[:dhcp_ip], "--netmask", options[:netmask], "--lowerip", options[:dhcp_lower], "--upperip", options[:dhcp_upper], "--enable") end def create_host_only_network(options) # Create the interface execute("hostonlyif", "create") =~ /^Interface '(.+?)' was successfully created$/ name = $1.to_s # Get the IP so we can determine v4 vs v6 ip = IPAddr.new(options[:adapter_ip]) # Configure if ip.ipv4? execute("hostonlyif", "ipconfig", name, "--ip", options[:adapter_ip], "--netmask", options[:netmask]) elsif ip.ipv6? execute("hostonlyif", "ipconfig", name, "--ipv6", options[:adapter_ip], "--netmasklengthv6", options[:netmask].to_s) end # Return the details return { name: name, ip: options[:adapter_ip], netmask: options[:netmask], dhcp: nil } end def reconfig_host_only(interface) execute("hostonlyif", "ipconfig", interface[:name], "--ipv6", interface[:ipv6]) end def create_snapshot(machine_id, snapshot_name) execute("snapshot", machine_id, "take", snapshot_name) end def delete_snapshot(machine_id, snapshot_name) # Start with 0% last = 0 total = "" yield 0 if block_given? # Snapshot and report the % progress execute("snapshot", machine_id, "delete", snapshot_name) do |type, data| if type == :stderr # Append the data so we can see the full view total << data.gsub("\r", "") # Break up the lines. We can't get the progress until we see an "OK" lines = total.split("\n") # The progress of the import will be in the last line. Do a greedy # regular expression to find what we're looking for. match = /.+(\d{2})%/.match(lines.last) if match current = match[1].to_i if current > last last = current yield current if block_given? end end end end end def list_snapshots(machine_id) output = execute( "snapshot", machine_id, "list", "--machinereadable", retryable: true) result = [] output.split("\n").each do |line| if line =~ /^SnapshotName.*?="(.+?)"$/i result << $1.to_s end end result.sort rescue Vagrant::Errors::VBoxManageError => e d = e.extra_data return [] if d[:stderr].include?("does not have") || d[:stdout].include?("does not have") raise end def restore_snapshot(machine_id, snapshot_name) # Start with 0% last = 0 total = "" yield 0 if block_given? execute("snapshot", machine_id, "restore", snapshot_name) do |type, data| if type == :stderr # Append the data so we can see the full view total << data.gsub("\r", "") # Break up the lines. We can't get the progress until we see an "OK" lines = total.split("\n") # The progress of the import will be in the last line. Do a greedy # regular expression to find what we're looking for. match = /.+(\d{2})%/.match(lines.last) if match current = match[1].to_i if current > last last = current yield current if block_given? end end end end end def delete execute("unregistervm", @uuid, "--delete") end def delete_unused_host_only_networks networks = [] execute("list", "hostonlyifs", retryable: true).split("\n").each do |line| networks << $1.to_s if line =~ /^Name:\s+(.+?)$/ end execute("list", "vms", retryable: true).split("\n").each do |line| if line =~ /^".+?"\s+\{(.+?)\}$/ begin info = execute("showvminfo", $1.to_s, "--machinereadable", retryable: true) info.split("\n").each do |inner_line| if inner_line =~ /^hostonlyadapter\d+="(.+?)"$/ networks.delete($1.to_s) end end rescue Vagrant::Errors::VBoxManageError => e raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND") # VirtualBox could not find the vm. It may have been deleted # by another process after we called 'vboxmanage list vms'? Ignore this error. end end end networks.each do |name| # First try to remove any DHCP servers attached. We use `raw` because # it is okay if this fails. It usually means that a DHCP server was # never attached. raw("dhcpserver", "remove", "--ifname", name) # Delete the actual host only network interface. execute("hostonlyif", "remove", name) end end def discard_saved_state execute("discardstate", @uuid) end def enable_adapters(adapters) args = [] adapters.each do |adapter| args.concat(["--nic#{adapter[:adapter]}", adapter[:type].to_s]) if adapter[:bridge] args.concat(["--bridgeadapter#{adapter[:adapter]}", adapter[:bridge], "--cableconnected#{adapter[:adapter]}", "on"]) end if adapter[:hostonly] args.concat(["--hostonlyadapter#{adapter[:adapter]}", adapter[:hostonly], "--cableconnected#{adapter[:adapter]}", "on"]) end if adapter[:intnet] args.concat(["--intnet#{adapter[:adapter]}", adapter[:intnet], "--cableconnected#{adapter[:adapter]}", "on"]) end if adapter[:mac_address] args.concat(["--macaddress#{adapter[:adapter]}", adapter[:mac_address]]) end if adapter[:nic_type] args.concat(["--nictype#{adapter[:adapter]}", adapter[:nic_type].to_s]) end end execute("modifyvm", @uuid, *args) end def execute_command(command) execute(*command) end def export(path) execute("export", @uuid, "--output", path.to_s) end def forward_ports(ports) args = [] ports.each do |options| pf_builder = [options[:name], options[:protocol] || "tcp", options[:hostip] || "", options[:hostport], options[:guestip] || "", options[:guestport]] args.concat(["--natpf#{options[:adapter] || 1}", pf_builder.join(",")]) end execute("modifyvm", @uuid, *args) if !args.empty? end def get_machine_id(machine_name) output = execute("list", "vms", retryable: true) match = /^"#{Regexp.escape(machine_name)}" \{(.+?)\}$/.match(output) return match[1].to_s if match nil end def halt execute("controlvm", @uuid, "poweroff") end def import(ovf) ovf = Vagrant::Util::Platform.cygwin_windows_path(ovf) output = "" total = "" last = 0 # Dry-run the import to get the suggested name and path @logger.debug("Doing dry-run import to determine parallel-safe name...") output = execute("import", "-n", ovf) result = /Suggested VM name "(.+?)"/.match(output) if !result raise Vagrant::Errors::VirtualBoxNoName, output: output end suggested_name = result[1].to_s # Append millisecond plus a random to the path in case we're # importing the same box elsewhere. specified_name = "#{suggested_name}_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}" @logger.debug("-- Parallel safe name: #{specified_name}") # Build the specified name param list name_params = [ "--vsys", "0", "--vmname", specified_name, ] # Extract the disks list and build the disk target params disk_params = [] disks = output.scan(/(\d+): Hard disk image: source image=.+, target path=(.+),/) disks.each do |unit_num, path| disk_params << "--vsys" disk_params << "0" 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 end execute("import", ovf , *name_params, *disk_params) do |type, data| if type == :stdout # Keep track of the stdout so that we can get the VM name output << data elsif type == :stderr # Append the data so we can see the full view total << data.gsub("\r", "") # Break up the lines. We can't get the progress until we see an "OK" lines = total.split("\n") if lines.include?("OK.") # The progress of the import will be in the last line. Do a greedy # regular expression to find what we're looking for. match = /.+(\d{2})%/.match(lines.last) if match current = match[1].to_i if current > last last = current yield current if block_given? end end end end end return get_machine_id specified_name end def max_network_adapters 8 end def read_forwarded_ports(uuid=nil, active_only=false) uuid ||= @uuid @logger.debug("read_forward_ports: uuid=#{uuid} active_only=#{active_only}") results = [] current_nic = nil info = execute("showvminfo", uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| # This is how we find the nic that a FP is attached to, # since this comes first. current_nic = $1.to_i if line =~ /^nic(\d+)=".+?"$/ # If we care about active VMs only, then we check the state # to verify the VM is running. if active_only && line =~ /^VMState="(.+?)"$/ && $1.to_s != "running" return [] end # Parse out the forwarded port information if line =~ /^Forwarding.+?="(.+?),.+?,.*?,(.+?),.*?,(.+?)"$/ result = [current_nic, $1.to_s, $2.to_i, $3.to_i] @logger.debug(" - #{result.inspect}") results << result end end results end def read_bridged_interfaces execute("list", "bridgedifs").split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if line =~ /^Name:\s+(.+?)$/ info[:name] = $1.to_s elsif line =~ /^IPAddress:\s+(.+?)$/ info[:ip] = $1.to_s elsif line =~ /^NetworkMask:\s+(.+?)$/ info[:netmask] = $1.to_s elsif line =~ /^Status:\s+(.+?)$/ info[:status] = $1.to_s end end # Return the info to build up the results info end end def read_dhcp_servers execute("list", "dhcpservers", retryable: true).split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if network = line[/^NetworkName:\s+HostInterfaceNetworking-(.+?)$/, 1] info[:network] = network info[:network_name] = "HostInterfaceNetworking-#{network}" elsif ip = line[/^IP:\s+(.+?)$/, 1] info[:ip] = ip elsif netmask = line[/^NetworkMask:\s+(.+?)$/, 1] info[:netmask] = netmask elsif lower = line[/^lowerIPAddress:\s+(.+?)$/, 1] info[:lower] = lower elsif upper = line[/^upperIPAddress:\s+(.+?)$/, 1] info[:upper] = upper end end info end end def read_guest_additions_version output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version", retryable: true) if output =~ /^Value: (.+?)$/ # Split the version by _ since some distro versions modify it # to look like this: 4.1.2_ubuntu, and the distro part isn't # too important. value = $1.to_s return value.split("_").first end # If we can't get the guest additions version by guest property, try # to get it from the VM info itself. info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| return $1.to_s if line =~ /^GuestAdditionsVersion="(.+?)"$/ end return nil end def read_guest_ip(adapter_number) ip = read_guest_property("/VirtualBox/GuestInfo/Net/#{adapter_number}/V4/IP") if !valid_ip_address?(ip) raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, guest_property: "/VirtualBox/GuestInfo/Net/#{adapter_number}/V4/IP" end return ip end def read_guest_property(property) output = execute("guestproperty", "get", @uuid, property) if output =~ /^Value: (.+?)$/ $1.to_s else raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, guest_property: property end end def read_host_only_interfaces execute("list", "hostonlyifs", retryable: true).split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if line =~ /^Name:\s+(.+?)$/ info[:name] = $1.to_s elsif line =~ /^IPAddress:\s+(.+?)$/ info[:ip] = $1.to_s elsif line =~ /^NetworkMask:\s+(.+?)$/ info[:netmask] = $1.to_s elsif line =~ /^IPV6Address:\s+(.+?)$/ info[:ipv6] = $1.to_s.strip elsif line =~ /^IPV6NetworkMaskPrefixLength:\s+(.+?)$/ info[:ipv6_prefix] = $1.to_s.strip elsif line =~ /^Status:\s+(.+?)$/ info[:status] = $1.to_s end end info end end def read_mac_address info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| return $1.to_s if line =~ /^macaddress1="(.+?)"$/ end nil end def read_mac_addresses macs = {} info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| if matcher = /^macaddress(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i mac = matcher[2].to_s macs[adapter] = mac end end macs end def read_machine_folder execute("list", "systemproperties", retryable: true).split("\n").each do |line| if line =~ /^Default machine folder:\s+(.+?)$/i return $1.to_s end end nil end def read_network_interfaces nics = {} info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| if line =~ /^nic(\d+)="(.+?)"$/ adapter = $1.to_i type = $2.to_sym nics[adapter] ||= {} nics[adapter][:type] = type elsif line =~ /^hostonlyadapter(\d+)="(.+?)"$/ adapter = $1.to_i network = $2.to_s nics[adapter] ||= {} nics[adapter][:hostonly] = network elsif line =~ /^bridgeadapter(\d+)="(.+?)"$/ adapter = $1.to_i network = $2.to_s nics[adapter] ||= {} nics[adapter][:bridge] = network end end nics end def read_state output = execute("showvminfo", @uuid, "--machinereadable", retryable: true) if output =~ /^name=""$/ return :inaccessible elsif output =~ /^VMState="(.+?)"$/ return $1.to_sym end nil end def read_used_ports ports = [] execute("list", "vms", retryable: true).split("\n").each do |line| if line =~ /^".+?" \{(.+?)\}$/ uuid = $1.to_s # Ignore our own used ports next if uuid == @uuid begin read_forwarded_ports(uuid, true).each do |_, _, hostport, _| ports << hostport end rescue Vagrant::Errors::VBoxManageError => e raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND") # VirtualBox could not find the vm. It may have been deleted # by another process after we called 'vboxmanage list vms'? Ignore this error. end end end ports end def read_vms results = {} execute("list", "vms", retryable: true).split("\n").each do |line| if line =~ /^"(.+?)" \{(.+?)\}$/ results[$1.to_s] = $2.to_s end end results end def remove_dhcp_server(network_name) execute("dhcpserver", "remove", "--netname", network_name) end def set_mac_address(mac) execute("modifyvm", @uuid, "--macaddress1", mac) end def set_name(name) execute("modifyvm", @uuid, "--name", name, retryable: true) rescue Vagrant::Errors::VBoxManageError => e raise if !e.extra_data[:stderr].include?("VERR_ALREADY_EXISTS") # We got VERR_ALREADY_EXISTS. This means that we're renaming to # a VM name that already exists. Raise a custom error. raise Vagrant::Errors::VirtualBoxNameExists, stderr: e.extra_data[:stderr] end def share_folders(folders) is_solaris = begin "SunOS" == read_guest_property("/VirtualBox/GuestInfo/OS/Product") rescue false end folders.each do |folder| hostpath = folder[:hostpath] if Vagrant::Util::Platform.windows? && is_solaris hostpath = Vagrant::Util::Platform.windows_unc_path(hostpath) end args = ["--name", folder[:name], "--hostpath", hostpath] args << "--transient" if folder.key?(:transient) && folder[:transient] if folder[:SharedFoldersEnableSymlinksCreate] # Enable symlinks on the shared folder execute("setextradata", @uuid, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/#{folder[:name]}", "1") end # Add the shared folder execute("sharedfolder", "add", @uuid, *args) end end def ssh_port(expected_port) @logger.debug("Searching for SSH port: #{expected_port.inspect}") # Look for the forwarded port only by comparing the guest port read_forwarded_ports.each do |_, _, hostport, guestport| return hostport if guestport == expected_port end nil end def resume @logger.debug("Resuming paused VM...") execute("controlvm", @uuid, "resume") end def start(mode) command = ["startvm", @uuid, "--type", mode.to_s] r = raw(*command) if r.exit_code == 0 || r.stdout =~ /VM ".+?" has been successfully started/ # Some systems return an exit code 1 for some reason. For that # we depend on the output. return true end # If we reached this point then it didn't work out. raise Vagrant::Errors::VBoxManageError, command: command.inspect, stderr: r.stderr end def suspend execute("controlvm", @uuid, "savestate") end def unshare_folders(names) names.each do |name| begin execute( "sharedfolder", "remove", @uuid, "--name", name, "--transient") execute( "setextradata", @uuid, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/#{name}") rescue Vagrant::Errors::VBoxManageError => e if e.extra_data[:stderr].include?("VBOX_E_FILE_ERROR") # The folder doesn't exist. ignore. else raise end end end end def verify! # This command sometimes fails if kernel drivers aren't properly loaded # so we just run the command and verify that it succeeded. execute("list", "hostonlyifs", retryable: true) end def verify_image(path) r = raw("import", path.to_s, "--dry-run") return r.exit_code == 0 end def vm_exists?(uuid) 5.times do |i| result = raw("showvminfo", uuid) return true if result.exit_code == 0 # If vboxmanage returned VBOX_E_OBJECT_NOT_FOUND, # then the vm truly does not exist. Any other error might be transient return false if result.stderr.include?("VBOX_E_OBJECT_NOT_FOUND") # Sleep a bit though to give VirtualBox time to fix itself sleep 2 end # If we reach this point, it means that we consistently got the # failure, do a standard vboxmanage now. This will raise an # exception if it fails again. execute("showvminfo", uuid) return true end protected def valid_ip_address?(ip) # Filter out invalid IP addresses # GH-4658 VirtualBox can report an IP address of 0.0.0.0 for FreeBSD guests. if ip == "0.0.0.0" return false else return true end end end end end end vagrant-2.0.2/plugins/providers/virtualbox/driver/version_5_0.rb000066400000000000000000000663411323370221500250320ustar00rootroot00000000000000require 'log4r' require "vagrant/util/platform" require File.expand_path("../base", __FILE__) module VagrantPlugins module ProviderVirtualBox module Driver # Driver for VirtualBox 5.0.x class Version_5_0 < Base def initialize(uuid) super() @logger = Log4r::Logger.new("vagrant::provider::virtualbox_5_0") @uuid = uuid end def clear_forwarded_ports retryable(on: Vagrant::Errors::VBoxManageError, tries: 3, sleep: 1) do args = [] read_forwarded_ports(@uuid).each do |nic, name, _, _| args.concat(["--natpf#{nic}", "delete", name]) end execute("modifyvm", @uuid, *args) if !args.empty? end end def clear_shared_folders retryable(on: Vagrant::Errors::VBoxManageError, tries: 3, sleep: 1) do info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| if line =~ /^SharedFolderNameMachineMapping\d+="(.+?)"$/ execute("sharedfolder", "remove", @uuid, "--name", $1.to_s) end end end end def clonevm(master_id, snapshot_name) machine_name = "temp_clone_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}" args = ["--register", "--name", machine_name] if snapshot_name args += ["--snapshot", snapshot_name, "--options", "link"] end execute("clonevm", master_id, *args, retryable: true) return get_machine_id(machine_name) end def create_dhcp_server(network, options) retryable(on: Vagrant::Errors::VBoxManageError, tries: 3, sleep: 1) do begin execute("dhcpserver", "add", "--ifname", network, "--ip", options[:dhcp_ip], "--netmask", options[:netmask], "--lowerip", options[:dhcp_lower], "--upperip", options[:dhcp_upper], "--enable") rescue Vagrant::Errors::VBoxManageError => e return if e.extra_data[:stderr] == 'VBoxManage: error: DHCP server already exists' raise end end end def create_host_only_network(options) # Create the interface execute("hostonlyif", "create", retryable: true) =~ /^Interface '(.+?)' was successfully created$/ name = $1.to_s # Get the IP so we can determine v4 vs v6 ip = IPAddr.new(options[:adapter_ip]) # Configure if ip.ipv4? execute("hostonlyif", "ipconfig", name, "--ip", options[:adapter_ip], "--netmask", options[:netmask], retryable: true) elsif ip.ipv6? execute("hostonlyif", "ipconfig", name, "--ipv6", options[:adapter_ip], "--netmasklengthv6", options[:netmask].to_s, retryable: true) else raise "BUG: Unknown IP type: #{ip.inspect}" end # Return the details return { name: name, ip: options[:adapter_ip], netmask: options[:netmask], dhcp: nil } end def create_snapshot(machine_id, snapshot_name) execute("snapshot", machine_id, "take", snapshot_name, retryable: true) end def delete_snapshot(machine_id, snapshot_name) # Start with 0% last = 0 total = "" yield 0 if block_given? # Snapshot and report the % progress execute("snapshot", machine_id, "delete", snapshot_name, retryable: true) do |type, data| if type == :stderr # Append the data so we can see the full view total << data.gsub("\r", "") # Break up the lines. We can't get the progress until we see an "OK" lines = total.split("\n") # The progress of the import will be in the last line. Do a greedy # regular expression to find what we're looking for. match = /.+(\d{2})%/.match(lines.last) if match current = match[1].to_i if current > last last = current yield current if block_given? end end end end end def list_snapshots(machine_id) output = execute( "snapshot", machine_id, "list", "--machinereadable", retryable: true) result = [] output.split("\n").each do |line| if line =~ /^SnapshotName.*?="(.+?)"$/i result << $1.to_s end end result.sort rescue Vagrant::Errors::VBoxManageError => e d = e.extra_data return [] if d[:stderr].include?("does not have") || d[:stdout].include?("does not have") raise end def restore_snapshot(machine_id, snapshot_name) # Start with 0% last = 0 total = "" yield 0 if block_given? execute("snapshot", machine_id, "restore", snapshot_name, retryable: true) do |type, data| if type == :stderr # Append the data so we can see the full view total << data.gsub("\r", "") # Break up the lines. We can't get the progress until we see an "OK" lines = total.split("\n") # The progress of the import will be in the last line. Do a greedy # regular expression to find what we're looking for. match = /.+(\d{2})%/.match(lines.last) if match current = match[1].to_i if current > last last = current yield current if block_given? end end end end end def delete execute("unregistervm", @uuid, "--delete", retryable: true) end def delete_unused_host_only_networks networks = [] execute("list", "hostonlyifs", retryable: true).split("\n").each do |line| networks << $1.to_s if line =~ /^Name:\s+(.+?)$/ end execute("list", "vms", retryable: true).split("\n").each do |line| if line =~ /^".+?"\s+\{(.+?)\}$/ begin info = execute("showvminfo", $1.to_s, "--machinereadable", retryable: true) info.split("\n").each do |inner_line| if inner_line =~ /^hostonlyadapter\d+="(.+?)"$/ networks.delete($1.to_s) end end rescue Vagrant::Errors::VBoxManageError => e raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND") # VirtualBox could not find the vm. It may have been deleted # by another process after we called 'vboxmanage list vms'? Ignore this error. end end end networks.each do |name| # First try to remove any DHCP servers attached. We use `raw` because # it is okay if this fails. It usually means that a DHCP server was # never attached. raw("dhcpserver", "remove", "--ifname", name) # Delete the actual host only network interface. execute("hostonlyif", "remove", name, retryable: true) end end def discard_saved_state execute("discardstate", @uuid, retryable: true) end def enable_adapters(adapters) args = [] adapters.each do |adapter| args.concat(["--nic#{adapter[:adapter]}", adapter[:type].to_s]) if adapter[:bridge] args.concat(["--bridgeadapter#{adapter[:adapter]}", adapter[:bridge], "--cableconnected#{adapter[:adapter]}", "on"]) end if adapter[:hostonly] args.concat(["--hostonlyadapter#{adapter[:adapter]}", adapter[:hostonly], "--cableconnected#{adapter[:adapter]}", "on"]) end if adapter[:intnet] args.concat(["--intnet#{adapter[:adapter]}", adapter[:intnet], "--cableconnected#{adapter[:adapter]}", "on"]) end if adapter[:mac_address] args.concat(["--macaddress#{adapter[:adapter]}", adapter[:mac_address]]) end if adapter[:nic_type] args.concat(["--nictype#{adapter[:adapter]}", adapter[:nic_type].to_s]) end end execute("modifyvm", @uuid, *args, retryable: true) end def execute_command(command) execute(*command) end def export(path) retryable(on: Vagrant::Errors::VBoxManageError, tries: 3, sleep: 1) do begin execute("export", @uuid, "--output", path.to_s) rescue Vagrant::Errors::VBoxManageError => e raise if !e.extra_data[:stderr].include?("VERR_E_FILE_ERROR") # If the file already exists we'll throw a custom error raise Vagrant::Errors::VirtualBoxFileExists, stderr: e.extra_data[:stderr] end end end def forward_ports(ports) args = [] ports.each do |options| pf_builder = [options[:name], options[:protocol] || "tcp", options[:hostip] || "", options[:hostport], options[:guestip] || "", options[:guestport]] args.concat(["--natpf#{options[:adapter] || 1}", pf_builder.join(",")]) end execute("modifyvm", @uuid, *args, retryable: true) if !args.empty? end def get_machine_id(machine_name) output = execute("list", "vms", retryable: true) match = /^"#{Regexp.escape(machine_name)}" \{(.+?)\}$/.match(output) return match[1].to_s if match nil end def halt execute("controlvm", @uuid, "poweroff", retryable: true) end def import(ovf) ovf = Vagrant::Util::Platform.windows_path(ovf) output = "" total = "" last = 0 # Dry-run the import to get the suggested name and path @logger.debug("Doing dry-run import to determine parallel-safe name...") output = execute("import", "-n", ovf) result = /Suggested VM name "(.+?)"/.match(output) if !result raise Vagrant::Errors::VirtualBoxNoName, output: output end suggested_name = result[1].to_s # Append millisecond plus a random to the path in case we're # importing the same box elsewhere. specified_name = "#{suggested_name}_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}" @logger.debug("-- Parallel safe name: #{specified_name}") # Build the specified name param list name_params = [ "--vsys", "0", "--vmname", specified_name, ] # Extract the disks list and build the disk target params disk_params = [] disks = output.scan(/(\d+): Hard disk image: source image=.+, target path=(.+),/) disks.each do |unit_num, path| disk_params << "--vsys" disk_params << "0" 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 end execute("import", ovf , *name_params, *disk_params, retryable: true) do |type, data| if type == :stdout # Keep track of the stdout so that we can get the VM name output << data elsif type == :stderr # Append the data so we can see the full view total << data.gsub("\r", "") # Break up the lines. We can't get the progress until we see an "OK" lines = total.split("\n") if lines.include?("OK.") # The progress of the import will be in the last line. Do a greedy # regular expression to find what we're looking for. match = /.+(\d{2})%/.match(lines.last) if match current = match[1].to_i if current > last last = current yield current if block_given? end end end end end return get_machine_id specified_name end def max_network_adapters 8 end def read_forwarded_ports(uuid=nil, active_only=false) uuid ||= @uuid @logger.debug("read_forward_ports: uuid=#{uuid} active_only=#{active_only}") results = [] current_nic = nil info = execute("showvminfo", uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| # This is how we find the nic that a FP is attached to, # since this comes first. current_nic = $1.to_i if line =~ /^nic(\d+)=".+?"$/ # If we care about active VMs only, then we check the state # to verify the VM is running. if active_only && line =~ /^VMState="(.+?)"$/ && $1.to_s != "running" return [] end # Parse out the forwarded port information # Forwarding(1)="172.22.8.201tcp32977,tcp,172.22.8.201,32977,,3777" # Forwarding(2)="tcp32978,tcp,,32978,,3777" if line =~ /^Forwarding.+?="(.+?),.+?,(.*?),(.+?),.*?,(.+?)"$/ result = [current_nic, $1.to_s, $3.to_i, $4.to_i, $2] @logger.debug(" - #{result.inspect}") results << result end end results end def read_bridged_interfaces execute("list", "bridgedifs").split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if line =~ /^Name:\s+(.+?)$/ info[:name] = $1.to_s elsif line =~ /^IPAddress:\s+(.+?)$/ info[:ip] = $1.to_s elsif line =~ /^NetworkMask:\s+(.+?)$/ info[:netmask] = $1.to_s elsif line =~ /^Status:\s+(.+?)$/ info[:status] = $1.to_s end end # Return the info to build up the results info end end def read_dhcp_servers execute("list", "dhcpservers", retryable: true).split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if network = line[/^NetworkName:\s+HostInterfaceNetworking-(.+?)$/, 1] info[:network] = network info[:network_name] = "HostInterfaceNetworking-#{network}" elsif ip = line[/^IP:\s+(.+?)$/, 1] info[:ip] = ip elsif netmask = line[/^NetworkMask:\s+(.+?)$/, 1] info[:netmask] = netmask elsif lower = line[/^lowerIPAddress:\s+(.+?)$/, 1] info[:lower] = lower elsif upper = line[/^upperIPAddress:\s+(.+?)$/, 1] info[:upper] = upper end end info end end def read_guest_additions_version output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version", retryable: true) if output =~ /^Value: (.+?)$/ # Split the version by _ since some distro versions modify it # to look like this: 4.1.2_ubuntu, and the distro part isn't # too important. value = $1.to_s return value.split("_").first end # If we can't get the guest additions version by guest property, try # to get it from the VM info itself. info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| return $1.to_s if line =~ /^GuestAdditionsVersion="(.+?)"$/ end return nil end def read_guest_ip(adapter_number) ip = read_guest_property("/VirtualBox/GuestInfo/Net/#{adapter_number}/V4/IP") if !valid_ip_address?(ip) raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, guest_property: "/VirtualBox/GuestInfo/Net/#{adapter_number}/V4/IP" end return ip end def read_guest_property(property) output = execute("guestproperty", "get", @uuid, property) if output =~ /^Value: (.+?)$/ $1.to_s else raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, guest_property: property end end def read_host_only_interfaces execute("list", "hostonlyifs", retryable: true).split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if line =~ /^Name:\s+(.+?)$/ info[:name] = $1.to_s elsif line =~ /^IPAddress:\s+(.+?)$/ info[:ip] = $1.to_s elsif line =~ /^NetworkMask:\s+(.+?)$/ info[:netmask] = $1.to_s elsif line =~ /^IPV6Address:\s+(.+?)$/ info[:ipv6] = $1.to_s.strip elsif line =~ /^IPV6NetworkMaskPrefixLength:\s+(.+?)$/ info[:ipv6_prefix] = $1.to_s.strip elsif line =~ /^Status:\s+(.+?)$/ info[:status] = $1.to_s end end info end end def read_mac_address info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| return $1.to_s if line =~ /^macaddress1="(.+?)"$/ end nil end def read_mac_addresses macs = {} info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| if matcher = /^macaddress(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i mac = matcher[2].to_s macs[adapter] = mac end end macs end def read_machine_folder execute("list", "systemproperties", retryable: true).split("\n").each do |line| if line =~ /^Default machine folder:\s+(.+?)$/i return $1.to_s end end nil end def read_network_interfaces nics = {} info = execute("showvminfo", @uuid, "--machinereadable", retryable: true) info.split("\n").each do |line| if line =~ /^nic(\d+)="(.+?)"$/ adapter = $1.to_i type = $2.to_sym nics[adapter] ||= {} nics[adapter][:type] = type elsif line =~ /^hostonlyadapter(\d+)="(.+?)"$/ adapter = $1.to_i network = $2.to_s nics[adapter] ||= {} nics[adapter][:hostonly] = network elsif line =~ /^bridgeadapter(\d+)="(.+?)"$/ adapter = $1.to_i network = $2.to_s nics[adapter] ||= {} nics[adapter][:bridge] = network end end nics end def read_state output = execute("showvminfo", @uuid, "--machinereadable", retryable: true) if output =~ /^name=""$/ return :inaccessible elsif output =~ /^VMState="(.+?)"$/ return $1.to_sym end nil end def read_used_ports used_ports = Hash.new{|hash, key| hash[key] = Set.new} execute("list", "vms", retryable: true).split("\n").each do |line| if line =~ /^".+?" \{(.+?)\}$/ uuid = $1.to_s # Ignore our own used ports next if uuid == @uuid begin read_forwarded_ports(uuid, true).each do |_, _, hostport, _, hostip| hostip = '*' if hostip.nil? || hostip.empty? used_ports[hostport].add?(hostip) end rescue Vagrant::Errors::VBoxManageError => e raise if !e.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND") # VirtualBox could not find the vm. It may have been deleted # by another process after we called 'vboxmanage list vms'? Ignore this error. end end end used_ports end def read_vms results = {} execute("list", "vms", retryable: true).split("\n").each do |line| if line =~ /^"(.+?)" \{(.+?)\}$/ results[$1.to_s] = $2.to_s end end results end def reconfig_host_only(interface) execute("hostonlyif", "ipconfig", interface[:name], "--ipv6", interface[:ipv6], retryable: true) end def remove_dhcp_server(network_name) execute("dhcpserver", "remove", "--netname", network_name, retryable: true) end def set_mac_address(mac) execute("modifyvm", @uuid, "--macaddress1", mac, retryable: true) end def set_name(name) retryable(on: Vagrant::Errors::VBoxManageError, tries: 3, sleep: 1) do begin execute("modifyvm", @uuid, "--name", name) rescue Vagrant::Errors::VBoxManageError => e raise if !e.extra_data[:stderr].include?("VERR_ALREADY_EXISTS") # We got VERR_ALREADY_EXISTS. This means that we're renaming to # a VM name that already exists. Raise a custom error. raise Vagrant::Errors::VirtualBoxNameExists, stderr: e.extra_data[:stderr] end end end def share_folders(folders) is_solaris = begin "SunOS" == read_guest_property("/VirtualBox/GuestInfo/OS/Product") rescue false end folders.each do |folder| # NOTE: Guest additions on Solaris guests do not properly handle # UNC style paths so prevent conversion (See GH-7264) if is_solaris hostpath = folder[:hostpath] else hostpath = Vagrant::Util::Platform.windows_path(folder[:hostpath]) end args = ["--name", folder[:name], "--hostpath", hostpath] args << "--transient" if folder.key?(:transient) && folder[:transient] if folder[:SharedFoldersEnableSymlinksCreate] # Enable symlinks on the shared folder execute("setextradata", @uuid, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/#{folder[:name]}", "1", retryable: true) end # Add the shared folder execute("sharedfolder", "add", @uuid, *args, retryable: true) end end def ssh_port(expected_port) @logger.debug("Searching for SSH port: #{expected_port.inspect}") # Look for the forwarded port only by comparing the guest port read_forwarded_ports.each do |_, _, hostport, guestport| return hostport if guestport == expected_port end nil end def resume @logger.debug("Resuming paused VM...") execute("controlvm", @uuid, "resume") end def start(mode) command = ["startvm", @uuid, "--type", mode.to_s] retryable(on: Vagrant::Errors::VBoxManageError, tries: 3, sleep: 1) do r = raw(*command) if r.exit_code == 0 || r.stdout =~ /VM ".+?" has been successfully started/ # Some systems return an exit code 1 for some reason. For that # we depend on the output. return true end # If we reached this point then it didn't work out. raise Vagrant::Errors::VBoxManageError, command: command.inspect, stderr: r.stderr end end def suspend execute("controlvm", @uuid, "savestate", retryable: true) end def unshare_folders(names) names.each do |name| retryable(on: Vagrant::Errors::VBoxManageError, tries: 3, sleep: 1) do begin execute( "sharedfolder", "remove", @uuid, "--name", name, "--transient") execute( "setextradata", @uuid, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/#{name}") rescue Vagrant::Errors::VBoxManageError => e raise if !e.extra_data[:stderr].include?("VBOX_E_FILE_ERROR") end end end end def verify! # This command sometimes fails if kernel drivers aren't properly loaded # so we just run the command and verify that it succeeded. execute("list", "hostonlyifs", retryable: true) end def verify_image(path) r = raw("import", path.to_s, "--dry-run") return r.exit_code == 0 end def vm_exists?(uuid) 5.times do |i| result = raw("showvminfo", uuid) return true if result.exit_code == 0 # If vboxmanage returned VBOX_E_OBJECT_NOT_FOUND, # then the vm truly does not exist. Any other error might be transient return false if result.stderr.include?("VBOX_E_OBJECT_NOT_FOUND") # Sleep a bit though to give VirtualBox time to fix itself sleep 2 end # If we reach this point, it means that we consistently got the # failure, do a standard vboxmanage now. This will raise an # exception if it fails again. execute("showvminfo", uuid) return true end protected def valid_ip_address?(ip) # Filter out invalid IP addresses # GH-4658 VirtualBox can report an IP address of 0.0.0.0 for FreeBSD guests. if ip == "0.0.0.0" return false else return true end end end end end end vagrant-2.0.2/plugins/providers/virtualbox/driver/version_5_1.rb000066400000000000000000000005451323370221500250250ustar00rootroot00000000000000require File.expand_path("../version_5_0", __FILE__) module VagrantPlugins module ProviderVirtualBox module Driver # Driver for VirtualBox 5.1.x class Version_5_1 < Version_5_0 def initialize(uuid) super @logger = Log4r::Logger.new("vagrant::provider::virtualbox_5_1") end end end end end vagrant-2.0.2/plugins/providers/virtualbox/driver/version_5_2.rb000066400000000000000000000005451323370221500250260ustar00rootroot00000000000000require File.expand_path("../version_5_1", __FILE__) module VagrantPlugins module ProviderVirtualBox module Driver # Driver for VirtualBox 5.2.x class Version_5_2 < Version_5_1 def initialize(uuid) super @logger = Log4r::Logger.new("vagrant::provider::virtualbox_5_2") end end end end end vagrant-2.0.2/plugins/providers/virtualbox/model/000077500000000000000000000000001323370221500221505ustar00rootroot00000000000000vagrant-2.0.2/plugins/providers/virtualbox/model/forwarded_port.rb000066400000000000000000000037211323370221500255210ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Model # Represents a single forwarded port for VirtualBox. This has various # helpers and defaults for a forwarded port. class ForwardedPort # The NAT adapter on which to attach the forwarded port. # # @return [Integer] attr_reader :adapter # If true, this port should be auto-corrected. # # @return [Boolean] attr_reader :auto_correct # The unique ID for the forwarded port. # # @return [String] attr_reader :id # The protocol to forward. # # @return [String] attr_reader :protocol # The IP that the forwarded port will connect to on the guest machine. # # @return [String] attr_reader :guest_ip # The port on the guest to be exposed on the host. # # @return [Integer] attr_reader :guest_port # The IP that the forwarded port will bind to on the host machine. # # @return [String] attr_reader :host_ip # The port on the host used to access the port on the guest. # # @return [Integer] attr_reader :host_port def initialize(id, host_port, guest_port, options) @id = id @guest_port = guest_port @host_port = host_port options ||= {} @auto_correct = false @auto_correct = options[:auto_correct] if options.key?(:auto_correct) @adapter = (options[:adapter] || 1).to_i @guest_ip = options[:guest_ip] || nil @host_ip = options[:host_ip] || nil @protocol = options[:protocol] || "tcp" end # This corrects the host port and changes it to the given new port. # # @param [Integer] new_port The new port def correct_host_port(new_port) @host_port = new_port end end end end end vagrant-2.0.2/plugins/providers/virtualbox/plugin.rb000066400000000000000000000043111323370221500226720ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module ProviderVirtualBox class Plugin < Vagrant.plugin("2") name "VirtualBox provider" description <<-EOF The VirtualBox provider allows Vagrant to manage and control VirtualBox-based virtual machines. EOF provider(:virtualbox, priority: 6) do require File.expand_path("../provider", __FILE__) Provider end config(:virtualbox, :provider) do require File.expand_path("../config", __FILE__) Config end synced_folder(:virtualbox) do require File.expand_path("../synced_folder", __FILE__) SyncedFolder end provider_capability(:virtualbox, :forwarded_ports) do require_relative "cap" Cap end provider_capability(:virtualbox, :nic_mac_addresses) do require_relative "cap" Cap end provider_capability(:virtualbox, :public_address) do require_relative "cap/public_address" Cap::PublicAddress end provider_capability(:virtualbox, :snapshot_list) do require_relative "cap" Cap end end autoload :Action, File.expand_path("../action", __FILE__) # Drop some autoloads in here to optimize the performance of loading # our drivers only when they are needed. module Driver autoload :Meta, File.expand_path("../driver/meta", __FILE__) autoload :Version_4_0, File.expand_path("../driver/version_4_0", __FILE__) autoload :Version_4_1, File.expand_path("../driver/version_4_1", __FILE__) autoload :Version_4_2, File.expand_path("../driver/version_4_2", __FILE__) autoload :Version_4_3, File.expand_path("../driver/version_4_3", __FILE__) autoload :Version_5_0, File.expand_path("../driver/version_5_0", __FILE__) autoload :Version_5_1, File.expand_path("../driver/version_5_1", __FILE__) autoload :Version_5_2, File.expand_path("../driver/version_5_2", __FILE__) end module Model autoload :ForwardedPort, File.expand_path("../model/forwarded_port", __FILE__) end module Util autoload :CompileForwardedPorts, File.expand_path("../util/compile_forwarded_ports", __FILE__) end end end vagrant-2.0.2/plugins/providers/virtualbox/provider.rb000066400000000000000000000101371323370221500232310ustar00rootroot00000000000000require "log4r" module VagrantPlugins module ProviderVirtualBox class Provider < Vagrant.plugin("2", :provider) attr_reader :driver def self.installed? Driver::Meta.new true rescue Vagrant::Errors::VirtualBoxInvalidVersion return false rescue Vagrant::Errors::VirtualBoxNotDetected return false end def self.usable?(raise_error=false) # Instantiate the driver, which will determine the VirtualBox # 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 raise if raise_error return false end def initialize(machine) @logger = Log4r::Logger.new("vagrant::provider::virtualbox") @machine = machine # This method will load in our driver, so we call it now to # initialize it. machine_id_changed end # @see Vagrant::Plugin::V1::Provider#action def action(name) # Attempt to get the action method from the Action class if it # exists, otherwise return nil to show that we don't support the # given action. action_method = "action_#{name}" return Action.send(action_method) if Action.respond_to?(action_method) nil end # If the machine ID changed, then we need to rebuild our underlying # driver. def machine_id_changed id = @machine.id begin @logger.debug("Instantiating the driver for machine ID: #{@machine.id.inspect}") @driver = Driver::Meta.new(id) rescue Driver::Meta::VMNotFound # The virtual machine doesn't exist, so we probably have a stale # ID. Just clear the id out of the machine and reload it. @logger.debug("VM not found! Clearing saved machine ID and reloading.") id = nil retry end end # Returns the SSH info for accessing the VirtualBox VM. def ssh_info # If the VM is not running that we can't possibly SSH into it return nil if state.id != :running # Return what we know. The host is always "127.0.0.1" because # VirtualBox VMs are always local. The port we try to discover # by reading the forwarded ports. return { host: "127.0.0.1", port: @driver.ssh_port(@machine.config.ssh.guest_port) } end # Return the state of VirtualBox virtual machine by actually # querying VBoxManage. # # @return [Symbol] def state # We have to check if the UID matches to avoid issues with # VirtualBox. if Vagrant::Util::Platform.wsl_windows_access_bypass?(@machine.data_dir) @logger.warn("Skipping UID check on machine by user request for WSL Windows access.") else uid = @machine.uid if uid && uid.to_s != Process.uid.to_s raise Vagrant::Errors::VirtualBoxUserMismatch, original_uid: uid.to_s, uid: Process.uid.to_s end end # Determine the ID of the state here. state_id = nil state_id = :not_created if !@driver.uuid state_id = @driver.read_state if !state_id state_id = :unknown if !state_id # Translate into short/long descriptions short = state_id.to_s.gsub("_", " ") long = I18n.t("vagrant.commands.status.#{state_id}") # If we're not created, then specify the special ID flag if state_id == :not_created state_id = Vagrant::MachineState::NOT_CREATED_ID end # Return the state Vagrant::MachineState.new(state_id, short, long) end # Returns a human-friendly string version of this provider which # includes the machine's ID that this provider represents, if it # has one. # # @return [String] def to_s id = @machine.id ? @machine.id : "new VM" "VirtualBox (#{id})" end end end end vagrant-2.0.2/plugins/providers/virtualbox/synced_folder.rb000066400000000000000000000114011323370221500242120ustar00rootroot00000000000000require "fileutils" require "vagrant/util/platform" module VagrantPlugins module ProviderVirtualBox class SyncedFolder < Vagrant.plugin("2", :synced_folder) def usable?(machine, raise_errors=false) # These synced folders only work if the provider if VirtualBox return false if machine.provider_name != :virtualbox # This only happens with `vagrant package --base`. Sigh. return true if !machine.provider_config machine.provider_config.functional_vboxsf end def prepare(machine, folders, _opts) share_folders(machine, folders, false) end def enable(machine, folders, _opts) share_folders(machine, folders, true) # short guestpaths first, so we don't step on ourselves folders = folders.sort_by do |id, data| if data[:guestpath] data[:guestpath].length else # A long enough path to just do this at the end. 10000 end end # Go through each folder and mount machine.ui.output(I18n.t("vagrant.actions.vm.share_folders.mounting")) folders.each do |id, data| if data[:guestpath] # Guest path specified, so mount the folder to specified point machine.ui.detail(I18n.t("vagrant.actions.vm.share_folders.mounting_entry", guestpath: data[:guestpath], hostpath: data[:hostpath])) # Dup the data so we can pass it to the guest API data = data.dup # Calculate the owner and group ssh_info = machine.ssh_info data[:owner] ||= ssh_info[:username] data[:group] ||= ssh_info[:username] # Mount the actual folder machine.guest.capability( :mount_virtualbox_shared_folder, os_friendly_id(id), data[:guestpath], data) else # If no guest path is specified, then automounting is disabled machine.ui.detail(I18n.t("vagrant.actions.vm.share_folders.nomount_entry", hostpath: data[:hostpath])) end end end def disable(machine, folders, _opts) if machine.guest.capability?(:unmount_virtualbox_shared_folder) folders.each do |id, data| machine.guest.capability( :unmount_virtualbox_shared_folder, data[:guestpath], data) end end # Remove the shared folders from the VM metadata names = folders.map { |id, _data| os_friendly_id(id) } driver(machine).unshare_folders(names) end def cleanup(machine, opts) driver(machine).clear_shared_folders if machine.id && machine.id != "" end protected # This is here so that we can stub it for tests def driver(machine) machine.provider.driver end def os_friendly_id(id) id.gsub(/[\s\/\\]/,'_').sub(/^_/, '') end # share_folders sets up the shared folder definitions on the # VirtualBox VM. # # The transient parameter determines if we're FORCING transient # or not. If this is false, then any shared folders will be # shared as non-transient unless they've specifically asked for # transient. def share_folders(machine, folders, transient) defs = [] warn_user_symlink = false folders.each do |id, data| hostpath = data[:hostpath] if !data[:hostpath_exact] hostpath = Vagrant::Util::Platform.cygwin_windows_path(hostpath) end enable_symlink_create = true if ENV['VAGRANT_DISABLE_VBOXSYMLINKCREATE'] enable_symlink_create = false end unless data[:SharedFoldersEnableSymlinksCreate].nil? enable_symlink_create = data[:SharedFoldersEnableSymlinksCreate] end warn_user_symlink ||= enable_symlink_create # Only setup the shared folders that match our transient level if (!!data[:transient]) == transient defs << { name: os_friendly_id(id), hostpath: hostpath.to_s, transient: transient, SharedFoldersEnableSymlinksCreate: enable_symlink_create } end end if warn_user_symlink display_symlink_create_warning(machine.env) end driver(machine).share_folders(defs) end def display_symlink_create_warning(env) d_file = env.data_dir.join("vbox_symlink_create_warning") if !d_file.exist? FileUtils.touch(d_file.to_path) env.ui.warn(I18n.t("vagrant.virtualbox.warning.shared_folder_symlink_create")) end end end end end vagrant-2.0.2/plugins/providers/virtualbox/util/000077500000000000000000000000001323370221500220255ustar00rootroot00000000000000vagrant-2.0.2/plugins/providers/virtualbox/util/compile_forwarded_ports.rb000066400000000000000000000021341323370221500272660ustar00rootroot00000000000000require "vagrant/util/scoped_hash_override" module VagrantPlugins module ProviderVirtualBox module Util module CompileForwardedPorts include Vagrant::Util::ScopedHashOverride # This method compiles the forwarded ports into {ForwardedPort} # models. def compile_forwarded_ports(config) mappings = {} config.vm.networks.each do |type, options| if type == :forwarded_port guest_port = options[:guest] host_port = options[:host] host_ip = options[:host_ip] protocol = options[:protocol] || "tcp" options = scoped_hash_override(options, :virtualbox) id = options[:id] # If the forwarded port was marked as disabled, ignore. next if options[:disabled] key = "#{host_ip}#{protocol}#{host_port}" mappings[key] = Model::ForwardedPort.new(id, host_port, guest_port, options) end end mappings.values end end end end end vagrant-2.0.2/plugins/provisioners/000077500000000000000000000000001323370221500173765ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/ansible/000077500000000000000000000000001323370221500210135ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/ansible/cap/000077500000000000000000000000001323370221500215565ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/000077500000000000000000000000001323370221500227055ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/arch/000077500000000000000000000000001323370221500236225ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/arch/ansible_install.rb000066400000000000000000000011421323370221500273100ustar00rootroot00000000000000require_relative "../../../errors" module VagrantPlugins module Ansible module Cap module Guest module Arch module AnsibleInstall def self.ansible_install(machine, install_mode, ansible_version, pip_args) if install_mode != :default raise Ansible::Errors::AnsiblePipInstallIsNotSupported else machine.communicate.sudo "pacman -Syy --noconfirm" machine.communicate.sudo "pacman -S --noconfirm ansible" end end end end end end end end vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/debian/000077500000000000000000000000001323370221500241275ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/debian/ansible_install.rb000066400000000000000000000027721323370221500276270ustar00rootroot00000000000000require_relative "../pip/pip" module VagrantPlugins module Ansible module Cap module Guest module Debian module AnsibleInstall def self.ansible_install(machine, install_mode, ansible_version, pip_args) case install_mode when :pip pip_setup machine Pip::pip_install machine, "ansible", ansible_version, pip_args, true when :pip_args_only pip_setup machine Pip::pip_install machine, "", "", pip_args, false else ansible_apt_install machine end end private def self.ansible_apt_install(machine) install_backports_if_wheezy_release = < /etc/apt/sources.list.d/wheezy-backports.list fi INLINE_CRIPT machine.communicate.sudo install_backports_if_wheezy_release machine.communicate.sudo "apt-get update -y -qq" machine.communicate.sudo "apt-get install -y -qq ansible" end def self.pip_setup(machine) 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 end end end end end end end vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/facts.rb000066400000000000000000000007131323370221500243330ustar00rootroot00000000000000 module VagrantPlugins module Ansible module Cap module Guest module Facts def self.dnf?(machine) machine.communicate.test "/usr/bin/which -s dnf" end def self.yum?(machine) machine.communicate.test "/usr/bin/which -s yum" end def self.rpm_package_manager(machine) dnf?(machine) ? "dnf" : "yum" end end end end end end vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/fedora/000077500000000000000000000000001323370221500241455ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/fedora/ansible_install.rb000066400000000000000000000023201323370221500276320ustar00rootroot00000000000000require_relative "../facts" require_relative "../pip/pip" module VagrantPlugins module Ansible module Cap module Guest module Fedora module AnsibleInstall def self.ansible_install(machine, install_mode, ansible_version, pip_args) case install_mode when :pip pip_setup machine Pip::pip_install machine, "ansible", ansible_version, pip_args, true when :pip_args_only pip_setup machine Pip::pip_install machine, "", "", pip_args, false else rpm_package_manager = Facts::rpm_package_manager(machine) machine.communicate.sudo "#{rpm_package_manager} -y install ansible" end end private def self.pip_setup(machine) 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 end end end end end end end vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/freebsd/000077500000000000000000000000001323370221500243175ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/freebsd/ansible_install.rb000066400000000000000000000010361323370221500300070ustar00rootroot00000000000000require_relative "../../../errors" module VagrantPlugins module Ansible module Cap module Guest module FreeBSD module AnsibleInstall def self.ansible_install(machine, install_mode, ansible_version, pip_args) if install_mode != :default raise Ansible::Errors::AnsiblePipInstallIsNotSupported else machine.communicate.sudo "yes | pkg install ansible" end end end end end end end end vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/pip/000077500000000000000000000000001323370221500234755ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/pip/pip.rb000066400000000000000000000015131323370221500246120ustar00rootroot00000000000000 module VagrantPlugins module Ansible module Cap module Guest module Pip def self.pip_install(machine, package = "", version = "", pip_args = "", upgrade = true) upgrade_arg = "--upgrade" if upgrade version_arg = "" if !version.to_s.empty? && version.to_s.to_sym != :latest version_arg = "==#{version}" end args_array = [pip_args, upgrade_arg, "#{package}#{version_arg}"] machine.communicate.sudo "pip install #{args_array.join(' ')}" end def self.get_pip(machine) machine.ui.detail I18n.t("vagrant.provisioners.ansible.installing_pip") machine.communicate.execute "curl https://bootstrap.pypa.io/get-pip.py | sudo python" end end end end end end vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/posix/000077500000000000000000000000001323370221500240475ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/posix/ansible_installed.rb000066400000000000000000000011421323370221500300460ustar00rootroot00000000000000module VagrantPlugins module Ansible module Cap module Guest module POSIX module AnsibleInstalled # Check if Ansible is installed (at the given version). # @return [true, false] def self.ansible_installed(machine, version) command = 'test -x "$(command -v ansible)"' if !version.empty? command << "&& ansible --version | grep 'ansible #{version}'" end machine.communicate.test command, sudo: false end end end end end end end vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/redhat/000077500000000000000000000000001323370221500241545ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/redhat/ansible_install.rb000066400000000000000000000031761323370221500276530ustar00rootroot00000000000000require_relative "../facts" require_relative "../pip/pip" module VagrantPlugins module Ansible module Cap module Guest module RedHat module AnsibleInstall def self.ansible_install(machine, install_mode, ansible_version, pip_args) case install_mode when :pip pip_setup machine Pip::pip_install machine, "ansible", ansible_version, pip_args, true when :pip_args_only pip_setup machine Pip::pip_install machine, "", "", pip_args, false else ansible_rpm_install machine end end private def self.ansible_rpm_install(machine) rpm_package_manager = Facts::rpm_package_manager(machine) epel = machine.communicate.execute "#{rpm_package_manager} repolist epel | grep -q epel", error_check: false if epel != 0 machine.communicate.sudo 'sudo rpm -i https://dl.fedoraproject.org/pub/epel/epel-release-latest-`rpm -E %dist | sed -n \'s/.*el\([0-9]\).*/\1/p\'`.noarch.rpm' end machine.communicate.sudo "#{rpm_package_manager} -y --enablerepo=epel install ansible" end def self.pip_setup(machine) 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 end end end end end end end vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/suse/000077500000000000000000000000001323370221500236645ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/suse/ansible_install.rb000066400000000000000000000010201323370221500273450ustar00rootroot00000000000000 module VagrantPlugins module Ansible module Cap module Guest module SUSE module AnsibleInstall def self.ansible_install(machine, install_mode, ansible_version, pip_args) if install_mode != :default raise Ansible::Errors::AnsiblePipInstallIsNotSupported else machine.communicate.sudo("zypper --non-interactive --quiet install ansible") end end end end end end end end vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/ubuntu/000077500000000000000000000000001323370221500242275ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/ansible/cap/guest/ubuntu/ansible_install.rb000066400000000000000000000020271323370221500277200ustar00rootroot00000000000000require_relative "../debian/ansible_install" module VagrantPlugins module Ansible module Cap module Guest module Ubuntu module AnsibleInstall def self.ansible_install(machine, install_mode, ansible_version, pip_args) if install_mode != :default Debian::AnsibleInstall::ansible_install machine, install_mode, ansible_version, pip_args else ansible_apt_install machine end end private def self.ansible_apt_install(machine) machine.communicate.sudo "apt-get update -y -qq" machine.communicate.sudo "apt-get install -y -qq software-properties-common python-software-properties" machine.communicate.sudo "add-apt-repository ppa:ansible/ansible -y" machine.communicate.sudo "apt-get update -y -qq" machine.communicate.sudo "apt-get install -y -qq ansible" end end end end end end end vagrant-2.0.2/plugins/provisioners/ansible/config/000077500000000000000000000000001323370221500222605ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/ansible/config/base.rb000066400000000000000000000153021323370221500235200ustar00rootroot00000000000000require_relative "../constants" module VagrantPlugins module Ansible module Config class Base < Vagrant.plugin("2", :config) GALAXY_COMMAND_DEFAULT = "ansible-galaxy install --role-file=%{role_file} --roles-path=%{roles_path} --force".freeze PLAYBOOK_COMMAND_DEFAULT = "ansible-playbook".freeze attr_accessor :become attr_accessor :become_user attr_accessor :compatibility_mode attr_accessor :config_file attr_accessor :extra_vars attr_accessor :galaxy_role_file attr_accessor :galaxy_roles_path attr_accessor :galaxy_command attr_accessor :groups attr_accessor :host_vars attr_accessor :inventory_path attr_accessor :limit attr_accessor :playbook attr_accessor :playbook_command attr_accessor :raw_arguments attr_accessor :skip_tags attr_accessor :start_at_task attr_accessor :tags attr_accessor :vault_password_file attr_accessor :verbose attr_accessor :version # # Deprecated options # alias :sudo :become def sudo=(value) show_deprecation_info 'sudo', 'become' @become = value end alias :sudo_user :become_user def sudo_user=(value) show_deprecation_info 'sudo_user', 'become_user' @become_user = value end def initialize @become = UNSET_VALUE @become_user = UNSET_VALUE @compatibility_mode = Ansible::COMPATIBILITY_MODE_AUTO @config_file = UNSET_VALUE @extra_vars = UNSET_VALUE @galaxy_role_file = UNSET_VALUE @galaxy_roles_path = UNSET_VALUE @galaxy_command = UNSET_VALUE @groups = UNSET_VALUE @host_vars = UNSET_VALUE @inventory_path = UNSET_VALUE @limit = UNSET_VALUE @playbook = UNSET_VALUE @playbook_command = UNSET_VALUE @raw_arguments = UNSET_VALUE @skip_tags = UNSET_VALUE @start_at_task = UNSET_VALUE @tags = UNSET_VALUE @vault_password_file = UNSET_VALUE @verbose = UNSET_VALUE @version = UNSET_VALUE end def finalize! @become = false if @become != true @become_user = nil if @become_user == UNSET_VALUE @compatibility_mode = nil unless Ansible::COMPATIBILITY_MODES.include?(@compatibility_mode) @config_file = nil if @config_file == UNSET_VALUE @extra_vars = nil if @extra_vars == UNSET_VALUE @galaxy_role_file = nil if @galaxy_role_file == UNSET_VALUE @galaxy_roles_path = nil if @galaxy_roles_path == UNSET_VALUE @galaxy_command = GALAXY_COMMAND_DEFAULT if @galaxy_command == UNSET_VALUE @groups = {} if @groups == UNSET_VALUE @host_vars = {} if @host_vars == UNSET_VALUE @inventory_path = nil if @inventory_path == UNSET_VALUE @limit = nil if @limit == UNSET_VALUE @playbook = nil if @playbook == UNSET_VALUE @playbook_command = PLAYBOOK_COMMAND_DEFAULT if @playbook_command == UNSET_VALUE @raw_arguments = nil if @raw_arguments == UNSET_VALUE @skip_tags = nil if @skip_tags == UNSET_VALUE @start_at_task = nil if @start_at_task == UNSET_VALUE @tags = nil if @tags == UNSET_VALUE @vault_password_file = nil if @vault_password_file == UNSET_VALUE @verbose = false if @verbose == UNSET_VALUE @version = "" if @version == UNSET_VALUE end # Just like the normal configuration "validate" method except that # it returns an array of errors that should be merged into some # other error accumulator. def validate(machine) @errors = _detected_errors # Validate that a compatibility mode was provided if !compatibility_mode @errors << I18n.t("vagrant.provisioners.ansible.errors.no_compatibility_mode", valid_modes: Ansible::COMPATIBILITY_MODES.map { |s| "'#{s}'" }.join(', ')) end # Validate that a playbook path was provided if !playbook @errors << I18n.t("vagrant.provisioners.ansible.errors.no_playbook") end # Validate that extra_vars is either a Hash or a String (for a file path) if extra_vars extra_vars_is_valid = extra_vars.kind_of?(Hash) || extra_vars.kind_of?(String) if extra_vars.kind_of?(String) # Accept the usage of '@' prefix in Vagrantfile # (e.g. '@vars.yml' and 'vars.yml' are both supported) match_data = /^@?(.+)$/.match(extra_vars) extra_vars_path = match_data[1].to_s @extra_vars = '@' + extra_vars_path end if !extra_vars_is_valid @errors << I18n.t( "vagrant.provisioners.ansible.errors.extra_vars_invalid", type: extra_vars.class.to_s, value: extra_vars.to_s) end end if raw_arguments if raw_arguments.kind_of?(String) @raw_arguments = [raw_arguments] elsif !raw_arguments.kind_of?(Array) @errors << I18n.t( "vagrant.provisioners.ansible.errors.raw_arguments_invalid", type: raw_arguments.class.to_s, value: raw_arguments.to_s) end end end protected def show_deprecation_info(deprecated_option, new_option) puts "DEPRECATION: The '#{deprecated_option}' option for the Ansible provisioner is deprecated." puts "Please use the '#{new_option}' option instead." puts "The '#{deprecated_option}' option will be removed in a future release of Vagrant.\n\n" end end end end end vagrant-2.0.2/plugins/provisioners/ansible/config/guest.rb000066400000000000000000000026651323370221500237450ustar00rootroot00000000000000require_relative "base" require_relative "../helpers" module VagrantPlugins module Ansible module Config class Guest < Base attr_accessor :provisioning_path attr_accessor :tmp_path attr_accessor :install attr_accessor :install_mode attr_accessor :pip_args def initialize super @install = UNSET_VALUE @install_mode = UNSET_VALUE @pip_args = UNSET_VALUE @provisioning_path = UNSET_VALUE @tmp_path = UNSET_VALUE end def finalize! super @install = true if @install == UNSET_VALUE @install_mode = :default if @install_mode == UNSET_VALUE @pip_args = "" if @pip_args == UNSET_VALUE @provisioning_path = "/vagrant" if provisioning_path == UNSET_VALUE @tmp_path = "/tmp/vagrant-ansible" if tmp_path == UNSET_VALUE end def validate(machine) super case @install_mode.to_s.to_sym when :pip @install_mode = :pip when :pip_args_only @install_mode = :pip_args_only else @install_mode = :default end { "ansible local provisioner" => @errors } end end end end end vagrant-2.0.2/plugins/provisioners/ansible/config/host.rb000066400000000000000000000033151323370221500235640ustar00rootroot00000000000000require_relative "base" module VagrantPlugins module Ansible module Config class Host < Base attr_accessor :ask_become_pass attr_accessor :ask_vault_pass attr_accessor :force_remote_user attr_accessor :host_key_checking attr_accessor :raw_ssh_args # # Deprecated options # alias :ask_sudo_pass :ask_become_pass def ask_sudo_pass=(value) show_deprecation_info 'ask_sudo_pass', 'ask_become_pass' @ask_become_pass = value end def initialize super @ask_become_pass = false @ask_vault_pass = false @force_remote_user = true @host_key_checking = false @raw_ssh_args = UNSET_VALUE end def finalize! super @ask_become_pass = false if @ask_become_pass != true @ask_vault_pass = false if @ask_vault_pass != true @force_remote_user = true if @force_remote_user != false @host_key_checking = false if @host_key_checking != true @raw_ssh_args = nil if @raw_ssh_args == UNSET_VALUE end def validate(machine) super if raw_ssh_args if raw_ssh_args.kind_of?(String) @raw_ssh_args = [raw_ssh_args] elsif !raw_ssh_args.kind_of?(Array) @errors << I18n.t( "vagrant.provisioners.ansible.errors.raw_ssh_args_invalid", type: raw_ssh_args.class.to_s, value: raw_ssh_args.to_s) end end { "ansible remote provisioner" => @errors } end end end end end vagrant-2.0.2/plugins/provisioners/ansible/constants.rb000066400000000000000000000006071323370221500233570ustar00rootroot00000000000000 module VagrantPlugins module Ansible COMPATIBILITY_MODE_AUTO = "auto".freeze COMPATIBILITY_MODE_V1_8 = "1.8".freeze COMPATIBILITY_MODE_V2_0 = "2.0".freeze SAFE_COMPATIBILITY_MODE = COMPATIBILITY_MODE_V1_8 COMPATIBILITY_MODES = [ COMPATIBILITY_MODE_AUTO, COMPATIBILITY_MODE_V1_8, COMPATIBILITY_MODE_V2_0, ].freeze end endvagrant-2.0.2/plugins/provisioners/ansible/errors.rb000066400000000000000000000017461323370221500226640ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Ansible module Errors class AnsibleError < Vagrant::Errors::VagrantError error_namespace("vagrant.provisioners.ansible.errors") end class AnsibleCommandFailed < AnsibleError error_key(:ansible_command_failed) end class AnsibleCompatibilityModeConflict < AnsibleError error_key(:ansible_compatibility_mode_conflict) end class AnsibleNotFoundOnGuest < AnsibleError error_key(:ansible_not_found_on_guest) end class AnsibleNotFoundOnHost < AnsibleError error_key(:ansible_not_found_on_host) end class AnsiblePipInstallIsNotSupported < AnsibleError error_key(:cannot_support_pip_install) end class AnsibleProgrammingError < AnsibleError error_key(:ansible_programming_error) end class AnsibleVersionMismatch < AnsibleError error_key(:ansible_version_mismatch) end end end endvagrant-2.0.2/plugins/provisioners/ansible/helpers.rb000066400000000000000000000006621323370221500230060ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Ansible class Helpers def self.expand_path_in_unix_style(path, base_dir) # Remove the possible drive letter, which is added # by `File.expand_path` when running on a Windows host File.expand_path(path, base_dir).sub(/^[a-zA-Z]:/, "") end def self.as_list_argument(v) v.kind_of?(Array) ? v.join(',') : v end end end endvagrant-2.0.2/plugins/provisioners/ansible/plugin.rb000066400000000000000000000043701323370221500226420ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Ansible class Plugin < Vagrant.plugin("2") name "ansible" description <<-DESC Provides support for provisioning your virtual machines with Ansible from the Vagrant host (`ansible`) or from the guests (`ansible_local`). DESC config("ansible", :provisioner) do require_relative "config/host" Config::Host end config("ansible_local", :provisioner) do require_relative "config/guest" Config::Guest end provisioner("ansible") do require_relative "provisioner/host" Provisioner::Host end provisioner("ansible_local") do require_relative "provisioner/guest" Provisioner::Guest end guest_capability(:linux, :ansible_installed) do require_relative "cap/guest/posix/ansible_installed" Cap::Guest::POSIX::AnsibleInstalled end guest_capability(:freebsd, :ansible_installed) do require_relative "cap/guest/posix/ansible_installed" Cap::Guest::POSIX::AnsibleInstalled end guest_capability(:arch, :ansible_install) do require_relative "cap/guest/arch/ansible_install" Cap::Guest::Arch::AnsibleInstall end guest_capability(:debian, :ansible_install) do require_relative "cap/guest/debian/ansible_install" Cap::Guest::Debian::AnsibleInstall end guest_capability(:ubuntu, :ansible_install) do require_relative "cap/guest/ubuntu/ansible_install" Cap::Guest::Ubuntu::AnsibleInstall end guest_capability(:fedora, :ansible_install) do require_relative "cap/guest/fedora/ansible_install" Cap::Guest::Fedora::AnsibleInstall end guest_capability(:redhat, :ansible_install) do require_relative "cap/guest/redhat/ansible_install" Cap::Guest::RedHat::AnsibleInstall end guest_capability(:suse, :ansible_install) do require_relative "cap/guest/suse/ansible_install" Cap::Guest::SUSE::AnsibleInstall end guest_capability(:freebsd, :ansible_install) do require_relative "cap/guest/freebsd/ansible_install" Cap::Guest::FreeBSD::AnsibleInstall end end end end vagrant-2.0.2/plugins/provisioners/ansible/provisioner/000077500000000000000000000000001323370221500233725ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/ansible/provisioner/base.rb000066400000000000000000000363301323370221500246360ustar00rootroot00000000000000require_relative "../constants" require_relative "../errors" require_relative "../helpers" module VagrantPlugins module Ansible module Provisioner # This class is a base class where the common functionality shared between # both Ansible provisioners are stored. # This is **not an actual provisioner**. # Instead, {Host} (ansible) or {Guest} (ansible_local) should be used. class Base < Vagrant.plugin("2", :provisioner) RANGE_PATTERN = %r{(?:\[[a-z]:[a-z]\]|\[[0-9]+?:[0-9]+?\])}.freeze ANSIBLE_PARAMETER_NAMES = { Ansible::COMPATIBILITY_MODE_V1_8 => { ansible_host: "ansible_ssh_host", ansible_password: "ansible_ssh_pass", ansible_port: "ansible_ssh_port", ansible_user: "ansible_ssh_user", ask_become_pass: "ask-sudo-pass", become: "sudo", become_user: "sudo-user", }, Ansible::COMPATIBILITY_MODE_V2_0 => { ansible_host: "ansible_host", ansible_password: "ansible_password", ansible_port: "ansible_port", ansible_user: "ansible_user", ask_become_pass: "ask-become-pass", become: "become", become_user: "become-user", } } protected def initialize(machine, config) super @control_machine = nil @command_arguments = [] @environment_variables = {} @inventory_machines = {} @inventory_path = nil @gathered_version_stdout = nil @gathered_version_major = nil @gathered_version = nil end def set_and_check_compatibility_mode begin set_gathered_ansible_version(gather_ansible_version) rescue StandardError => e # Nothing to do here, as the fallback on safe compatibility_mode is done below @logger.error("Error while gathering the ansible version: #{e.to_s}") end if @gathered_version_major if config.compatibility_mode == Ansible::COMPATIBILITY_MODE_AUTO detect_compatibility_mode elsif @gathered_version_major.to_i < 2 && config.compatibility_mode == Ansible::COMPATIBILITY_MODE_V2_0 # A better version comparator will be needed # when more compatibility modes come... but so far let's keep it simple! raise Ansible::Errors::AnsibleCompatibilityModeConflict, ansible_version: @gathered_version, system: @control_machine, compatibility_mode: config.compatibility_mode end end if config.compatibility_mode == Ansible::COMPATIBILITY_MODE_AUTO config.compatibility_mode = Ansible::SAFE_COMPATIBILITY_MODE @machine.env.ui.warn(I18n.t("vagrant.provisioners.ansible.compatibility_mode_not_detected", compatibility_mode: config.compatibility_mode, gathered_version: @gathered_version_stdout) + "\n") end unless Ansible::COMPATIBILITY_MODES.slice(1..-1).include?(config.compatibility_mode) raise Ansible::Errors::AnsibleProgrammingError, message: "The config.compatibility_mode must be correctly set at this stage!", details: "config.compatibility_mode: '#{config.compatibility_mode}'" end @lexicon = ANSIBLE_PARAMETER_NAMES[config.compatibility_mode] end def check_files_existence check_path_is_a_file(config.playbook, :playbook) check_path_exists(config.inventory_path, :inventory_path) if config.inventory_path check_path_is_a_file(config.config_file, :config_file) if config.config_file check_path_is_a_file(config.extra_vars[1..-1], :extra_vars) if has_an_extra_vars_file_argument check_path_is_a_file(config.galaxy_role_file, :galaxy_role_file) if config.galaxy_role_file check_path_is_a_file(config.vault_password_file, :vault_password_file) if config.vault_password_file end def get_environment_variables_for_shell_execution shell_env_vars = [] @environment_variables.each_pair do |k, v| if k =~ /ANSIBLE_SSH_ARGS|ANSIBLE_ROLES_PATH|ANSIBLE_CONFIG/ shell_env_vars << "#{k}='#{v}'" else shell_env_vars << "#{k}=#{v}" end end shell_env_vars end def ansible_galaxy_command_for_shell_execution command_values = { role_file: "'#{get_galaxy_role_file}'", roles_path: "'#{get_galaxy_roles_path}'" } shell_command = get_environment_variables_for_shell_execution shell_command << config.galaxy_command % command_values shell_command.flatten.join(' ') end def ansible_playbook_command_for_shell_execution shell_command = get_environment_variables_for_shell_execution shell_command << config.playbook_command shell_args = [] @command_arguments.each do |arg| if arg =~ /(--start-at-task|--limit)=(.+)/ shell_args << %Q(#{$1}="#{$2}") elsif arg =~ /(--extra-vars)=(.+)/ shell_args << %Q(%s=%s) % [$1, $2.shellescape] else shell_args << arg end end shell_command << shell_args # Add the raw arguments at the end, to give them the highest precedence shell_command << config.raw_arguments if config.raw_arguments shell_command << config.playbook shell_command.flatten.join(' ') end def prepare_common_command_arguments # By default we limit by the current machine, # but this can be overridden by the `limit` option. if config.limit @command_arguments << "--limit=#{Helpers::as_list_argument(config.limit)}" else @command_arguments << "--limit=#{@machine.name}" end @command_arguments << "--inventory-file=#{inventory_path}" @command_arguments << "--extra-vars=#{extra_vars_argument}" if config.extra_vars @command_arguments << "--#{@lexicon[:become]}" if config.become @command_arguments << "--#{@lexicon[:become_user]}=#{config.become_user}" if config.become_user @command_arguments << "#{verbosity_argument}" if verbosity_is_enabled? @command_arguments << "--vault-password-file=#{config.vault_password_file}" if config.vault_password_file @command_arguments << "--tags=#{Helpers::as_list_argument(config.tags)}" if config.tags @command_arguments << "--skip-tags=#{Helpers::as_list_argument(config.skip_tags)}" if config.skip_tags @command_arguments << "--start-at-task=#{config.start_at_task}" if config.start_at_task end def prepare_common_environment_variables # Ensure Ansible output isn't buffered so that we receive output # on a task-by-task basis. @environment_variables["PYTHONUNBUFFERED"] = 1 # When Ansible output is piped in Vagrant integration, its default colorization is # automatically disabled and the only way to re-enable colors is to use ANSIBLE_FORCE_COLOR. @environment_variables["ANSIBLE_FORCE_COLOR"] = "true" if @machine.env.ui.color? # Setting ANSIBLE_NOCOLOR is "unnecessary" at the moment, but this could change in the future # (e.g. local provisioner [GH-2103], possible change in vagrant/ansible integration, etc.) @environment_variables["ANSIBLE_NOCOLOR"] = "true" if !@machine.env.ui.color? # Use ANSIBLE_ROLES_PATH to tell ansible-playbook where to look for roles # (there is no equivalent command line argument in ansible-playbook) @environment_variables["ANSIBLE_ROLES_PATH"] = get_galaxy_roles_path if config.galaxy_roles_path prepare_ansible_config_environment_variable end def prepare_ansible_config_environment_variable @environment_variables["ANSIBLE_CONFIG"] = config.config_file if config.config_file end # Auto-generate "safe" inventory file based on Vagrantfile, # unless inventory_path is explicitly provided def inventory_path if config.inventory_path config.inventory_path else @inventory_path ||= generate_inventory end end def get_inventory_host_vars_string(machine_name) # In Ruby, Symbol and String values are different, but # Vagrant has to unify them for better user experience. vars = config.host_vars[machine_name.to_sym] if !vars vars = config.host_vars[machine_name.to_s] end s = nil if vars.is_a?(Hash) s = vars.each.collect { |k, v| if v.is_a?(String) && v.include?(' ') && !v.match(/^('|")[^'"]+('|")$/) v = %Q('#{v}') end "#{k}=#{v}" }.join(" ") elsif vars.is_a?(Array) s = vars.join(" ") elsif vars.is_a?(String) s = vars end if s and !s.empty? then s else nil end end def generate_inventory inventory = "# Generated by Vagrant\n\n" # This "abstract" step must fill the @inventory_machines list # and return the list of supported host(s) inventory += generate_inventory_machines inventory += generate_inventory_groups # This "abstract" step must create the inventory file and # return its location path # TODO: explain possible race conditions, etc. @inventory_path = ship_generated_inventory(inventory) end # Write out groups information. # All defined groups will be included, but only supported # machines and defined child groups will be included. def generate_inventory_groups groups_of_groups = {} defined_groups = [] group_vars = {} inventory_groups = "" # Verify if host range patterns exist and warn if config.groups.any? { |gm| gm.to_s[RANGE_PATTERN] } @machine.ui.warn(I18n.t("vagrant.provisioners.ansible.ansible_host_pattern_detected")) end config.groups.each_pair do |gname, gmembers| if gname.is_a?(Symbol) gname = gname.to_s end if gmembers.is_a?(String) gmembers = gmembers.split(/\s+/) elsif gmembers.is_a?(Hash) gmembers = gmembers.each.collect{ |k, v| "#{k}=#{v}" } elsif !gmembers.is_a?(Array) gmembers = [] end if gname.end_with?(":children") groups_of_groups[gname] = gmembers defined_groups << gname.sub(/:children$/, '') elsif gname.end_with?(":vars") group_vars[gname] = gmembers else defined_groups << gname inventory_groups += "\n[#{gname}]\n" gmembers.each do |gm| # TODO : Expand and validate host range patterns # against @inventory_machines list before adding them # otherwise abort with an error message if gm[RANGE_PATTERN] inventory_groups += "#{gm}\n" end inventory_groups += "#{gm}\n" if @inventory_machines.include?(gm.to_sym) end end end defined_groups.uniq! groups_of_groups.each_pair do |gname, gmembers| inventory_groups += "\n[#{gname}]\n" gmembers.each do |gm| inventory_groups += "#{gm}\n" if defined_groups.include?(gm) end end group_vars.each_pair do |gname, gmembers| if defined_groups.include?(gname.sub(/:vars$/, "")) || gname == "all:vars" inventory_groups += "\n[#{gname}]\n" + gmembers.join("\n") + "\n" end end return inventory_groups end def has_an_extra_vars_file_argument config.extra_vars && config.extra_vars.kind_of?(String) && config.extra_vars =~ /^@.+$/ end def extra_vars_argument if has_an_extra_vars_file_argument # A JSON or YAML file is referenced. config.extra_vars else # Expected to be a Hash after config validation. config.extra_vars.to_json end end def get_galaxy_role_file Helpers::expand_path_in_unix_style(config.galaxy_role_file, get_provisioning_working_directory) end def get_galaxy_roles_path base_dir = get_provisioning_working_directory if config.galaxy_roles_path Helpers::expand_path_in_unix_style(config.galaxy_roles_path, base_dir) else playbook_path = Helpers::expand_path_in_unix_style(config.playbook, base_dir) File.join(Pathname.new(playbook_path).parent, 'roles') end end def ui_running_ansible_command(name, command) @machine.ui.detail I18n.t("vagrant.provisioners.ansible.running_#{name}") if verbosity_is_enabled? # Show the ansible command in use @machine.env.ui.detail command end end def verbosity_is_enabled? config.verbose && !config.verbose.to_s.empty? end def verbosity_argument if config.verbose.to_s =~ /^-?(v+)$/ "-#{$+}" else # safe default, in case input strays '-v' end end private def detect_compatibility_mode if !@gathered_version_major || config.compatibility_mode != Ansible::COMPATIBILITY_MODE_AUTO raise Ansible::Errors::AnsibleProgrammingError, message: "The detect_compatibility_mode() function shouldn't have been called!", details: %Q(config.compatibility_mode: '#{config.compatibility_mode}' gathered version major number: '#{@gathered_version_major}' gathered version stdout version: #{@gathered_version_stdout}) end if @gathered_version_major.to_i <= 1 config.compatibility_mode = Ansible::COMPATIBILITY_MODE_V1_8 else config.compatibility_mode = Ansible::COMPATIBILITY_MODE_V2_0 end @machine.env.ui.warn(I18n.t("vagrant.provisioners.ansible.compatibility_mode_warning", compatibility_mode: config.compatibility_mode, ansible_version: @gathered_version) + "\n") end def set_gathered_ansible_version(stdout_output) @gathered_version_stdout = stdout_output if !@gathered_version_stdout.empty? first_line = @gathered_version_stdout.lines[0] ansible_version_pattern = first_line.match(/(^ansible\s+)(.+)$/) if ansible_version_pattern _, @gathered_version, _ = ansible_version_pattern.captures if @gathered_version @gathered_version_major = @gathered_version.match(/^(\d)\..+$/).captures[0].to_i end end end end end end end end vagrant-2.0.2/plugins/provisioners/ansible/provisioner/guest.rb000066400000000000000000000153311323370221500250510ustar00rootroot00000000000000require "tempfile" require_relative "base" module VagrantPlugins module Ansible module Provisioner class Guest < Base include Vagrant::Util def initialize(machine, config) super @control_machine = "guest" @logger = Log4r::Logger.new("vagrant::provisioners::ansible_guest") end def provision check_files_existence check_and_install_ansible execute_ansible_galaxy_on_guest if config.galaxy_role_file execute_ansible_playbook_on_guest end protected # # This handles verifying the Ansible installation, installing it if it was # requested, and so on. This method will raise exceptions if things are wrong. # The compatibility mode checks are also performed here in order to fetch the # Ansible version information only once. # # Current limitations: # - The installation of a specific Ansible version is only supported by # the "pip" install_mode. # - There is no absolute guarantee that the automated installation will replace # a previous Ansible installation (although it works fine in many cases) # def check_and_install_ansible @logger.info("Checking for Ansible installation...") # If the guest cannot check if Ansible is installed, # print a warning and try to continue without any installation attempt... if !@machine.guest.capability?(:ansible_installed) @machine.ui.warn(I18n.t("vagrant.provisioners.ansible.cannot_detect")) return end # Try to install Ansible (if needed and requested) if config.install && (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) end # This step will also fetch the Ansible version data into related instance variables set_and_check_compatibility_mode # Check if requested ansible version is available if (!config.version.empty? && config.version.to_s.to_sym != :latest && config.version != @gathered_version) raise Ansible::Errors::AnsibleVersionMismatch, system: @control_machine, required_version: config.version, current_version: @gathered_version end end def gather_ansible_version raw_output = "" result = @machine.communicate.execute( "ansible --version", error_class: Ansible::Errors::AnsibleNotFoundOnGuest, error_key: :ansible_not_found_on_guest) do |type, output| if type == :stdout && output.lines[0] raw_output = output.lines[0] end end if result != 0 raw_output = "" end raw_output end def get_provisioning_working_directory config.provisioning_path end def execute_ansible_galaxy_on_guest prepare_ansible_config_environment_variable execute_ansible_command_on_guest "galaxy", ansible_galaxy_command_for_shell_execution end def execute_ansible_playbook_on_guest prepare_common_command_arguments prepare_common_environment_variables execute_ansible_command_on_guest "playbook", ansible_playbook_command_for_shell_execution end def execute_ansible_command_on_guest(name, command) remote_command = "cd #{config.provisioning_path} && #{command}" ui_running_ansible_command name, remote_command result = execute_on_guest(remote_command) raise Ansible::Errors::AnsibleCommandFailed if result != 0 end def execute_on_guest(command) @machine.communicate.execute(command, error_check: false) do |type, data| if [:stderr, :stdout].include?(type) @machine.env.ui.info(data, new_line: false, prefix: false) end end end def ship_generated_inventory(inventory_content) inventory_basedir = File.join(config.tmp_path, "inventory") inventory_path = File.join(inventory_basedir, "vagrant_ansible_local_inventory") create_and_chown_remote_folder(inventory_basedir) @machine.communicate.sudo("rm -f #{inventory_path}", error_check: false) Tempfile.open("vagrant-ansible-local-inventory-#{@machine.name}") do |f| f.binmode f.write(inventory_content) f.fsync f.close @machine.communicate.upload(f.path, inventory_path) end return inventory_basedir end def generate_inventory_machines machines = "" # TODO: Instead, why not loop over active_machines and skip missing guests, like in Host? machine.env.machine_names.each do |machine_name| begin @inventory_machines[machine_name] = machine_name if @machine.name == machine_name machines += "#{machine_name} ansible_connection=local\n" else machines += "#{machine_name}\n" end host_vars = get_inventory_host_vars_string(machine_name) machines.sub!(/\n$/, " #{host_vars}\n") if host_vars end end return machines end def create_and_chown_remote_folder(path) @machine.communicate.tap do |comm| comm.sudo("mkdir -p #{path}") comm.sudo("chown -h #{@machine.ssh_info[:username]} #{path}") end end def check_path(path, test_args, option_name) # Checks for the existence of given file (or directory) on the guest system, # and error if it doesn't exist. remote_path = Helpers::expand_path_in_unix_style(path, config.provisioning_path) command = "test #{test_args} '#{remote_path}'" @machine.communicate.execute( command, error_class: Ansible::Errors::AnsibleError, error_key: :config_file_not_found, config_option: option_name, path: remote_path, system: @control_machine ) end def check_path_is_a_file(path, error_message_key) check_path(path, "-f", error_message_key) end def check_path_exists(path, error_message_key) check_path(path, "-e", error_message_key) end end end end end vagrant-2.0.2/plugins/provisioners/ansible/provisioner/host.rb000066400000000000000000000332721323370221500247030ustar00rootroot00000000000000require "thread" require_relative "base" module VagrantPlugins module Ansible module Provisioner class Host < Base @@lock = Mutex.new def initialize(machine, config) super @control_machine = "host" @logger = Log4r::Logger.new("vagrant::provisioners::ansible_host") end def provision # At this stage, the SSH access is guaranteed to be ready @ssh_info = @machine.ssh_info warn_for_unsupported_platform check_files_existence check_ansible_version_and_compatibility execute_ansible_galaxy_from_host if config.galaxy_role_file execute_ansible_playbook_from_host end protected VAGRANT_ARG_SEPARATOR = 'VAGRANT_ARG_SEP' def warn_for_unsupported_platform if Vagrant::Util::Platform.windows? @machine.env.ui.warn(I18n.t("vagrant.provisioners.ansible.windows_not_supported_for_control_machine") + "\n") end end def check_ansible_version_and_compatibility # This step will also fetch the Ansible version data into related instance variables set_and_check_compatibility_mode # Skip this check when not required, nor possible if !@gathered_version || config.version.empty? || config.version.to_s.to_sym == :latest return end if config.version != @gathered_version raise Ansible::Errors::AnsibleVersionMismatch, system: @control_machine, required_version: config.version, current_version: @gathered_version end end def prepare_command_arguments # Connect with native OpenSSH client # Other modes (e.g. paramiko) are not officially supported, # but can be enabled via raw_arguments option. @command_arguments << "--connection=ssh" # Increase the SSH connection timeout, as the Ansible default value (10 seconds) # is a bit demanding for some overloaded developer boxes. This is particularly # helpful when additional virtual networks are configured, as their availability # is not controlled during vagrant boot process. @command_arguments << "--timeout=30" if !config.force_remote_user # Pass the vagrant ssh username as Ansible default remote user, because # the ansible_ssh_user/ansible_user parameter won't be added to the auto-generated inventory. @command_arguments << "--user=#{@ssh_info[:username]}" elsif config.inventory_path # Using an extra variable is the only way to ensure that the Ansible remote user # is overridden (as the ansible inventory is not under vagrant control) @command_arguments << "--extra-vars=#{@lexicon[:ansible_user]}='#{@ssh_info[:username]}'" end @command_arguments << "--#{@lexicon[:ask_become_pass]}" if config.ask_become_pass @command_arguments << "--ask-vault-pass" if config.ask_vault_pass prepare_common_command_arguments end def prepare_environment_variables prepare_common_environment_variables # Some Ansible options must be passed as environment variables, # as there is no equivalent command line arguments @environment_variables["ANSIBLE_HOST_KEY_CHECKING"] = "#{config.host_key_checking}" # ANSIBLE_SSH_ARGS is required for Multiple SSH keys, SSH forwarding and custom SSH settings @environment_variables["ANSIBLE_SSH_ARGS"] = ansible_ssh_args unless ansible_ssh_args.empty? end def execute_command_from_host(command) begin result = Vagrant::Util::Subprocess.execute(*command) do |type, data| if type == :stdout || type == :stderr @machine.env.ui.detail(data, new_line: false, prefix: false) end end raise Ansible::Errors::AnsibleCommandFailed if result.exit_code != 0 rescue Vagrant::Errors::CommandUnavailable raise Ansible::Errors::AnsibleNotFoundOnHost end end def gather_ansible_version raw_output = "" command = %w(ansible --version) command << { notify: [:stdout, :stderr] } begin result = Vagrant::Util::Subprocess.execute(*command) do |type, output| if type == :stdout && output.lines[0] raw_output = output end end if result.exit_code != 0 raw_output = "" end rescue Vagrant::Errors::CommandUnavailable raise Ansible::Errors::AnsibleNotFoundOnHost end raw_output end def execute_ansible_galaxy_from_host prepare_ansible_config_environment_variable command_values = { role_file: get_galaxy_role_file, roles_path: get_galaxy_roles_path } command_template = config.galaxy_command.gsub(' ', VAGRANT_ARG_SEPARATOR) str_command = command_template % command_values command = str_command.split(VAGRANT_ARG_SEPARATOR) command << { env: @environment_variables, # Write stdout and stderr data, since it's the regular Ansible output notify: [:stdout, :stderr], workdir: @machine.env.root_path.to_s } ui_running_ansible_command "galaxy", ansible_galaxy_command_for_shell_execution execute_command_from_host command end def execute_ansible_playbook_from_host prepare_environment_variables prepare_command_arguments # Assemble the full ansible-playbook command command = [config.playbook_command] << @command_arguments # Add the raw arguments at the end, to give them the highest precedence command << config.raw_arguments if config.raw_arguments command << config.playbook command = command.flatten command << { env: @environment_variables, # Write stdout and stderr data, since it's the regular Ansible output notify: [:stdout, :stderr], workdir: @machine.env.root_path.to_s } ui_running_ansible_command "playbook", ansible_playbook_command_for_shell_execution execute_command_from_host command end def ship_generated_inventory(inventory_content) inventory_path = Pathname.new(File.join(@machine.env.local_data_path.join, %w(provisioners ansible inventory))) FileUtils.mkdir_p(inventory_path) unless File.directory?(inventory_path) inventory_file = Pathname.new(File.join(inventory_path, 'vagrant_ansible_inventory')) @@lock.synchronize do if !File.exists?(inventory_file) or inventory_content != File.read(inventory_file) begin # ansible dir inventory will ignore files starting with '.' inventory_tmpfile = Tempfile.new('.vagrant_ansible_inventory', inventory_path) inventory_tmpfile.write(inventory_content) inventory_tmpfile.close File.rename(inventory_tmpfile.path, inventory_file) ensure inventory_tmpfile.close inventory_tmpfile.unlink end end end return inventory_path end def generate_inventory_machines machines = "" @machine.env.active_machines.each do |am| begin m = @machine.env.machine(*am) # Call only once the SSH and WinRM info computation # Note that machines configured with WinRM communicator, also have a "partial" ssh_info. m_ssh_info = m.ssh_info host_vars = get_inventory_host_vars_string(m.name) if m.config.vm.communicator == :winrm m_winrm_net_info = CommunicatorWinRM::Helper.winrm_info(m) # can raise a WinRMNotReady exception... machines += get_inventory_winrm_machine(m, m_winrm_net_info) machines.sub!(/\n$/, " #{host_vars}\n") if host_vars @inventory_machines[m.name] = m elsif !m_ssh_info.nil? machines += get_inventory_ssh_machine(m, m_ssh_info) machines.sub!(/\n$/, " #{host_vars}\n") if host_vars @inventory_machines[m.name] = m else @logger.error("Auto-generated inventory: Impossible to get SSH information for machine '#{m.name} (#{m.provider_name})'. This machine should be recreated.") # Let a note about this missing machine machines += "# MISSING: '#{m.name}' machine was probably removed without using Vagrant. This machine should be recreated.\n" end rescue Vagrant::Errors::MachineNotFound, CommunicatorWinRM::Errors::WinRMNotReady => e @logger.info("Auto-generated inventory: Skip machine '#{am[0]} (#{am[1]})', which is not configured for this Vagrant environment.") end end return machines end def get_provisioning_working_directory machine.env.root_path end def get_inventory_ssh_machine(machine, ssh_info) forced_remote_user = "" if config.force_remote_user forced_remote_user = "#{@lexicon[:ansible_user]}='#{ssh_info[:username]}' " end "#{machine.name} #{@lexicon[:ansible_host]}=#{ssh_info[:host]} #{@lexicon[:ansible_port]}=#{ssh_info[:port]} #{forced_remote_user}ansible_ssh_private_key_file='#{ssh_info[:private_key_path][0]}'\n" end def get_inventory_winrm_machine(machine, winrm_net_info) forced_remote_user = "" if config.force_remote_user forced_remote_user = "#{@lexicon[:ansible_user]}='#{machine.config.winrm.username}' " end "#{machine.name} ansible_connection=winrm #{@lexicon[:ansible_host]}=#{winrm_net_info[:host]} #{@lexicon[:ansible_port]}=#{winrm_net_info[:port]} #{forced_remote_user}#{@lexicon[:ansible_password]}='#{machine.config.winrm.password}'\n" end def ansible_ssh_args @ansible_ssh_args ||= prepare_ansible_ssh_args end def prepare_ansible_ssh_args ssh_options = [] # Use an SSH ProxyCommand when using the Docker provider with the intermediate host if @machine.provider_name == :docker && machine.provider.host_vm? docker_host_ssh_info = machine.provider.host_vm.ssh_info proxy_cmd = "ssh #{docker_host_ssh_info[:username]}@#{docker_host_ssh_info[:host]}" + " -p #{docker_host_ssh_info[:port]} -i #{docker_host_ssh_info[:private_key_path][0]}" # Use same options than plugins/providers/docker/communicator.rb # Note: this could be improved (DRY'ed) by sharing these settings. proxy_cmd += " -o Compression=yes -o ConnectTimeout=5 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" proxy_cmd += " -o ForwardAgent=yes" if @ssh_info[:forward_agent] proxy_cmd += " exec nc %h %p 2>/dev/null" ssh_options << "-o ProxyCommand='#{ proxy_cmd }'" # TODO ssh_options << "-o ProxyCommand=\"#{ proxy_cmd }\"" end # Use an SSH ProxyCommand when corresponding Vagrant setting is defined if @machine.ssh_info[:proxy_command] proxy_cmd = @machine.ssh_info[:proxy_command] ssh_options << "-o ProxyCommand='#{ proxy_cmd }'" end # Don't access user's known_hosts file, except when host_key_checking is enabled. ssh_options << "-o UserKnownHostsFile=/dev/null" unless config.host_key_checking # Compare to lib/vagrant/util/ssh.rb ssh_options << "-o IdentitiesOnly=yes" if !Vagrant::Util::Platform.solaris? && @ssh_info[:keys_only] # Multiple Private Keys unless !config.inventory_path && @ssh_info[:private_key_path].size == 1 @ssh_info[:private_key_path].each do |key| ssh_options += ["-o", "IdentityFile=%s" % [key.gsub('%', '%%')]] end end # SSH Forwarding ssh_options << "-o ForwardAgent=yes" if @ssh_info[:forward_agent] # Unchecked SSH Parameters ssh_options.concat(config.raw_ssh_args) if config.raw_ssh_args # Re-enable ControlPersist Ansible defaults, # which are lost when ANSIBLE_SSH_ARGS is defined. unless ssh_options.empty? ssh_options << "-o ControlMaster=auto" ssh_options << "-o ControlPersist=60s" # Intentionally keep ControlPath undefined to let ansible-playbook # automatically sets this option to Ansible default value end ssh_options.join(' ') end def check_path(path, path_test_method, option_name) # Checks for the existence of given file (or directory) on the host system, # and error if it doesn't exist. expanded_path = Pathname.new(path).expand_path(@machine.env.root_path) if !expanded_path.public_send(path_test_method) raise Ansible::Errors::AnsibleError, _key: :config_file_not_found, config_option: option_name, path: expanded_path, system: @control_machine end end def check_path_is_a_file(path, option_name) check_path(path, "file?", option_name) end def check_path_exists(path, option_name) check_path(path, "exist?", option_name) end end end end end vagrant-2.0.2/plugins/provisioners/cfengine/000077500000000000000000000000001323370221500211545ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/cfengine/cap/000077500000000000000000000000001323370221500217175ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/cfengine/cap/debian/000077500000000000000000000000001323370221500231415ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/cfengine/cap/debian/cfengine_install.rb000066400000000000000000000013731323370221500267760ustar00rootroot00000000000000module VagrantPlugins module CFEngine module Cap module Debian module CFEngineInstall def self.cfengine_install(machine, config) machine.communicate.tap do |comm| comm.sudo("mkdir -p #{File.dirname(config.deb_repo_file)} && /bin/echo #{config.deb_repo_line} > #{config.deb_repo_file}") comm.sudo("GPGFILE=`tempfile`; wget -O $GPGFILE #{config.repo_gpg_key_url} && apt-key add $GPGFILE; rm -f $GPGFILE") comm.sudo("apt-get install -y --force-yes apt-transport-https"); comm.sudo("apt-get update") comm.sudo("apt-get install -y #{config.package_name}") end end end end end end end vagrant-2.0.2/plugins/provisioners/cfengine/cap/linux/000077500000000000000000000000001323370221500230565ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/cfengine/cap/linux/cfengine_installed.rb000066400000000000000000000005171323370221500272230ustar00rootroot00000000000000module VagrantPlugins module CFEngine module Cap module Linux module CFEngineInstalled def self.cfengine_installed(machine) machine.communicate.test( "test -d /var/cfengine && test -x /var/cfengine/bin/cf-agent", sudo: true) end end end end end end vagrant-2.0.2/plugins/provisioners/cfengine/cap/linux/cfengine_needs_bootstrap.rb000066400000000000000000000023131323370221500304330ustar00rootroot00000000000000require "log4r" module VagrantPlugins module CFEngine module Cap module Linux module CFEngineNeedsBootstrap def self.cfengine_needs_bootstrap(machine, config) logger = Log4r::Logger.new("vagrant::plugins::cfengine::cap_linux_cfengine_bootstrap") machine.communicate.tap do |comm| # We hardcode fixing the permissions on /var/cfengine/ppkeys/, if it exists, # because otherwise CFEngine will fail to bootstrap. if comm.test("test -d /var/cfengine/ppkeys", sudo: true) logger.debug("Fixing permissions on /var/cfengine/ppkeys") comm.sudo("chmod -R 600 /var/cfengine/ppkeys") end logger.debug("Checking if CFEngine is bootstrapped...") bootstrapped = comm.test("test -f /var/cfengine/policy_server.dat", sudo: true) if bootstrapped && !config.force_bootstrap logger.info("CFEngine already bootstrapped, no need to do it again") return false end logger.info("CFEngine needs bootstrap.") return true end end end end end end end vagrant-2.0.2/plugins/provisioners/cfengine/cap/redhat/000077500000000000000000000000001323370221500231665ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/cfengine/cap/redhat/cfengine_install.rb000066400000000000000000000024441323370221500270230ustar00rootroot00000000000000require "log4r" module VagrantPlugins module CFEngine module Cap module RedHat module CFEngineInstall def self.cfengine_install(machine, config) logger = Log4r::Logger.new("vagrant::plugins::cfengine::cap_redhat_cfengine_install") machine.communicate.tap do |comm| logger.info("Adding the CFEngine repository to #{config.yum_repo_file}") comm.sudo("mkdir -p #{File.dirname(config.yum_repo_file)} && (echo '[cfengine-repository]'; echo 'name=CFEngine Community Yum Repository'; echo 'baseurl=#{config.yum_repo_url}'; echo 'enabled=1'; echo 'gpgcheck=1') > #{config.yum_repo_file}") logger.info("Installing CFEngine Community Yum Repository GPG KEY from #{config.repo_gpg_key_url}") comm.sudo("GPGFILE=$(mktemp) && wget -O $GPGFILE #{config.repo_gpg_key_url} && rpm --import $GPGFILE; rm -f $GPGFILE") if dnf?(machine) comm.sudo("dnf -y install #{config.package_name}") else comm.sudo("yum -y install #{config.package_name}") end end end protected def self.dnf?(machine) machine.communicate.test("/usr/bin/which -s dnf") end end end end end end vagrant-2.0.2/plugins/provisioners/cfengine/cap/suse/000077500000000000000000000000001323370221500226765ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/cfengine/cap/suse/cfengine_install.rb000066400000000000000000000010161323370221500265250ustar00rootroot00000000000000module VagrantPlugins module CFEngine module Cap module SUSE module CFEngineInstall def self.cfengine_install(machine, config) machine.communicate.tap do |comm| comm.sudo("rpm --import #{config.repo_gpg_key_url}") comm.sudo("zypper addrepo -t YUM #{config.yum_repo_url} CFEngine") comm.sudo("zypper se #{config.package_name} && zypper -n install #{config.package_name}") end end end end end end end vagrant-2.0.2/plugins/provisioners/cfengine/config.rb000066400000000000000000000075061323370221500227560ustar00rootroot00000000000000require "pathname" require "vagrant" module VagrantPlugins module CFEngine class Config < Vagrant.plugin("2", :config) attr_accessor :am_policy_hub attr_accessor :extra_agent_args attr_accessor :classes attr_accessor :deb_repo_file attr_accessor :deb_repo_line attr_accessor :files_path attr_accessor :force_bootstrap attr_accessor :install attr_accessor :mode attr_accessor :policy_server_address attr_accessor :repo_gpg_key_url attr_accessor :run_file attr_accessor :upload_path attr_accessor :yum_repo_file attr_accessor :yum_repo_url attr_accessor :package_name def initialize @am_policy_hub = UNSET_VALUE @classes = UNSET_VALUE @deb_repo_file = UNSET_VALUE @deb_repo_line = UNSET_VALUE @extra_agent_args = UNSET_VALUE @files_path = UNSET_VALUE @force_bootstrap = UNSET_VALUE @install = UNSET_VALUE @mode = UNSET_VALUE @policy_server_address = UNSET_VALUE @repo_gpg_key_url = UNSET_VALUE @run_file = UNSET_VALUE @upload_path = UNSET_VALUE @yum_repo_file = UNSET_VALUE @yum_repo_url = UNSET_VALUE @package_name = UNSET_VALUE end def finalize! @am_policy_hub = false if @am_policy_hub == UNSET_VALUE @classes = nil if @classes == UNSET_VALUE if @deb_repo_file == UNSET_VALUE @deb_repo_file = "/etc/apt/sources.list.d/cfengine-community.list" end if @deb_repo_line == UNSET_VALUE @deb_repo_line = "deb https://cfengine.com/pub/apt/packages stable main" end @extra_agent_args = nil if @extra_agent_args == UNSET_VALUE @files_path = nil if @files_path == UNSET_VALUE @force_bootstrap = false if @force_bootstrap == UNSET_VALUE @install = true if @install == UNSET_VALUE @install = @install.to_sym if @install.respond_to?(:to_sym) @mode = :bootstrap if @mode == UNSET_VALUE @mode = @mode.to_sym @run_file = nil if @run_file == UNSET_VALUE @policy_server_address = nil if @policy_server_address == UNSET_VALUE if @repo_gpg_key_url == UNSET_VALUE @repo_gpg_key_url = "https://cfengine.com/pub/gpg.key" end @upload_path = "/tmp/vagrant-cfengine-file" if @upload_path == UNSET_VALUE if @yum_repo_file == UNSET_VALUE @yum_repo_file = "/etc/yum.repos.d/cfengine-community.repo" end if @yum_repo_url == UNSET_VALUE @yum_repo_url = "https://cfengine.com/pub/yum/$basearch" end if @package_name == UNSET_VALUE @package_name = "cfengine-community" end end def validate(machine) errors = _detected_errors valid_modes = [:bootstrap, :single_run] errors << I18n.t("vagrant.cfengine_config.invalid_mode") if !valid_modes.include?(@mode) if @mode == :bootstrap if !@policy_server_address && !@am_policy_hub errors << I18n.t("vagrant.cfengine_config.policy_server_address") end end if @classes && !@classes.is_a?(Array) errors << I18n.t("vagrant.cfengine_config.classes_array") end if @files_path expanded = Pathname.new(@files_path).expand_path(machine.env.root_path) if !expanded.directory? errors << I18n.t("vagrant.cfengine_config.files_path_not_directory") end end if @run_file expanded = Pathname.new(@run_file).expand_path(machine.env.root_path) if !expanded.file? errors << I18n.t("vagrant.cfengine_config.run_file_not_found") end end { "CFEngine" => errors } end end end end vagrant-2.0.2/plugins/provisioners/cfengine/plugin.rb000066400000000000000000000023331323370221500230000ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CFEngine class Plugin < Vagrant.plugin("2") name "CFEngine Provisioner" description <<-DESC Provisions machines with CFEngine. DESC config(:cfengine, :provisioner) do require_relative "config" Config end guest_capability("debian", "cfengine_install") do require_relative "cap/debian/cfengine_install" Cap::Debian::CFEngineInstall end guest_capability("linux", "cfengine_needs_bootstrap") do require_relative "cap/linux/cfengine_needs_bootstrap" Cap::Linux::CFEngineNeedsBootstrap end guest_capability("linux", "cfengine_installed") do require_relative "cap/linux/cfengine_installed" Cap::Linux::CFEngineInstalled end guest_capability("redhat", "cfengine_install") do require_relative "cap/redhat/cfengine_install" Cap::RedHat::CFEngineInstall end guest_capability("suse", "cfengine_install") do require_relative "cap/suse/cfengine_install" Cap::SUSE::CFEngineInstall end provisioner(:cfengine) do require_relative "provisioner" Provisioner end end end end vagrant-2.0.2/plugins/provisioners/cfengine/provisioner.rb000066400000000000000000000121651323370221500240650ustar00rootroot00000000000000require "log4r" require "vagrant" module VagrantPlugins module CFEngine class Provisioner < Vagrant.plugin("2", :provisioner) def provision if @machine.config.vm.communicator == :winrm raise Vagrant::Errors::ProvisionerWinRMUnsupported, name: "cfengine" end @logger = Log4r::Logger.new("vagrant::plugins::cfengine") @logger.info("Checking for CFEngine installation...") handle_cfengine_installation if @config.files_path @machine.ui.info(I18n.t("vagrant.cfengine_installing_files_path")) install_files(Pathname.new(@config.files_path).expand_path(@machine.env.root_path)) end handle_cfengine_bootstrap if @config.mode == :bootstrap if @config.mode == :single_run # Just let people know @machine.ui.info(I18n.t("vagrant.cfengine_single_run")) end if @config.run_file @machine.ui.info(I18n.t("vagrant.cfengine_single_run_execute")) path = Pathname.new(@config.run_file).expand_path(@machine.env.root_path) machine.communicate.upload(path.to_s, @config.upload_path) cfagent("-KI -f #{@config.upload_path}#{cfagent_classes_args}#{cfagent_extra_args}") end end protected # This runs cf-agent with the given arguments. def cfagent(args, options=nil) options ||= {} command = "/var/cfengine/bin/cf-agent #{args}" @machine.communicate.sudo(command, error_check: options[:error_check]) do |type, data| if [:stderr, :stdout].include?(type) # Output the data with the proper color based on the stream. color = type == :stdout ? :green : :red @machine.ui.info( data, color: color, new_line: false, prefix: false) end end end # Returns the arguments for the classes configuration if they are # set. # # @return [String] def cfagent_classes_args return "" if !@config.classes args = @config.classes.map { |c| "-D#{c}" }.join(" ") return " #{args}" end # Extra arguments for calles to cf-agent. # # @return [String] def cfagent_extra_args return "" if !@config.extra_agent_args return " #{@config.extra_agent_args}" end # This handles checking if the CFEngine installation needs to # be bootstrapped, and bootstraps if it does. def handle_cfengine_bootstrap @logger.info("Bootstrapping CFEngine...") if !@machine.guest.capability(:cfengine_needs_bootstrap, @config) @machine.ui.info(I18n.t("vagrant.cfengine_no_bootstrap")) return end # Needs bootstrap, let's determine the parameters policy_server_address = @config.policy_server_address if !policy_server_address policy_server_address = @machine.guest.capability(:read_ip_address) raise Vagrant::Errors::CFEngineCantAutodetectIP if !policy_server_address @machine.ui.info(I18n.t("vagrant.cfengine_detected_ip", address: policy_server_address)) end @machine.ui.info(I18n.t("vagrant.cfengine_bootstrapping", policy_server: policy_server_address)) result = cfagent("--bootstrap #{policy_server_address}", error_check: false) raise Vagrant::Errors::CFEngineBootstrapFailed if result != 0 # Policy hubs need to do additional things before they're ready # to accept agents. Force that run now... if @config.am_policy_hub @machine.ui.info(I18n.t("vagrant.cfengine_bootstrapping_policy_hub")) cfagent("-KI -f /var/cfengine/masterfiles/failsafe.cf#{cfagent_classes_args}") cfagent("-KI #{cfagent_classes_args}#{cfagent_extra_args}") end end # This handles verifying the CFEngine installation, installing it # if it was requested, and so on. This method will raise exceptions # if things are wrong. def handle_cfengine_installation if !@machine.guest.capability?(:cfengine_installed) @machine.ui.warn(I18n.t("vagrant.cfengine_cant_detect")) return end installed = @machine.guest.capability(:cfengine_installed) if !installed || @config.install == :force raise Vagrant::Errors::CFEngineNotInstalled if !@config.install @machine.ui.info(I18n.t("vagrant.cfengine_installing")) @machine.guest.capability(:cfengine_install, @config) if !@machine.guest.capability(:cfengine_installed) raise Vagrant::Errors::CFEngineInstallFailed end end end # This installs a set of files into the CFEngine folder within # the machine. # # @param [Pathname] local_path def install_files(local_path) @logger.debug("Copying local files to CFEngine: #{local_path}") @machine.communicate.sudo("rm -rf /tmp/cfengine-files") @machine.communicate.upload(local_path.to_s, "/tmp/cfengine-files") @machine.communicate.sudo("cp -R /tmp/cfengine-files/* /var/cfengine") end end end end vagrant-2.0.2/plugins/provisioners/chef/000077500000000000000000000000001323370221500203035ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/chef/cap/000077500000000000000000000000001323370221500210465ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/chef/cap/debian/000077500000000000000000000000001323370221500222705ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/chef/cap/debian/chef_install.rb000066400000000000000000000010441323370221500252470ustar00rootroot00000000000000require_relative "../../omnibus" module VagrantPlugins module Chef module Cap module Debian module ChefInstall def self.chef_install(machine, project, version, channel, omnibus_url, options = {}) machine.communicate.sudo("apt-get update -y -qq") machine.communicate.sudo("apt-get install -y -qq curl") command = Omnibus.sh_command(project, version, channel, omnibus_url, options) machine.communicate.sudo(command) end end end end end end vagrant-2.0.2/plugins/provisioners/chef/cap/freebsd/000077500000000000000000000000001323370221500224605ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/chef/cap/freebsd/chef_install.rb000066400000000000000000000007501323370221500254420ustar00rootroot00000000000000require_relative "../../omnibus" module VagrantPlugins module Chef module Cap module FreeBSD module ChefInstall def self.chef_install(machine, project, version, channel, omnibus_url, options = {}) machine.communicate.sudo("pkg install -y -qq curl bash") command = Omnibus.sh_command(project, version, channel, omnibus_url, options) machine.communicate.sudo(command) end end end end end end vagrant-2.0.2/plugins/provisioners/chef/cap/freebsd/chef_installed.rb000066400000000000000000000011111323370221500257430ustar00rootroot00000000000000module VagrantPlugins module Chef module Cap module FreeBSD module ChefInstalled # Check if Chef is installed at the given version. # @return [true, false] def self.chef_installed(machine, product, version) knife = "/opt/#{product}/bin/knife" command = "test -x #{knife}" if version != :latest command << "&& #{knife} --version | grep 'Chef: #{version}'" end machine.communicate.test(command, sudo: true) end end end end end end vagrant-2.0.2/plugins/provisioners/chef/cap/linux/000077500000000000000000000000001323370221500222055ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/chef/cap/linux/chef_installed.rb000066400000000000000000000011071323370221500254750ustar00rootroot00000000000000module VagrantPlugins module Chef module Cap module Linux module ChefInstalled # Check if Chef is installed at the given version. # @return [true, false] def self.chef_installed(machine, product, version) knife = "/opt/#{product}/bin/knife" command = "test -x #{knife}" if version != :latest command << "&& #{knife} --version | grep 'Chef: #{version}'" end machine.communicate.test(command, sudo: true) end end end end end end vagrant-2.0.2/plugins/provisioners/chef/cap/omnios/000077500000000000000000000000001323370221500223525ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/chef/cap/omnios/chef_install.rb000066400000000000000000000011511323370221500253300ustar00rootroot00000000000000require_relative "../../omnibus" module VagrantPlugins module Chef module Cap module OmniOS module ChefInstall def self.chef_install(machine, project, version, channel, omnibus_url, options = {}) su = machine.config.solaris.suexec_cmd machine.communicate.execute("#{su} pkg list --no-refresh web/curl > /dev/null 2>&1 || pkg install -q --accept web/curl") command = Omnibus.sh_command(project, version, channel, omnibus_url, options) machine.communicate.execute("#{su} #{command}") end end end end end end vagrant-2.0.2/plugins/provisioners/chef/cap/omnios/chef_installed.rb000066400000000000000000000012501323370221500256410ustar00rootroot00000000000000module VagrantPlugins module Chef module Cap module OmniOS module ChefInstalled # TODO: this is the same code as cap/linux/chef_installed, consider merging # Check if Chef is installed at the given version. # @return [true, false] def self.chef_installed(machine, product, version) knife = "/opt/#{product}/bin/knife" command = "test -x #{knife}" if version != :latest command << "&& #{knife} --version | grep 'Chef: #{version}'" end machine.communicate.test(command, sudo: true) end end end end end end vagrant-2.0.2/plugins/provisioners/chef/cap/redhat/000077500000000000000000000000001323370221500223155ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/chef/cap/redhat/chef_install.rb000066400000000000000000000012031323370221500252710ustar00rootroot00000000000000require_relative "../../omnibus" module VagrantPlugins module Chef module Cap module Redhat module ChefInstall def self.chef_install(machine, project, version, channel, omnibus_url, options = {}) machine.communicate.sudo <<-EOH.gsub(/^ {14}/, '') if command -v dnf; then dnf -y install curl else yum -y install curl fi EOH command = Omnibus.sh_command(project, version, channel, omnibus_url, options) machine.communicate.sudo(command) end end end end end end vagrant-2.0.2/plugins/provisioners/chef/cap/suse/000077500000000000000000000000001323370221500220255ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/chef/cap/suse/chef_install.rb000066400000000000000000000013311323370221500250030ustar00rootroot00000000000000require_relative "../../omnibus" module VagrantPlugins module Chef module Cap module Suse module ChefInstall def self.chef_install(machine, project, version, channel, omnibus_url, options = {}) unless curl?(machine) machine.communicate.sudo("zypper -n -q update") machine.communicate.sudo("zypper -n -q install curl") end command = Omnibus.sh_command(project, version, channel, omnibus_url, options) machine.communicate.sudo(command) end protected def self.curl?(machine) machine.communicate.test("/usr/bin/which -s curl") end end end end end end vagrant-2.0.2/plugins/provisioners/chef/cap/windows/000077500000000000000000000000001323370221500225405ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/chef/cap/windows/chef_install.rb000066400000000000000000000006421323370221500255220ustar00rootroot00000000000000require_relative "../../omnibus" module VagrantPlugins module Chef module Cap module Windows module ChefInstall def self.chef_install(machine, project, version, channel, omnibus_url, options = {}) command = Omnibus.ps_command(project, version, channel, omnibus_url, options) machine.communicate.sudo(command) end end end end end end vagrant-2.0.2/plugins/provisioners/chef/cap/windows/chef_installed.rb000066400000000000000000000012011323370221500260230ustar00rootroot00000000000000module VagrantPlugins module Chef module Cap module Windows module ChefInstalled # Check if Chef is installed at the given version. # @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 }' else command = 'if ((&knife --version) -Match "Chef: *"){ exit 0 } else { exit 1 }' end machine.communicate.test(command, sudo: true) end end end end end end vagrant-2.0.2/plugins/provisioners/chef/command_builder.rb000066400000000000000000000042001323370221500237500ustar00rootroot00000000000000module VagrantPlugins module Chef class CommandBuilder def self.command(type, config, options = {}) new(type, config, options).command end attr_reader :type attr_reader :config attr_reader :options def initialize(type, config, options = {}) @type = type @config = config @options = options.dup if type != :client && type != :solo raise "Unknown type `#{type.inspect}'!" end end def command if config.binary_env "#{config.binary_env} #{binary_path} #{args}" else "#{binary_path} #{args}" end end protected def binary_path binary_path = "chef-#{type}" if config.binary_path binary_path = File.join(config.binary_path, binary_path) if windows? binary_path = windows_friendly_path(binary_path) end end binary_path end def args args = "" args << " --config #{provisioning_path("#{type}.rb")}" args << " --json-attributes #{provisioning_path("dna.json")}" args << " --local-mode" if options[:local_mode] args << " --legacy-mode" if options[:legacy_mode] args << " --log_level #{config.log_level}" if config.log_level args << " --no-color" if !options[:colored] if config.install && (config.version == :latest || config.version.to_s >= "11.0") args << " --force-formatter" end args << " #{config.arguments.strip}" if config.arguments args.strip end def provisioning_path(file) if windows? path = config.provisioning_path || "C:/vagrant-chef" return windows_friendly_path(File.join(path, file)) else path = config.provisioning_path || "/tmp/vagrant-chef" return File.join(path, file) end end def windows_friendly_path(path) path = path.gsub("/", "\\") path = "c:#{path}" if path.start_with?("\\") return path end def windows? !!options[:windows] end end end end vagrant-2.0.2/plugins/provisioners/chef/config/000077500000000000000000000000001323370221500215505ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/chef/config/base.rb000066400000000000000000000117501323370221500230130ustar00rootroot00000000000000require "vagrant/util/presence" module VagrantPlugins module Chef module Config class Base < Vagrant.plugin("2", :config) include Vagrant::Util::Presence # The path to Chef's bin/ directory. # @return [String] attr_accessor :binary_path # Arbitrary environment variables to set before running the Chef # provisioner command. # @return [String] attr_accessor :binary_env # The name of the Chef project to install. This is "chef" for the Chef # Client or "chefdk" for the Chef Development Kit. Other product names # may be available as well. # @return [String] attr_accessor :product # Install Chef on the system if it does not exist. Default is true. # This is a trinary attribute (it can have three values): # # - true (bool) install Chef # - false (bool) do not install Chef # - "force" (string) install Chef, even if it is already installed at # the proper version # # @return [true, false, String] attr_accessor :install # The Chef log level. See the Chef docs for acceptable values. # @return [String, Symbol] attr_accessor :log_level # The channel from which to download Chef. Currently known values are # "current" and "stable", but more may be added in the future. The # default is "stable". # @return [String] attr_accessor :channel # The version of Chef to install. If Chef is already installed on the # system, the installed version is compared with the requested version. # If they match, no action is taken. If they do not match, version of # the value specified in this attribute will be installed over top of # the existing version (a warning will be displayed). # # You can also specify "latest" (default), which will install the latest # version of Chef on the system. In this case, Chef will use whatever # version is on the system. To force the newest version of Chef to be # installed on every provision, set the {#install} option to "force". # # @return [String] attr_accessor :version # Location of Omnibus installation scripts. # This URL specifies the location of install.sh/install.ps1 for # Linux/Unix and Windows respectively. # # It defaults to https://omnitruck.chef.io/. The full URL is then: # - Linux/Unix: https://omnitruck.chef.io/install.sh # - Windows: https://omnitruck.chef.io/install.ps1 # # If you want to have https://example.com/install.sh as Omnibus script # for your Linux/Unix installations, you should set this option to # https://example.com # # @return [String] attr_accessor :omnibus_url # The path where the Chef installer will be downloaded to. Only valid if # install is true or "force". It defaults to nil, which means that the # omnibus installer will choose the destination and you have no control # over it. # # @return [String] attr_accessor :installer_download_path def initialize super @binary_path = UNSET_VALUE @binary_env = UNSET_VALUE @product = UNSET_VALUE @install = UNSET_VALUE @log_level = UNSET_VALUE @channel = UNSET_VALUE @version = UNSET_VALUE @omnibus_url = UNSET_VALUE @installer_download_path = UNSET_VALUE end def finalize! @binary_path = nil if @binary_path == UNSET_VALUE @binary_env = nil if @binary_env == UNSET_VALUE @product = "chef" if @product == UNSET_VALUE @install = true if @install == UNSET_VALUE @log_level = :info if @log_level == UNSET_VALUE @channel = "stable" if @channel == UNSET_VALUE @version = :latest if @version == UNSET_VALUE @omnibus_url = 'https://omnitruck.chef.io' if @omnibus_url == UNSET_VALUE @installer_download_path = nil if @installer_download_path == UNSET_VALUE # Make sure the install is a symbol if it's not a boolean if @install.respond_to?(:to_sym) @install = @install.to_sym end # Make sure the version is a symbol if it's not a boolean if @version.respond_to?(:to_sym) @version = @version.to_sym end # Make sure the log level is a symbol @log_level = @log_level.to_sym end # Like validate, but returns a list of errors to append. # # @return [Array] def validate_base(machine) errors = _detected_errors if !present?(log_level) errors << I18n.t("vagrant.provisioners.chef.log_level_empty") end errors end end end end end vagrant-2.0.2/plugins/provisioners/chef/config/base_runner.rb000066400000000000000000000112151323370221500244000ustar00rootroot00000000000000require "vagrant/util/counter" require_relative "base" module VagrantPlugins module Chef module Config # This is the config base for Chef provisioners that need a full Chef # Runner object, like chef-solo or chef-client. For provisioners like # chef-apply, these options are not valid class BaseRunner < Base attr_accessor :arguments attr_accessor :attempts attr_accessor :custom_config_path attr_accessor :encrypted_data_bag_secret_key_path attr_accessor :environment attr_accessor :formatter attr_accessor :http_proxy attr_accessor :http_proxy_user attr_accessor :http_proxy_pass attr_accessor :https_proxy attr_accessor :https_proxy_user attr_accessor :https_proxy_pass attr_accessor :json attr_accessor :no_proxy attr_accessor :node_name attr_accessor :provisioning_path attr_accessor :run_list attr_accessor :file_cache_path attr_accessor :file_backup_path attr_accessor :verbose_logging attr_accessor :enable_reporting def initialize super @arguments = UNSET_VALUE @attempts = UNSET_VALUE @custom_config_path = UNSET_VALUE # /etc/chef/client.rb config options @encrypted_data_bag_secret_key_path = UNSET_VALUE @environment = UNSET_VALUE @formatter = UNSET_VALUE @http_proxy = UNSET_VALUE @http_proxy_user = UNSET_VALUE @http_proxy_pass = UNSET_VALUE @https_proxy = UNSET_VALUE @https_proxy_user = UNSET_VALUE @https_proxy_pass = UNSET_VALUE @no_proxy = UNSET_VALUE @node_name = UNSET_VALUE @provisioning_path = UNSET_VALUE @file_cache_path = UNSET_VALUE @file_backup_path = UNSET_VALUE @verbose_logging = UNSET_VALUE @enable_reporting = UNSET_VALUE # Runner options @json = {} @run_list = [] end def finalize! super @arguments = nil if @arguments == UNSET_VALUE @attempts = 1 if @attempts == UNSET_VALUE @custom_config_path = nil if @custom_config_path == UNSET_VALUE @environment = nil if @environment == UNSET_VALUE @formatter = nil if @formatter == UNSET_VALUE @http_proxy = nil if @http_proxy == UNSET_VALUE @http_proxy_user = nil if @http_proxy_user == UNSET_VALUE @http_proxy_pass = nil if @http_proxy_pass == UNSET_VALUE @https_proxy = nil if @https_proxy == UNSET_VALUE @https_proxy_user = nil if @https_proxy_user == UNSET_VALUE @https_proxy_pass = nil if @https_proxy_pass == UNSET_VALUE @no_proxy = nil if @no_proxy == UNSET_VALUE @node_name = nil if @node_name == UNSET_VALUE @provisioning_path = nil if @provisioning_path == UNSET_VALUE @file_backup_path = nil if @file_backup_path == UNSET_VALUE @file_cache_path = nil if @file_cache_path == UNSET_VALUE @verbose_logging = false if @verbose_logging == UNSET_VALUE @enable_reporting = true if @enable_reporting == UNSET_VALUE if @encrypted_data_bag_secret_key_path == UNSET_VALUE @encrypted_data_bag_secret_key_path = nil end end def merge(other) super.tap do |result| result.instance_variable_set(:@json, @json.merge(other.json)) result.instance_variable_set(:@run_list, (@run_list + other.run_list)) end end # Just like the normal configuration "validate" method except that # it returns an array of errors that should be merged into some # other error accumulator. def validate_base(machine) errors = super if @custom_config_path expanded = File.expand_path(@custom_config_path, machine.env.root_path) if !File.file?(expanded) errors << I18n.t("vagrant.config.chef.custom_config_path_missing") end end errors end # Adds a recipe to the run list def add_recipe(name) name = "recipe[#{name}]" unless name =~ /^recipe\[(.+?)\]$/ run_list << name end # Adds a role to the run list def add_role(name) name = "role[#{name}]" unless name =~ /^role\[(.+?)\]$/ run_list << name end end end end end vagrant-2.0.2/plugins/provisioners/chef/config/chef_apply.rb000066400000000000000000000023011323370221500242030ustar00rootroot00000000000000require "vagrant/util/presence" require_relative "base" module VagrantPlugins module Chef module Config class ChefApply < Base include Vagrant::Util::Presence # The raw recipe text (as a string) to execute via chef-apply. # @return [String] attr_accessor :recipe # The path (on the guest) where the uploaded apply recipe should be # written (/tmp/vagrant-chef-apply-#.rb). # @return [String] attr_accessor :upload_path def initialize super @recipe = UNSET_VALUE @upload_path = UNSET_VALUE end def finalize! super @recipe = nil if @recipe == UNSET_VALUE @upload_path = "/tmp/vagrant-chef-apply" if @upload_path == UNSET_VALUE end def validate(machine) errors = validate_base(machine) if !present?(recipe) errors << I18n.t("vagrant.provisioners.chef.recipe_empty") end if !present?(upload_path) errors << I18n.t("vagrant.provisioners.chef.upload_path_empty") end { "chef apply provisioner" => errors } end end end end end vagrant-2.0.2/plugins/provisioners/chef/config/chef_client.rb000066400000000000000000000043301323370221500243400ustar00rootroot00000000000000require "vagrant/util/presence" require "vagrant/util/which" require_relative "base_runner" module VagrantPlugins module Chef module Config class ChefClient < BaseRunner include Vagrant::Util::Presence # The URL endpoint to the Chef Server. # @return [String] attr_accessor :chef_server_url # The path on disk to the Chef client key, # @return [String] attr_accessor :client_key_path # Delete the client key when the VM is destroyed. Default is false. # @return [true, false] attr_accessor :delete_client # Delete the node when the VM is destroyed. Default is false. # @return [true, false] attr_accessor :delete_node # The path to the validation key on disk. # @return [String] attr_accessor :validation_key_path # The name of the validation client. # @return [String] attr_accessor :validation_client_name def initialize super @chef_server_url = UNSET_VALUE @client_key_path = UNSET_VALUE @delete_client = UNSET_VALUE @delete_node = UNSET_VALUE @validation_key_path = UNSET_VALUE @validation_client_name = UNSET_VALUE end def finalize! super @chef_server_url = nil if @chef_server_url == UNSET_VALUE @client_key_path = nil if @client_key_path == UNSET_VALUE @delete_client = false if @delete_client == UNSET_VALUE @delete_node = false if @delete_node == UNSET_VALUE @validation_client_name = "chef-validator" if @validation_client_name == UNSET_VALUE @validation_key_path = nil if @validation_key_path == UNSET_VALUE end def validate(machine) errors = validate_base(machine) if !present?(chef_server_url) errors << I18n.t("vagrant.config.chef.server_url_empty") end if !present?(validation_key_path) errors << I18n.t("vagrant.config.chef.validation_key_path") end { "chef client provisioner" => errors } end end end end end vagrant-2.0.2/plugins/provisioners/chef/config/chef_solo.rb000066400000000000000000000105261323370221500240420ustar00rootroot00000000000000require "vagrant/util/presence" require_relative "base_runner" module VagrantPlugins module Chef module Config class ChefSolo < BaseRunner include Vagrant::Util::Presence # The path on disk where Chef cookbooks are stored. # Default is "cookbooks". # @return [String] attr_accessor :cookbooks_path # The path where data bags are stored on disk. # @return [String] attr_accessor :data_bags_path # The path where environments are stored on disk. # @return [String] attr_accessor :environments_path # The path where nodes are stored on disk. # @return [String] attr_accessor :nodes_path # A URL download a remote recipe from. Note: you should use chef-apply # instead. # # @deprecated # # @return [String] attr_accessor :recipe_url # Enable chef-solo legacy mode rather than local mode. # @return [true, false] attr_accessor :legacy_mode # The path where roles are stored on disk. # @return [String] attr_accessor :roles_path # The type of synced folders to use. # @return [String] attr_accessor :synced_folder_type def initialize super @cookbooks_path = UNSET_VALUE @data_bags_path = UNSET_VALUE @environments_path = UNSET_VALUE @nodes_path = UNSET_VALUE @recipe_url = UNSET_VALUE @legacy_mode = UNSET_VALUE @roles_path = UNSET_VALUE @synced_folder_type = UNSET_VALUE end #------------------------------------------------------------ # Internal methods #------------------------------------------------------------ def finalize! super @recipe_url = nil if @recipe_url == UNSET_VALUE @synced_folder_type = nil if @synced_folder_type == UNSET_VALUE @legacy_mode = false if @legacy_mode == UNSET_VALUE if @cookbooks_path == UNSET_VALUE @cookbooks_path = [] @cookbooks_path << [:host, "cookbooks"] if !@recipe_url @cookbooks_path << [:vm, "cookbooks"] end @data_bags_path = [] if @data_bags_path == UNSET_VALUE @nodes_path = [] if @nodes_path == UNSET_VALUE @roles_path = [] if @roles_path == UNSET_VALUE @environments_path = [] if @environments_path == UNSET_VALUE @environments_path = [@environments_path].flatten # Make sure the path is an array. @cookbooks_path = prepare_folders_config(@cookbooks_path) @data_bags_path = prepare_folders_config(@data_bags_path) @nodes_path = prepare_folders_config(@nodes_path) @roles_path = prepare_folders_config(@roles_path) @environments_path = prepare_folders_config(@environments_path) end def validate(machine) errors = validate_base(machine) if !present?(Array(cookbooks_path)) errors << I18n.t("vagrant.config.chef.cookbooks_path_empty") end if environment && !present?(environments_path) errors << I18n.t("vagrant.config.chef.environment_path_required") end environments_path.each do |type, raw_path| next if type != :host path = Pathname.new(raw_path).expand_path(machine.env.root_path) if !path.directory? errors << I18n.t("vagrant.config.chef.environment_path_missing", path: raw_path.to_s ) end end { "chef solo provisioner" => errors } end protected # This takes any of the configurations that take a path or # array of paths and turns it into the proper format. # # @return [Array] def prepare_folders_config(config) # Make sure the path is an array config = [config] if !config.is_a?(Array) || config.first.is_a?(Symbol) return [] if config.flatten.compact.empty? # Make sure all the paths are in the proper format config.map do |path| path = [:host, path] if !path.is_a?(Array) path end end end end end end vagrant-2.0.2/plugins/provisioners/chef/config/chef_zero.rb000066400000000000000000000102161323370221500240410ustar00rootroot00000000000000require "vagrant/util/presence" require_relative "chef_solo" module VagrantPlugins module Chef module Config class ChefZero < BaseRunner include Vagrant::Util::Presence # The path on disk where Chef cookbooks are stored. # Default is "cookbooks". # @return [String] attr_accessor :cookbooks_path # The path where data bags are stored on disk. # @return [String] attr_accessor :data_bags_path # The path where environments are stored on disk. # @return [String] attr_accessor :environments_path # The path where nodes are stored on disk. # @return [String] attr_accessor :nodes_path # The path where roles are stored on disk. # @return [String] attr_accessor :roles_path # The type of synced folders to use. # @return [String] attr_accessor :synced_folder_type def initialize super @cookbooks_path = UNSET_VALUE @data_bags_path = UNSET_VALUE @environments_path = UNSET_VALUE @nodes_path = UNSET_VALUE @roles_path = UNSET_VALUE @synced_folder_type = UNSET_VALUE end def finalize! super @synced_folder_type = nil if @synced_folder_type == UNSET_VALUE if @cookbooks_path == UNSET_VALUE @cookbooks_path = [] @cookbooks_path << [:host, "cookbooks"] if !@recipe_url @cookbooks_path << [:vm, "cookbooks"] end @data_bags_path = [] if @data_bags_path == UNSET_VALUE @nodes_path = [] if @nodes_path == UNSET_VALUE @roles_path = [] if @roles_path == UNSET_VALUE @environments_path = [] if @environments_path == UNSET_VALUE @environments_path = [@environments_path].flatten # Make sure the path is an array. @cookbooks_path = prepare_folders_config(@cookbooks_path) @data_bags_path = prepare_folders_config(@data_bags_path) @nodes_path = prepare_folders_config(@nodes_path) @roles_path = prepare_folders_config(@roles_path) @environments_path = prepare_folders_config(@environments_path) end def validate(machine) errors = validate_base(machine) if !present?(Array(cookbooks_path)) errors << I18n.t("vagrant.config.chef.cookbooks_path_empty") end if !present?(Array(nodes_path)) errors << I18n.t("vagrant.config.chef.nodes_path_empty") else missing_paths = Array.new nodes_path.each { |dir| missing_paths << dir[1] if !File.exists? dir[1] } # If it exists at least one path on disk it's ok for Chef provisioning if missing_paths.size == nodes_path.size errors << I18n.t("vagrant.config.chef.nodes_path_missing", path: missing_paths.to_s) end end if environment && environments_path.empty? errors << I18n.t("vagrant.config.chef.environment_path_required") end environments_path.each do |type, raw_path| next if type != :host path = Pathname.new(raw_path).expand_path(machine.env.root_path) if !path.directory? errors << I18n.t("vagrant.config.chef.environment_path_missing", path: raw_path.to_s ) end end { "chef zero provisioner" => errors } end protected # This takes any of the configurations that take a path or # array of paths and turns it into the proper format. # # @return [Array] def prepare_folders_config(config) # Make sure the path is an array config = [config] if !config.is_a?(Array) || config.first.is_a?(Symbol) return [] if config.flatten.compact.empty? # Make sure all the paths are in the proper format config.map do |path| path = [:host, path] if !path.is_a?(Array) path end end end end end end vagrant-2.0.2/plugins/provisioners/chef/installer.rb000066400000000000000000000032561323370221500226330ustar00rootroot00000000000000module VagrantPlugins module Chef class Installer def initialize(machine, options = {}) @machine = machine @product = options.fetch(:product) @channel = options.fetch(:channel) @version = options.fetch(:version) @force = options.fetch(:force) @omnibus_url = options.fetch(:omnibus_url) @options = options.dup end # This handles verifying the Chef installation, installing it if it was # requested, and so on. This method will raise exceptions if things are # wrong. def ensure_installed # If the guest cannot check if Chef is installed, just exit printing a # warning... if !@machine.guest.capability?(:chef_installed) @machine.ui.warn(I18n.t("vagrant.chef_cant_detect")) return end if !should_install_chef? @machine.ui.info(I18n.t("vagrant.chef_already_installed", version: @version.to_s)) return end @machine.ui.detail(I18n.t("vagrant.chef_installing", version: @version.to_s)) @machine.guest.capability(:chef_install, @product, @version, @channel, @omnibus_url, @options) if !@machine.guest.capability(:chef_installed, @product, @version) raise Provisioner::Base::ChefError, :install_failed end end # Determine if Chef should be installed. Chef is installed if the "force" # option is given or if the guest does not have Chef installed at the # proper version. def should_install_chef? @force || !@machine.guest.capability(:chef_installed, @product, @version) end end end end vagrant-2.0.2/plugins/provisioners/chef/omnibus.rb000066400000000000000000000017621323370221500223120ustar00rootroot00000000000000module VagrantPlugins module Chef # Read more about the Omnibus installer here: # # https://docs.chef.io/install_omnibus.html # module Omnibus def sh_command(project, version, channel, omnibus_url, options = {}) command = "curl -sL #{omnibus_url}/install.sh | bash" command << " -s -- -P \"#{project}\" -c \"#{channel}\"" if version != :latest command << " -v \"#{version}\"" end if options[:download_path] command << " -d \"#{options[:download_path]}\"" end command end module_function :sh_command def ps_command(project, version, channel, omnibus_url, options = {}) command = ". { iwr -useb #{omnibus_url}/install.ps1 } | iex; install" command << " -project '#{project}' -channel '#{channel}'" if version != :latest command << " -version '#{version}'" end command end module_function :ps_command end end end vagrant-2.0.2/plugins/provisioners/chef/plugin.rb000066400000000000000000000054311323370221500221310ustar00rootroot00000000000000require "pathname" require "vagrant" require_relative "command_builder" module VagrantPlugins module Chef class Plugin < Vagrant.plugin("2") name "chef" description <<-DESC Provides support for provisioning your virtual machines with Chef via `chef-solo`, `chef-client`, `chef-zero` or `chef-apply`. DESC config(:chef_apply, :provisioner) do require_relative "config/chef_apply" Config::ChefApply end config(:chef_client, :provisioner) do require_relative "config/chef_client" Config::ChefClient end config(:chef_solo, :provisioner) do require_relative "config/chef_solo" Config::ChefSolo end config(:chef_zero, :provisioner) do require_relative "config/chef_zero" Config::ChefZero end provisioner(:chef_apply) do require_relative "provisioner/chef_apply" Provisioner::ChefApply end provisioner(:chef_client) do require_relative "provisioner/chef_client" Provisioner::ChefClient end provisioner(:chef_solo) do require_relative "provisioner/chef_solo" Provisioner::ChefSolo end provisioner(:chef_zero) do require_relative "provisioner/chef_zero" Provisioner::ChefZero end guest_capability(:debian, :chef_install) do require_relative "cap/debian/chef_install" Cap::Debian::ChefInstall end guest_capability(:freebsd, :chef_install) do require_relative "cap/freebsd/chef_install" Cap::FreeBSD::ChefInstall end guest_capability(:freebsd, :chef_installed) do require_relative "cap/freebsd/chef_installed" Cap::FreeBSD::ChefInstalled end guest_capability(:linux, :chef_installed) do require_relative "cap/linux/chef_installed" Cap::Linux::ChefInstalled end guest_capability(:omnios, :chef_install) do require_relative "cap/omnios/chef_install" Cap::OmniOS::ChefInstall end guest_capability(:omnios, :chef_installed) do require_relative "cap/omnios/chef_installed" Cap::OmniOS::ChefInstalled end guest_capability(:redhat, :chef_install) do require_relative "cap/redhat/chef_install" Cap::Redhat::ChefInstall end guest_capability(:suse, :chef_install) do require_relative "cap/suse/chef_install" Cap::Suse::ChefInstall end guest_capability(:windows, :chef_install) do require_relative "cap/windows/chef_install" Cap::Windows::ChefInstall end guest_capability(:windows, :chef_installed) do require_relative "cap/windows/chef_installed" Cap::Windows::ChefInstalled end end end end vagrant-2.0.2/plugins/provisioners/chef/provisioner/000077500000000000000000000000001323370221500226625ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/chef/provisioner/base.rb000066400000000000000000000207201323370221500241220ustar00rootroot00000000000000require "tempfile" require_relative "../../../../lib/vagrant/util/presence" require_relative "../../../../lib/vagrant/util/template_renderer" require_relative "../installer" module VagrantPlugins module Chef module Provisioner # This class is a base class where the common functionality shared between # chef-solo and chef-client provisioning are stored. This is **not an actual # provisioner**. Instead, {ChefSolo} or {ChefServer} should be used. class Base < Vagrant.plugin("2", :provisioner) include Vagrant::Util include Vagrant::Util::Presence class ChefError < Vagrant::Errors::VagrantError error_namespace("vagrant.provisioners.chef") end def initialize(machine, config) super @logger = Log4r::Logger.new("vagrant::provisioners::chef") if !present?(@config.node_name) # First attempt to get the node name from the hostname, and if that # is not present, generate/retrieve a random hostname. hostname = @machine.config.vm.hostname if present?(hostname) @machine.ui.info I18n.t("vagrant.provisioners.chef.using_hostname_node_name", hostname: hostname, ) @config.node_name = hostname else cache = @machine.data_dir.join("chef_node_name") if !cache.exist? @machine.ui.info I18n.t("vagrant.provisioners.chef.generating_node_name") cache.open("w+") do |f| f.write("vagrant-#{SecureRandom.hex(4)}") end end if cache.file? @logger.info("Loading cached node_name...") @config.node_name = cache.read.strip end end end end def install_chef return if !config.install @logger.info("Checking for Chef installation...") installer = Installer.new(@machine, product: config.product, channel: config.channel, version: config.version, omnibus_url: config.omnibus_url, force: config.install == :force, download_path: config.installer_download_path ) installer.ensure_installed end def verify_binary(binary) # 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 }" else command = "sh -c 'command -v #{binary}'" end @machine.communicate.sudo( command, error_class: ChefError, error_key: :chef_not_detected, binary: binary, ) end # Returns the path to the Chef binary, taking into account the # `binary_path` configuration option. def chef_binary_path(binary) return binary if !@config.binary_path return File.join(@config.binary_path, binary) end def chown_provisioning_folder paths = [ guest_provisioning_path, guest_file_backup_path, guest_file_cache_path, ] @machine.communicate.tap do |comm| paths.each do |path| if windows? comm.sudo("mkdir ""#{path}"" -f") else comm.sudo("mkdir -p #{path}") comm.sudo("chown -h #{@machine.ssh_info[:username]} #{path}") end end end end def setup_config(template, filename, template_vars) # If we have custom configuration, upload it remote_custom_config_path = nil if @config.custom_config_path expanded = File.expand_path( @config.custom_config_path, @machine.env.root_path) remote_custom_config_path = File.join( guest_provisioning_path, "custom-config.rb") @machine.communicate.upload(expanded, remote_custom_config_path) end config_file = TemplateRenderer.render(template, { custom_configuration: remote_custom_config_path, encrypted_data_bag_secret: guest_encrypted_data_bag_secret_key_path, environment: @config.environment, file_cache_path: guest_file_cache_path, file_backup_path: guest_file_backup_path, log_level: @config.log_level.to_sym, node_name: @config.node_name, verbose_logging: @config.verbose_logging, enable_reporting: @config.enable_reporting, http_proxy: @config.http_proxy, http_proxy_user: @config.http_proxy_user, http_proxy_pass: @config.http_proxy_pass, https_proxy: @config.https_proxy, https_proxy_user: @config.https_proxy_user, https_proxy_pass: @config.https_proxy_pass, no_proxy: @config.no_proxy, formatter: @config.formatter }.merge(template_vars)) # Create a temporary file to store the data so we can upload it. remote_file = File.join(guest_provisioning_path, filename) @machine.communicate.sudo(remove_command(remote_file), error_check: false) Tempfile.open("vagrant-chef-provisioner-config") do |f| f.binmode f.write(config_file) f.fsync f.close @machine.communicate.upload(f.path, remote_file) end end def setup_json @machine.ui.info I18n.t("vagrant.provisioners.chef.json") # Get the JSON that we're going to expose to Chef json = @config.json json[:run_list] = @config.run_list if @config.run_list && !@config.run_list.empty? json = JSON.pretty_generate(json) # Create a temporary file to store the data so we can upload it. remote_file = File.join(guest_provisioning_path, "dna.json") @machine.communicate.sudo(remove_command(remote_file), error_check: false) Tempfile.open("vagrant-chef-provisioner-config") do |f| f.binmode f.write(json) f.fsync f.close @machine.communicate.upload(f.path, remote_file) end end def upload_encrypted_data_bag_secret remote_file = guest_encrypted_data_bag_secret_key_path return if !remote_file @machine.ui.info I18n.t( "vagrant.provisioners.chef.upload_encrypted_data_bag_secret_key") @machine.communicate.sudo(remove_command(remote_file), error_check: false) @machine.communicate.upload(encrypted_data_bag_secret_key_path, remote_file) end def delete_encrypted_data_bag_secret remote_file = guest_encrypted_data_bag_secret_key_path return if remote_file.nil? @machine.communicate.sudo(remove_command(remote_file), error_check: false) end def encrypted_data_bag_secret_key_path File.expand_path(@config.encrypted_data_bag_secret_key_path, @machine.env.root_path) end def guest_encrypted_data_bag_secret_key_path if @config.encrypted_data_bag_secret_key_path File.join(guest_provisioning_path, "encrypted_data_bag_secret_key") end end def guest_provisioning_path if !@config.provisioning_path.nil? return @config.provisioning_path end if windows? "C:/vagrant-chef" else "/tmp/vagrant-chef" end end def guest_file_backup_path if !@config.file_backup_path.nil? return @config.file_backup_path end if windows? "C:/chef/backup" else "/var/chef/backup" end end def guest_file_cache_path if !@config.file_cache_path.nil? return @config.file_cache_path end if windows? "C:/chef/cache" else "/var/chef/cache" end end def remove_command(path) if windows? "if (test-path ""#{path}"") {rm ""#{path}"" -force -recurse}" else "rm -f #{path}" end end def windows? @machine.config.vm.communicator == :winrm end end end end end vagrant-2.0.2/plugins/provisioners/chef/provisioner/chef_apply.rb000066400000000000000000000042371323370221500253270ustar00rootroot00000000000000require "digest/md5" require "tempfile" require_relative "base" module VagrantPlugins module Chef module Provisioner class ChefApply < Base def provision install_chef verify_binary(chef_binary_path("chef-apply")) command = "chef-apply" command << " \"#{target_recipe_path}\"" command << " --log_level #{config.log_level}" user = @machine.ssh_info[:username] # Reset upload path permissions for the current ssh user if windows? @machine.communicate.sudo("mkdir ""#{config.upload_path}"" -f") else @machine.communicate.sudo("mkdir -p #{config.upload_path}") @machine.communicate.sudo("chown -R #{user} #{config.upload_path}") end # Upload the recipe upload_recipe @machine.ui.info(I18n.t("vagrant.provisioners.chef.running_apply", script: config.path) ) # Execute it with sudo @machine.communicate.sudo(command) do |type, data| if [:stderr, :stdout].include?(type) # Output the data with the proper color based on the stream. color = (type == :stdout) ? :green : :red # Chomp the data to avoid the newlines that the Chef outputs @machine.env.ui.info(data.chomp, color: color, prefix: false) end end end # The destination (on the guest) where the recipe will live # @return [String] def target_recipe_path key = Digest::MD5.hexdigest(config.recipe) File.join(config.upload_path, "recipe-#{key}.rb") end # Write the raw recipe contents to a tempfile and upload that to the # machine. def upload_recipe # Write the raw recipe contents to a tempfile and upload Tempfile.open(["vagrant-chef-apply", ".rb"]) do |f| f.binmode f.write(config.recipe) f.fsync f.close # Upload the tempfile to the guest @machine.communicate.upload(f.path, target_recipe_path) end end end end end end vagrant-2.0.2/plugins/provisioners/chef/provisioner/chef_client.rb000066400000000000000000000133041323370221500254530ustar00rootroot00000000000000require 'pathname' require 'vagrant' require 'vagrant/util/presence' require 'vagrant/util/subprocess' require_relative "base" module VagrantPlugins module Chef module Provisioner # This class implements provisioning via chef-client, allowing provisioning # with a chef server. class ChefClient < Base include Vagrant::Util::Presence def configure(root_config) raise ChefError, :server_validation_key_required if @config.validation_key_path.nil? raise ChefError, :server_validation_key_doesnt_exist if !File.file?(validation_key_path) raise ChefError, :server_url_required if @config.chef_server_url.nil? end def provision install_chef verify_binary(chef_binary_path("chef-client")) chown_provisioning_folder create_client_key_folder upload_validation_key upload_encrypted_data_bag_secret setup_json setup_server_config run_chef_client delete_encrypted_data_bag_secret end def cleanup if @config.delete_node delete_from_chef_server("node") end if @config.delete_client delete_from_chef_server("client") end end def create_client_key_folder @machine.ui.info I18n.t("vagrant.provisioners.chef.client_key_folder") path = Pathname.new(guest_client_key_path) if windows? @machine.communicate.sudo("mkdir ""#{path.dirname}"" -f") else @machine.communicate.sudo("mkdir -p #{path.dirname}") end end def upload_validation_key @machine.ui.info I18n.t("vagrant.provisioners.chef.upload_validation_key") @machine.communicate.upload(validation_key_path, guest_validation_key_path) end def setup_server_config setup_config("provisioners/chef_client/client", "client.rb", { chef_server_url: @config.chef_server_url, validation_client_name: @config.validation_client_name, validation_key: guest_validation_key_path, client_key: guest_client_key_path, }) end def run_chef_client if @config.run_list && @config.run_list.empty? @machine.ui.warn(I18n.t("vagrant.chef_run_list_empty")) end command = CommandBuilder.command(:client, @config, windows: windows?, colored: @machine.env.ui.color?, ) still_active = 259 #provisioner has asked chef to reboot @config.attempts.times do |attempt| exit_status = 0 while exit_status == 0 || exit_status == still_active if @machine.guest.capability?(:wait_for_reboot) @machine.guest.capability(:wait_for_reboot) elsif attempt > 0 sleep 10 @machine.communicate.wait_for_ready(@machine.config.vm.boot_timeout) end if attempt == 0 @machine.ui.info I18n.t("vagrant.provisioners.chef.running_client") else @machine.ui.info I18n.t("vagrant.provisioners.chef.running_client_again") end opts = { error_check: false, elevated: true } exit_status = @machine.communicate.sudo(command, opts) do |type, data| # Output the data with the proper color based on the stream. color = type == :stdout ? :green : :red data = data.chomp next if data.empty? @machine.ui.info(data, color: color) end # There is no need to run Chef again if it converges return if exit_status == 0 end end # If we reached this point then Chef never converged! Error. raise ChefError, :no_convergence end def validation_key_path File.expand_path(@config.validation_key_path, @machine.env.root_path) end def guest_client_key_path if !@config.client_key_path.nil? return @config.client_key_path end if windows? "C:/chef/client.pem" else "/etc/chef/client.pem" end end def guest_client_rb_path File.join(guest_provisioning_path, "client.rb") end def guest_validation_key_path File.join(guest_provisioning_path, "validation.pem") end def delete_from_chef_server(deletable) node_name = @config.node_name if !present?(node_name) @machine.ui.warn(I18n.t("vagrant.provisioners.chef.missing_node_name", deletable: deletable, )) return end @machine.ui.info(I18n.t("vagrant.provisioners.chef.deleting_from_server", deletable: deletable, name: node_name)) command = "knife #{deletable} delete #{node_name}" command << " --config '#{guest_client_rb_path}'" command << " --yes" output = [] result = @machine.communicate.sudo(command, error_check: false) do |_, data| output << data end if result != 0 @machine.ui.error("There were errors removing the #{deletable} from the Chef Server:") @machine.ui.error("") @machine.ui.error(output.join("\n")) @machine.ui.error("") @machine.ui.error("Vagrant will continue destroying the virtual machine, but you may need") @machine.ui.error("to manually delete the #{deletable} from the Chef Server!") end end end end end end vagrant-2.0.2/plugins/provisioners/chef/provisioner/chef_solo.rb000066400000000000000000000214551323370221500251570ustar00rootroot00000000000000require "digest/md5" require "securerandom" require "set" require "log4r" require "vagrant/util/counter" require_relative "base" module VagrantPlugins module Chef module Provisioner # This class implements provisioning via chef-solo. class ChefSolo < Base extend Vagrant::Util::Counter include Vagrant::Util::Counter include Vagrant::Action::Builtin::MixinSyncedFolders attr_reader :environments_folders attr_reader :cookbook_folders attr_reader :node_folders attr_reader :role_folders attr_reader :data_bags_folders def initialize(machine, config) super @logger = Log4r::Logger.new("vagrant::provisioners::chef_solo") @shared_folders = [] end def configure(root_config) @cookbook_folders = expanded_folders(@config.cookbooks_path, "cookbooks") @role_folders = expanded_folders(@config.roles_path, "roles") @data_bags_folders = expanded_folders(@config.data_bags_path, "data_bags") @environments_folders = expanded_folders(@config.environments_path, "environments") @node_folders = expanded_folders(@config.nodes_path, "nodes") existing = synced_folders(@machine, cached: true) share_folders(root_config, "csc", @cookbook_folders, existing) share_folders(root_config, "csr", @role_folders, existing) share_folders(root_config, "csdb", @data_bags_folders, existing) share_folders(root_config, "cse", @environments_folders, existing) share_folders(root_config, "csn", @node_folders, existing) end def provision install_chef # Verify that the proper shared folders exist. check = [] @shared_folders.each do |type, local_path, remote_path| # We only care about checking folders that have a local path, meaning # they were shared from the local machine, rather than assumed to # exist on the VM. check << remote_path if local_path end chown_provisioning_folder verify_shared_folders(check) verify_binary(chef_binary_path("chef-solo")) upload_encrypted_data_bag_secret setup_json setup_solo_config run_chef_solo delete_encrypted_data_bag_secret end # Converts paths to a list of properly expanded paths with types. def expanded_folders(paths, appended_folder=nil) # Convert the path to an array if it is a string or just a single # path element which contains the folder location (:host or :vm) paths = [paths] if paths.is_a?(String) || paths.first.is_a?(Symbol) results = [] paths.each do |type, path| # Create the local/remote path based on whether this is a host # or VM path. local_path = nil remote_path = nil if type == :host # Get the expanded path that the host path points to local_path = File.expand_path(path, @machine.env.root_path) if File.exist?(local_path) # Path exists on the host, setup the remote path. We use # the MD5 of the local path so that it is predictable. key = Digest::MD5.hexdigest(local_path) remote_path = "#{guest_provisioning_path}/#{key}" else appended_folder = "cookbooks" if appended_folder.nil? @machine.ui.warn(I18n.t("vagrant.provisioners.chef.#{appended_folder}_folder_not_found_warning", path: local_path.to_s)) next end else # Path already exists on the virtual machine. Expand it # relative to where we're provisioning. remote_path = File.expand_path(path, guest_provisioning_path) # Remove drive letter if running on a windows host. This is a bit # of a hack but is the most portable way I can think of at the moment # to achieve this. Otherwise, Vagrant attempts to share at some crazy # path like /home/vagrant/c:/foo/bar remote_path = remote_path.gsub(/^[a-zA-Z]:/, "") end # If we have specified a folder name to append then append it if type == :host remote_path += "/#{appended_folder}" if appended_folder end # Append the result results << [type, local_path, remote_path] end results end # Shares the given folders with the given prefix. The folders should # be of the structure resulting from the `expanded_folders` function. def share_folders(root_config, prefix, folders, existing=nil) existing_set = Set.new (existing || []).each do |_, fs| fs.each do |id, data| existing_set.add(data[:guestpath]) end end folders.each do |type, local_path, remote_path| next if type != :host # If this folder already exists, then we don't share it, it means # it was already put down on disk. # # NOTE: This is currently commented out because it was causing # major bugs (GH-5199). We will investigate why this is in more # detail for 1.8.0, but we wanted to fix this in a patch release # and this was the hammer that did that. =begin if existing_set.include?(remote_path) @logger.debug("Not sharing #{local_path}, exists as #{remote_path}") next end =end key = Digest::MD5.hexdigest(remote_path) key = key[0..8] opts = {} opts[:id] = "v-#{prefix}-#{key}" opts[:type] = @config.synced_folder_type if @config.synced_folder_type root_config.vm.synced_folder(local_path, remote_path, opts) end @shared_folders += folders end def setup_solo_config setup_config("provisioners/chef_solo/solo", "solo.rb", solo_config) end def solo_config { cookbooks_path: guest_paths(@cookbook_folders), recipe_url: @config.recipe_url, nodes_path: guest_paths(@node_folders), roles_path: guest_paths(@role_folders), data_bags_path: guest_paths(@data_bags_folders), environments_path: guest_paths(@environments_folders).first } end def run_chef_solo if @config.run_list && @config.run_list.empty? @machine.ui.warn(I18n.t("vagrant.chef_run_list_empty")) end command = CommandBuilder.command(:solo, @config, windows: windows?, colored: @machine.env.ui.color?, legacy_mode: @config.legacy_mode, ) still_active = 259 #provisioner has asked chef to reboot @config.attempts.times do |attempt| exit_status = 0 while exit_status == 0 || exit_status == still_active if @machine.guest.capability?(:wait_for_reboot) @machine.guest.capability(:wait_for_reboot) elsif attempt > 0 sleep 10 @machine.communicate.wait_for_ready(@machine.config.vm.boot_timeout) end if attempt == 0 @machine.ui.info I18n.t("vagrant.provisioners.chef.running_solo") else @machine.ui.info I18n.t("vagrant.provisioners.chef.running_solo_again") end opts = { error_check: false, elevated: true } exit_status = @machine.communicate.sudo(command, opts) do |type, data| # Output the data with the proper color based on the stream. color = type == :stdout ? :green : :red data = data.chomp next if data.empty? @machine.ui.info(data, color: color) end # There is no need to run Chef again if it converges return if exit_status == 0 end end # If we reached this point then Chef never converged! Error. raise ChefError, :no_convergence end def verify_shared_folders(folders) folders.each do |folder| @logger.debug("Checking for shared folder: #{folder}") if !@machine.communicate.test("test -d #{folder}", sudo: true) raise ChefError, :missing_shared_folders end end end protected # Extracts only the remote paths from a list of folders def guest_paths(folders) folders.map { |parts| parts[2] } end end end end end vagrant-2.0.2/plugins/provisioners/chef/provisioner/chef_zero.rb000066400000000000000000000076011323370221500251570ustar00rootroot00000000000000require "digest/md5" require "securerandom" require "set" require "log4r" require "vagrant/util/counter" require_relative "chef_solo" module VagrantPlugins module Chef module Provisioner # This class implements provisioning via chef-zero. class ChefZero < ChefSolo def initialize(machine, config) super @logger = Log4r::Logger.new("vagrant::provisioners::chef_zero") end def provision install_chef # Verify that the proper shared folders exist. check = [] @shared_folders.each do |type, local_path, remote_path| # We only care about checking folders that have a local path, meaning # they were shared from the local machine, rather than assumed to # exist on the VM. check << remote_path if local_path end chown_provisioning_folder verify_shared_folders(check) verify_binary(chef_binary_path("chef-client")) upload_encrypted_data_bag_secret setup_json setup_zero_config run_chef_zero delete_encrypted_data_bag_secret end def setup_zero_config setup_config("provisioners/chef_zero/zero", "client.rb", { local_mode: true, enable_reporting: false, cookbooks_path: guest_paths(@cookbook_folders), nodes_path: guest_paths(@node_folders), roles_path: guest_paths(@role_folders), data_bags_path: guest_paths(@data_bags_folders).first, environments_path: guest_paths(@environments_folders).first, }) end def run_chef_zero if @config.run_list && @config.run_list.empty? @machine.ui.warn(I18n.t("vagrant.chef_run_list_empty")) end command = CommandBuilder.command(:client, @config, windows: windows?, colored: @machine.env.ui.color?, local_mode: true, ) still_active = 259 #provisioner has asked chef to reboot @config.attempts.times do |attempt| exit_status = 0 while exit_status == 0 || exit_status == still_active if @machine.guest.capability?(:wait_for_reboot) @machine.guest.capability(:wait_for_reboot) elsif attempt > 0 sleep 10 @machine.communicate.wait_for_ready(@machine.config.vm.boot_timeout) end if attempt == 0 @machine.ui.info I18n.t("vagrant.provisioners.chef.running_zero") else @machine.ui.info I18n.t("vagrant.provisioners.chef.running_zero_again") end opts = { error_check: false, elevated: true } exit_status = @machine.communicate.sudo(command, opts) do |type, data| # Output the data with the proper color based on the stream. color = type == :stdout ? :green : :red data = data.chomp next if data.empty? @machine.ui.info(data, color: color) end # There is no need to run Chef again if it converges return if exit_status == 0 end end # If we reached this point then Chef never converged! Error. raise ChefError, :no_convergence end def verify_shared_folders(folders) folders.each do |folder| @logger.debug("Checking for shared folder: #{folder}") if !@machine.communicate.test("test -d #{folder}", sudo: true) raise ChefError, :missing_shared_folders end end end protected # Extracts only the remote paths from a list of folders def guest_paths(folders) folders.map { |parts| parts[2] } end end end end end vagrant-2.0.2/plugins/provisioners/docker/000077500000000000000000000000001323370221500206455ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/docker/cap/000077500000000000000000000000001323370221500214105ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/docker/cap/debian/000077500000000000000000000000001323370221500226325ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/docker/cap/debian/docker_install.rb000066400000000000000000000010361323370221500261540ustar00rootroot00000000000000module VagrantPlugins module DockerProvisioner module Cap module Debian module DockerInstall def self.docker_install(machine) machine.communicate.tap do |comm| comm.sudo("apt-get update -qq -y") comm.sudo("apt-get install -qq -y --force-yes curl apt-transport-https") comm.sudo("apt-get purge -qq -y lxc-docker* || true") comm.sudo("curl -sSL https://get.docker.com/ | sh") end end end end end end end vagrant-2.0.2/plugins/provisioners/docker/cap/debian/docker_start_service.rb000066400000000000000000000004321323370221500273620ustar00rootroot00000000000000module VagrantPlugins module DockerProvisioner module Cap module Debian module DockerStartService def self.docker_start_service(machine) machine.communicate.sudo("service docker start") end end end end end end vagrant-2.0.2/plugins/provisioners/docker/cap/fedora/000077500000000000000000000000001323370221500226505ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/docker/cap/fedora/docker_install.rb000066400000000000000000000012621323370221500261730ustar00rootroot00000000000000module VagrantPlugins module DockerProvisioner module Cap module Fedora module DockerInstall def self.docker_install(machine) machine.communicate.tap do |comm| if dnf?(machine) comm.sudo("dnf -y install docker") else comm.sudo("yum -y install docker") end comm.sudo("systemctl start docker.service") comm.sudo("systemctl enable docker.service") end end protected def self.dnf?(machine) machine.communicate.test("/usr/bin/which -s dnf") end end end end end end vagrant-2.0.2/plugins/provisioners/docker/cap/linux/000077500000000000000000000000001323370221500225475ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/docker/cap/linux/docker_configure_vagrant_user.rb000066400000000000000000000010071323370221500311620ustar00rootroot00000000000000module VagrantPlugins module DockerProvisioner module Cap module Linux module DockerConfigureVagrantUser def self.docker_configure_vagrant_user(machine) ssh_info = machine.ssh_info machine.communicate.tap do |comm| if comm.test("getent group docker") && !comm.test("id -Gn | grep docker") comm.sudo("usermod -a -G docker #{ssh_info[:username]}") end end end end end end end end vagrant-2.0.2/plugins/provisioners/docker/cap/linux/docker_daemon_running.rb000066400000000000000000000004421323370221500274260ustar00rootroot00000000000000module VagrantPlugins module DockerProvisioner module Cap module Linux module DockerDaemonRunning def self.docker_daemon_running(machine) machine.communicate.test("test -f /var/run/docker.pid") end end end end end end vagrant-2.0.2/plugins/provisioners/docker/cap/linux/docker_installed.rb000066400000000000000000000010751323370221500264050ustar00rootroot00000000000000module VagrantPlugins module DockerProvisioner module Cap module Linux module DockerInstalled def self.docker_installed(machine) paths = [ "/bin/docker", "/usr/bin/docker", "/usr/local/bin/docker", "/usr/sbin/docker", ] paths.each do |p| if machine.communicate.test("test -f #{p}", sudo: true) return true end end return false end end end end end end vagrant-2.0.2/plugins/provisioners/docker/cap/redhat/000077500000000000000000000000001323370221500226575ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/docker/cap/redhat/docker_install.rb000066400000000000000000000021241323370221500262000ustar00rootroot00000000000000module VagrantPlugins module DockerProvisioner module Cap module Redhat module DockerInstall def self.docker_install(machine) machine.communicate.tap do |comm| comm.sudo("yum -q -y update") comm.sudo("yum -q -y remove docker-io* || true") comm.sudo("curl -sSL https://get.docker.com/ | sh") end case machine.guest.capability("flavor") when :rhel_7 docker_enable_rhel7(machine) else docker_enable_default(machine) end end def self.docker_enable_rhel7(machine) machine.communicate.tap do |comm| comm.sudo("systemctl start docker.service") comm.sudo("systemctl enable docker.service") end end def self.docker_enable_default(machine) machine.communicate.tap do |comm| comm.sudo("service docker start") comm.sudo("chkconfig docker on") end end end end end end end vagrant-2.0.2/plugins/provisioners/docker/cap/redhat/docker_start_service.rb000066400000000000000000000006211323370221500274070ustar00rootroot00000000000000module VagrantPlugins module DockerProvisioner module Cap module Redhat module DockerStartService def self.docker_start_service(machine) machine.communicate.sudo("service docker start") # TODO :: waiting to start sleep 5 machine.communicate.sudo("chkconfig docker on") end end end end end end vagrant-2.0.2/plugins/provisioners/docker/client.rb000066400000000000000000000134471323370221500224610ustar00rootroot00000000000000require 'digest/sha1' module VagrantPlugins module DockerProvisioner class Client def initialize(machine) @machine = machine end def build_images(images) @machine.communicate.tap do |comm| images.each do |path, opts| @machine.ui.info(I18n.t("vagrant.docker_building_single", path: path)) comm.sudo("docker build #{opts[:args]} #{path}") do |type, data| handle_comm(type, data) end end end end def pull_images(*images) @machine.communicate.tap do |comm| images.each do |image| @machine.ui.info(I18n.t("vagrant.docker_pulling_single", name: image)) comm.sudo("docker pull #{image}") do |type, data| handle_comm(type, data) end end end end def start_service if !daemon_running? && @machine.guest.capability?(:docker_start_service) @machine.guest.capability(:docker_start_service) end end def daemon_running? @machine.guest.capability(:docker_daemon_running) end def run(containers) containers.each do |name, config| cids_dir = "/var/lib/vagrant/cids" config[:cidfile] ||= "#{cids_dir}/#{Digest::SHA1.hexdigest name}" @machine.ui.info(I18n.t("vagrant.docker_running", name: name)) @machine.communicate.sudo("mkdir -p #{cids_dir}") run_container({ name: name, original_name: name, }.merge(config)) end end def run_container(config) raise "Container's cidfile was not provided!" if !config[:cidfile] id = "$(cat #{config[:cidfile]})" if container_exists?(id) if container_args_changed?(config) @machine.ui.info(I18n.t("vagrant.docker_restarting_container_args", name: config[:name], )) stop_container(id) create_container(config) elsif container_image_changed?(config) @machine.ui.info(I18n.t("vagrant.docker_restarting_container_image", name: config[:name], )) stop_container(id) create_container(config) else start_container(id) end else create_container(config) end end def container_exists?(id) lookup_container(id, true) end def start_container(id) if !container_running?(id) @machine.communicate.sudo("docker start #{id}") end end def stop_container(id) @machine.communicate.sudo %[ docker stop #{id} docker rm #{id} ] end def container_running?(id) lookup_container(id) end def container_image_changed?(config) # Returns true if there is a container running with the given :name, # and the container is not using the latest :image. # Here, "docker inspect " returns the id of the image # that the container is using. We check that the latest image that # has been built with that name (:image) matches the one that the # container is running. cmd = ("docker inspect --format='{{.Image}}' #{config[:name]} |" + " grep $(docker images -q #{config[:image]})") return !@machine.communicate.test(cmd) end def container_args_changed?(config) path = container_data_path(config) return true if !path.exist? args = container_run_args(config) sha = Digest::SHA1.hexdigest(args) return true if path.read.chomp != sha return false end def create_container(config) args = container_run_args(config) @machine.communicate.sudo %[ rm -f #{config[:cidfile]} docker run #{args} ] name = container_name(config) sha = Digest::SHA1.hexdigest(args) container_data_path(config).open("w+") do |f| f.write(sha) end end def lookup_container(id, list_all = false) docker_ps = "sudo docker ps -q" docker_ps << " -a" if list_all @machine.communicate.tap do |comm| # Docker < 0.7.0 stores container IDs using its short version while # recent versions use the full container ID # using full container ID from now on. return comm.test("#{docker_ps} --no-trunc | grep -wFq #{id}") end end def container_name(config) name = config[:name] # If the name is the automatically assigned name, then # replace the "/" with "-" because "/" is not a valid # character for a docker container name. name = name.gsub("/", "-") if name == config[:original_name] name end def container_run_args(config) name = container_name(config) args = "--cidfile=#{config[:cidfile]} " args << "-d " if config[:daemonize] args << "--name #{name} " if name && config[:auto_assign_name] args << "--restart=#{config[:restart]}" if config[:restart] args << " #{config[:args]}" if config[:args] "#{args} #{config[:image]} #{config[:cmd]}".strip end def container_data_path(config) name = container_name(config) @machine.data_dir.join("docker-#{name}") end protected # This handles outputting the communication data back to the UI def handle_comm(type, data) if [:stderr, :stdout].include?(type) # Clear out the newline since we add one data = data.chomp return if data.empty? options = {} #options[:color] = color if !config.keep_color @machine.ui.info(data.chomp, options) end end end end end vagrant-2.0.2/plugins/provisioners/docker/config.rb000066400000000000000000000044701323370221500224440ustar00rootroot00000000000000require 'set' module VagrantPlugins module DockerProvisioner class Config < Vagrant.plugin("2", :config) attr_reader :images attr_accessor :post_install_provisioner def initialize @images = Set.new @post_install_provisioner = nil @__build_images = [] @__containers = Hash.new { |h, k| h[k] = {} } end # Accessor for internal state. def build_images @__build_images end # Accessor for the internal state. def containers @__containers end # Defines an image to build using `docker build` within the machine. # # @param [String] path Path to the Dockerfile to pass to # `docker build`. def build_image(path, **opts) @__build_images << [path, opts] end def images=(images) @images = Set.new(images) end def pull_images(*images) @images += images.map(&:to_s) end def post_install_provision(name, **options, &block) # Abort raise DockerError, :wrong_provisioner if options[:type] == "docker" proxy = VagrantPlugins::Kernel_V2::VMConfig.new proxy.provision(name, **options, &block) @post_install_provisioner = proxy.provisioners.first end def run(name, **options) @__containers[name.to_s] = options.dup end def merge(other) super.tap do |result| result.pull_images(*(other.images + self.images)) build_images = @__build_images.dup build_images += other.build_images result.instance_variable_set(:@__build_images, build_images) containers = {} @__containers.each do |name, params| containers[name] = params.dup end other.containers.each do |name, params| containers[name] = @__containers[name].merge(params) end result.instance_variable_set(:@__containers, containers) end end def finalize! @__containers.each do |name, params| params[:image] ||= name params[:auto_assign_name] = true if !params.key?(:auto_assign_name) params[:daemonize] = true if !params.key?(:daemonize) params[:restart] = "always" if !params.key?(:restart) end end end end end vagrant-2.0.2/plugins/provisioners/docker/installer.rb000066400000000000000000000021761323370221500231750ustar00rootroot00000000000000module VagrantPlugins module DockerProvisioner class Installer def initialize(machine) @machine = machine end # This handles verifying the Docker installation, installing it if it was # requested, and so on. This method will raise exceptions if things are # wrong. # @return [Boolean] - false if docker cannot be detected on machine, else # true if docker installs correctly or is installed def ensure_installed if !@machine.guest.capability?(:docker_installed) @machine.ui.warn(I18n.t("vagrant.docker_cant_detect")) return false end if !@machine.guest.capability(:docker_installed) @machine.ui.detail(I18n.t("vagrant.docker_installing")) @machine.guest.capability(:docker_install) end if !@machine.guest.capability(:docker_installed) raise DockerError, :install_failed end if @machine.guest.capability?(:docker_configure_vagrant_user) @machine.guest.capability(:docker_configure_vagrant_user) end true end end end end vagrant-2.0.2/plugins/provisioners/docker/plugin.rb000066400000000000000000000034121323370221500224700ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module DockerProvisioner class Plugin < Vagrant.plugin("2") name "docker" description <<-DESC Provides support for provisioning your virtual machines with Docker images and containers. DESC config(:docker, :provisioner) do require_relative "config" Config end guest_capability("debian", "docker_install") do require_relative "cap/debian/docker_install" Cap::Debian::DockerInstall end guest_capability("debian", "docker_start_service") do require_relative "cap/debian/docker_start_service" Cap::Debian::DockerStartService end guest_capability("fedora", "docker_install") do require_relative "cap/fedora/docker_install" Cap::Fedora::DockerInstall end guest_capability("redhat", "docker_install") do require_relative "cap/redhat/docker_install" Cap::Redhat::DockerInstall end guest_capability("redhat", "docker_start_service") do require_relative "cap/redhat/docker_start_service" Cap::Redhat::DockerStartService end guest_capability("linux", "docker_installed") do require_relative "cap/linux/docker_installed" Cap::Linux::DockerInstalled end guest_capability("linux", "docker_configure_vagrant_user") do require_relative "cap/linux/docker_configure_vagrant_user" Cap::Linux::DockerConfigureVagrantUser end guest_capability("linux", "docker_daemon_running") do require_relative "cap/linux/docker_daemon_running" Cap::Linux::DockerDaemonRunning end provisioner(:docker) do require_relative "provisioner" Provisioner end end end end vagrant-2.0.2/plugins/provisioners/docker/provisioner.rb000066400000000000000000000036721323370221500235610ustar00rootroot00000000000000require_relative "client" require_relative "installer" module VagrantPlugins module DockerProvisioner class DockerError < Vagrant::Errors::VagrantError error_namespace("vagrant.provisioners.docker") end class Provisioner < Vagrant.plugin("2", :provisioner) def initialize(machine, config, installer = nil, client = nil) super(machine, config) @installer = installer || Installer.new(@machine) @client = client || Client.new(@machine) end def provision @logger = Log4r::Logger.new("vagrant::provisioners::docker") @logger.info("Checking for Docker installation...") if @installer.ensure_installed if !config.post_install_provisioner.nil? @logger.info("Running post setup provision script...") env = { callable: method(:run_provisioner), provisioner: config.post_install_provisioner, machine: machine} machine.env.hook(:run_provisioner, env) end end # Attempt to start service if not running @client.start_service raise DockerError, :not_running if !@client.daemon_running? if config.images.any? @machine.ui.info(I18n.t("vagrant.docker_pulling_images")) @client.pull_images(*config.images) end if config.build_images.any? @machine.ui.info(I18n.t("vagrant.docker_building_images")) @client.build_images(config.build_images) end if config.containers.any? @machine.ui.info(I18n.t("vagrant.docker_starting_containers")) @client.run(config.containers) end end def run_provisioner(env) klass = Vagrant.plugin("2").manager.provisioners[env[:provisioner].type] result = klass.new(env[:machine], env[:provisioner].config) result.config.finalize! result.provision end end end end vagrant-2.0.2/plugins/provisioners/file/000077500000000000000000000000001323370221500203155ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/file/config.rb000066400000000000000000000014161323370221500221110ustar00rootroot00000000000000require "pathname" require "vagrant" module VagrantPlugins module FileUpload class Config < Vagrant.plugin("2", :config) attr_accessor :source attr_accessor :destination def validate(machine) errors = _detected_errors if !source errors << I18n.t("vagrant.provisioners.file.no_source_file") end if !destination errors << I18n.t("vagrant.provisioners.file.no_dest_file") end if source s = Pathname.new(source).expand_path(machine.env.root_path) if !s.exist? errors << I18n.t("vagrant.provisioners.file.path_invalid", path: s.to_s) end end { "File provisioner" => errors } end end end end vagrant-2.0.2/plugins/provisioners/file/plugin.rb000066400000000000000000000007651323370221500221500ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module FileUpload class Plugin < Vagrant.plugin("2") name "file" description <<-DESC Provides support for provisioning your virtual machines with uploaded files. DESC config(:file, :provisioner) do require File.expand_path("../config", __FILE__) Config end provisioner(:file) do require File.expand_path("../provisioner", __FILE__) Provisioner end end end end vagrant-2.0.2/plugins/provisioners/file/provisioner.rb000066400000000000000000000044341323370221500232260ustar00rootroot00000000000000module VagrantPlugins module FileUpload class Provisioner < Vagrant.plugin("2", :provisioner) def provision @machine.communicate.tap do |comm| source = File.expand_path(config.source) 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 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 end else command = "mkdir -p \"%s\"" % File.dirname(destination) end comm.execute(command) # now upload the file comm.upload(source, destination) end end private # Expand the guest path if the guest has the capability def expand_guest_path(destination) if machine.guest.capability?(:shell_expand_guest_path) machine.guest.capability(:shell_expand_guest_path, destination) else destination end end end end end vagrant-2.0.2/plugins/provisioners/puppet/000077500000000000000000000000001323370221500207135ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/puppet/config/000077500000000000000000000000001323370221500221605ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/puppet/config/puppet.rb000066400000000000000000000151131323370221500240230ustar00rootroot00000000000000module VagrantPlugins module Puppet module Config class Puppet < Vagrant.plugin("2", :config) # The path to Puppet's bin/ directory. # @return [String] attr_accessor :binary_path attr_accessor :facter attr_accessor :hiera_config_path attr_accessor :manifest_file attr_accessor :manifests_path attr_accessor :environment attr_accessor :environment_path attr_accessor :environment_variables attr_accessor :module_path attr_accessor :options attr_accessor :synced_folder_type attr_accessor :synced_folder_args attr_accessor :temp_dir attr_accessor :working_directory def initialize super @binary_path = UNSET_VALUE @hiera_config_path = UNSET_VALUE @manifest_file = UNSET_VALUE @manifests_path = UNSET_VALUE @environment = UNSET_VALUE @environment_path = UNSET_VALUE @environment_variables = UNSET_VALUE @module_path = UNSET_VALUE @options = [] @facter = {} @synced_folder_type = UNSET_VALUE @temp_dir = UNSET_VALUE @working_directory = UNSET_VALUE end def nfs=(value) puts "DEPRECATION: The 'nfs' setting for the Puppet provisioner is" puts "deprecated. Please use the 'synced_folder_type' setting instead." puts "The 'nfs' setting will be removed in the next version of Vagrant." if value @synced_folder_type = "nfs" else @synced_folder_type = nil end end def merge(other) super.tap do |result| result.facter = @facter.merge(other.facter) end end def finalize! super if @environment_path == UNSET_VALUE && @manifests_path == UNSET_VALUE #If both are unset, assume 'manifests' mode for now. TBD: Switch to environments by default? @manifests_path = [:host, "manifests"] end # If the paths are just strings, assume they are 'host' paths (rather than guest) if @environment_path != UNSET_VALUE && !@environment_path.is_a?(Array) @environment_path = [:host, @environment_path] end if @manifests_path != UNSET_VALUE && !@manifests_path.is_a?(Array) @manifests_path = [:host, @manifests_path] end @hiera_config_path = nil if @hiera_config_path == UNSET_VALUE if @environment_path == UNSET_VALUE @manifests_path[0] = @manifests_path[0].to_sym @environment_path = nil @manifest_file = "default.pp" if @manifest_file == UNSET_VALUE else @environment_path[0] = @environment_path[0].to_sym @environment = "production" if @environment == UNSET_VALUE if @manifests_path == UNSET_VALUE @manifests_path = nil end if @manifest_file == UNSET_VALUE @manifest_file = nil end end if @environment_variables == UNSET_VALUE @environment_variables = {} end @binary_path = nil if @binary_path == UNSET_VALUE @module_path = nil if @module_path == UNSET_VALUE @synced_folder_type = nil if @synced_folder_type == UNSET_VALUE @synced_folder_args = nil if @synced_folder_args == UNSET_VALUE @temp_dir = "/tmp/vagrant-puppet" if @temp_dir == UNSET_VALUE @working_directory = nil if @working_directory == UNSET_VALUE end # Returns the module paths as an array of paths expanded relative to the # root path. def expanded_module_paths(root_path) return [] if !module_path # Get all the paths and expand them relative to the root path, returning # the array of expanded paths paths = module_path paths = [paths] if !paths.is_a?(Array) paths.map do |path| Pathname.new(path).expand_path(root_path) end end def validate(machine) errors = _detected_errors # Calculate the manifests and module paths based on env this_expanded_module_paths = expanded_module_paths(machine.env.root_path) # Manifests path/file validation if manifests_path != nil && manifests_path[0].to_sym == :host expanded_path = Pathname.new(manifests_path[1]). expand_path(machine.env.root_path) if !expanded_path.directory? errors << I18n.t("vagrant.provisioners.puppet.manifests_path_missing", path: expanded_path.to_s) else expanded_manifest_file = expanded_path.join(manifest_file) if !expanded_manifest_file.file? && !expanded_manifest_file.directory? errors << I18n.t("vagrant.provisioners.puppet.manifest_missing", manifest: expanded_manifest_file.to_s) end end elsif environment_path != nil && environment_path[0].to_sym == :host # Environments path/file validation expanded_path = Pathname.new(environment_path[1]). expand_path(machine.env.root_path) if !expanded_path.directory? errors << I18n.t("vagrant.provisioners.puppet.environment_path_missing", path: expanded_path.to_s) else expanded_environment_file = expanded_path.join(environment) if !expanded_environment_file.file? && !expanded_environment_file.directory? errors << I18n.t("vagrant.provisioners.puppet.environment_missing", environment: environment.to_s, environmentpath: expanded_path.to_s) end end end if environment_path == nil && manifests_path == nil errors << "Please specify either a Puppet environment_path + environment (preferred) or manifests_path (deprecated)." end # Module paths validation this_expanded_module_paths.each do |path| if !path.directory? errors << I18n.t("vagrant.provisioners.puppet.module_path_missing", path: path) end end { "puppet provisioner" => errors } end end end end end vagrant-2.0.2/plugins/provisioners/puppet/config/puppet_server.rb000066400000000000000000000050161323370221500254120ustar00rootroot00000000000000module VagrantPlugins module Puppet module Config class PuppetServer < Vagrant.plugin("2", :config) # The path to Puppet's bin/ directory. # @return [String] attr_accessor :binary_path attr_accessor :client_cert_path attr_accessor :client_private_key_path attr_accessor :facter attr_accessor :options attr_accessor :puppet_server attr_accessor :puppet_node def initialize super @binary_path = UNSET_VALUE @client_cert_path = UNSET_VALUE @client_private_key_path = UNSET_VALUE @facter = {} @options = [] @puppet_node = UNSET_VALUE @puppet_server = UNSET_VALUE end def merge(other) super.tap do |result| result.facter = @facter.merge(other.facter) end end def finalize! super @binary_path = nil if @binary_path == UNSET_VALUE @client_cert_path = nil if @client_cert_path == UNSET_VALUE @client_private_key_path = nil if @client_private_key_path == UNSET_VALUE @puppet_node = nil if @puppet_node == UNSET_VALUE @puppet_server = "puppet" if @puppet_server == UNSET_VALUE end def validate(machine) errors = _detected_errors if (client_cert_path && !client_private_key_path) || (client_private_key_path && !client_cert_path) errors << I18n.t( "vagrant.provisioners.puppet_server.client_cert_and_private_key") end if client_cert_path path = Pathname.new(client_cert_path). expand_path(machine.env.root_path) if !path.file? errors << I18n.t( "vagrant.provisioners.puppet_server.client_cert_not_found") end end if client_private_key_path path = Pathname.new(client_private_key_path). expand_path(machine.env.root_path) if !path.file? errors << I18n.t( "vagrant.provisioners.puppet_server.client_private_key_not_found") end end if !puppet_node && (client_cert_path || client_private_key_path) errors << I18n.t( "vagrant.provisioners.puppet_server.cert_requires_node") end { "puppet server provisioner" => errors } end end end end end vagrant-2.0.2/plugins/provisioners/puppet/plugin.rb000066400000000000000000000014351323370221500225410ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Puppet class Plugin < Vagrant.plugin("2") name "puppet" description <<-DESC Provides support for provisioning your virtual machines with Puppet either using `puppet apply` or a Puppet server. DESC config(:puppet, :provisioner) do require_relative "config/puppet" Config::Puppet end config(:puppet_server, :provisioner) do require_relative "config/puppet_server" Config::PuppetServer end provisioner(:puppet) do require_relative "provisioner/puppet" Provisioner::Puppet end provisioner(:puppet_server) do require_relative "provisioner/puppet_server" Provisioner::PuppetServer end end end end vagrant-2.0.2/plugins/provisioners/puppet/provisioner/000077500000000000000000000000001323370221500232725ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/puppet/provisioner/puppet.rb000066400000000000000000000256051323370221500251440ustar00rootroot00000000000000require "digest/md5" require "log4r" module VagrantPlugins module Puppet module Provisioner class PuppetError < Vagrant::Errors::VagrantError error_namespace("vagrant.provisioners.puppet") end class Puppet < Vagrant.plugin("2", :provisioner) def initialize(machine, config) super @logger = Log4r::Logger.new("vagrant::provisioners::puppet") end def configure(root_config) # Calculate the paths we're going to use based on the environment root_path = @machine.env.root_path @expanded_module_paths = @config.expanded_module_paths(root_path) # Setup the module paths @module_paths = [] @expanded_module_paths.each_with_index do |path, _| key = Digest::MD5.hexdigest(path.to_s) @module_paths << [path, File.join(config.temp_dir, "modules-#{key}")] end folder_opts = {} folder_opts[:type] = @config.synced_folder_type if @config.synced_folder_type folder_opts[:owner] = "root" if !@config.synced_folder_type folder_opts[:args] = @config.synced_folder_args if @config.synced_folder_args folder_opts[:nfs__quiet] = true if @config.environment_path.is_a?(Array) # Share the environments directory with the guest if @config.environment_path[0].to_sym == :host root_config.vm.synced_folder( File.expand_path(@config.environment_path[1], root_path), environments_guest_path, folder_opts) end end if @config.manifest_file @manifest_file = File.join(manifests_guest_path, @config.manifest_file) # Share the manifests directory with the guest if @config.manifests_path[0].to_sym == :host root_config.vm.synced_folder( File.expand_path(@config.manifests_path[1], root_path), manifests_guest_path, folder_opts) end end # Share the module paths @module_paths.each do |from, to| root_config.vm.synced_folder(from, to, folder_opts) end end def parse_environment_metadata # Parse out the environment manifest path since puppet apply doesnt do that for us. environment_conf = File.join(environments_guest_path, @config.environment, "environment.conf") if @machine.communicate.test("test -e #{environment_conf}", sudo: true) @machine.communicate.sudo("cat #{environment_conf}") do | type, data| if type == :stdout data.each_line do |line| if line =~ /^\s*manifest\s+=\s+([^\s]+)/ @manifest_file = $1 @manifest_file.gsub! "$codedir", File.dirname(environments_guest_path) @manifest_file.gsub! "$environment", @config.environment if !@manifest_file.start_with? "/" @manifest_file = File.join(environments_guest_path, @config.environment, @manifest_file) end @logger.debug("Using manifest from environment.conf: #{@manifest_file}") end end end end end end def provision # If the machine has a wait for reboot functionality, then # do that (primarily Windows) if @machine.guest.capability?(:wait_for_reboot) @machine.guest.capability(:wait_for_reboot) end # In environment mode we still need to specify a manifest file, if its not, use the one from env config if specified. if !@manifest_file @manifest_file = "#{environments_guest_path}/#{@config.environment}/manifests" parse_environment_metadata end # Check that the shared folders are properly shared check = [] if @config.manifests_path.is_a?(Array) && @config.manifests_path[0] == :host check << manifests_guest_path end if @config.environment_path.is_a?(Array) && @config.environment_path[0] == :host check << environments_guest_path end @module_paths.each do |host_path, guest_path| check << guest_path end # Make sure the temporary directory is properly set up if windows? tmp_command = "mkdir -p #{config.temp_dir}" comm_opts = { shell: :powershell} else tmp_command = "mkdir -p #{config.temp_dir}; chmod 0777 #{config.temp_dir}" comm_opts = {} end @machine.communicate.tap do |comm| comm.sudo(tmp_command, comm_opts) end verify_shared_folders(check) # Verify Puppet is installed and run it puppet_bin = "puppet" verify_binary(puppet_bin) # Upload Hiera configuration if we have it @hiera_config_path = nil if config.hiera_config_path local_hiera_path = File.expand_path(config.hiera_config_path, @machine.env.root_path) @hiera_config_path = File.join(config.temp_dir, "hiera.yaml") @machine.communicate.upload(local_hiera_path, @hiera_config_path) end run_puppet_apply end def manifests_guest_path if config.manifests_path[0] == :host # The path is on the host, so point to where it is shared key = Digest::MD5.hexdigest(config.manifests_path[1]) File.join(config.temp_dir, "manifests-#{key}") else # The path is on the VM, so just point directly to it config.manifests_path[1] end end def environments_guest_path if config.environment_path[0] == :host # The path is on the host, so point to where it is shared File.join(config.temp_dir, "environments") else # The path is on the VM, so just point directly to it config.environment_path[1] end end def verify_binary(binary) # Determine the command to use to test whether Puppet is available. # This is very platform dependent. test_cmd = "sh -c 'command -v #{binary}'" if windows? test_cmd = "where.exe #{binary}" if @config.binary_path test_cmd = "where.exe \"#{@config.binary_path}:#{binary}\"" end end if !machine.communicate.test(test_cmd) @config.binary_path = "/opt/puppetlabs/bin/" @machine.communicate.sudo( "test -x /opt/puppetlabs/bin/#{binary}", error_class: PuppetError, error_key: :not_detected, binary: binary) end end def run_puppet_apply default_module_path = "/etc/puppet/modules" if windows? default_module_path = "/ProgramData/PuppetLabs/puppet/etc/modules" end options = [config.options].flatten module_paths = @module_paths.map { |_, to| to } if !@module_paths.empty? # Append the default module path module_paths << default_module_path # Add the command line switch to add the module path module_path_sep = windows? ? ";" : ":" options << "--modulepath '#{module_paths.join(module_path_sep)}'" end if @hiera_config_path options << "--hiera_config=#{@hiera_config_path}" end if !@machine.env.ui.color? options << "--color=false" end options << "--detailed-exitcodes" if config.environment_path options << "--environmentpath #{environments_guest_path}/" options << "--environment #{@config.environment}" end options << @manifest_file options = options.join(" ") # Build up the custom facts if we have any facter = nil if !config.facter.empty? facts = [] config.facter.each do |key, value| facts << "FACTER_#{key}='#{value}'" end # If we're on Windows, we need to use the PowerShell style if windows? facts.map! { |v| "$env:#{v};" } end facter = facts.join(" ") end puppet_bin = "puppet" if @config.binary_path puppet_bin = File.join(@config.binary_path, puppet_bin) end env_vars = nil if !config.environment_variables.nil? && !config.environment_variables.empty? env_vars = config.environment_variables.map do |env_key, env_value| "#{env_key}=\"#{env_value}\"" end if windows? env_vars.map! do |env_var_string| "$env:#{env_var_string}" end end env_vars = env_vars.join(" ") end command = [ env_vars, facter, puppet_bin, "apply", options ].compact.map(&:to_s).join(" ") if config.working_directory if windows? command = "cd #{config.working_directory}; if ($?) \{ #{command} \}" else command = "cd #{config.working_directory} && #{command}" end end if config.environment_path @machine.ui.info(I18n.t( "vagrant.provisioners.puppet.running_puppet_env", environment: config.environment)) else @machine.ui.info(I18n.t( "vagrant.provisioners.puppet.running_puppet", manifest: config.manifest_file)) end opts = { elevated: true, error_class: Vagrant::Errors::VagrantError, error_key: :ssh_bad_exit_status_muted, good_exit: [0,2], } if windows? opts[:shell] = :powershell end @machine.communicate.sudo(command, opts) do |type, data| if !data.chomp.empty? @machine.ui.info(data.chomp) end end end def verify_shared_folders(folders) folders.each do |folder| @logger.debug("Checking for shared folder: #{folder}") if windows? testcommand = "Test-Path #{folder}" comm_opts = { shell: :powershell} else testcommand = "test -d #{folder}" comm_opts = { sudo: true} end if !@machine.communicate.test(testcommand, comm_opts) raise PuppetError, :missing_shared_folders end end end def windows? @machine.config.vm.communicator == :winrm || @machine.config.vm.communicator == :winssh end end end end end vagrant-2.0.2/plugins/provisioners/puppet/provisioner/puppet_server.rb000066400000000000000000000073431323370221500265310ustar00rootroot00000000000000module VagrantPlugins module Puppet module Provisioner class PuppetServerError < Vagrant::Errors::VagrantError error_namespace("vagrant.provisioners.puppet_server") end class PuppetServer < Vagrant.plugin("2", :provisioner) def provision if @machine.config.vm.communicator == :winrm raise Vagrant::Errors::ProvisionerWinRMUnsupported, name: "puppet_server" end verify_binary("puppet") run_puppet_agent end def verify_binary(binary) if @config.binary_path test_cmd = "test -x #{@config.binary_path}/#{binary}" else test_cmd = "which #{binary}" end @machine.communicate.sudo( test_cmd, error_class: PuppetServerError, error_key: :not_detected, binary: binary) end def run_puppet_agent options = config.options options = [options] if !options.is_a?(Array) # Intelligently set the puppet node cert name based on certain # external parameters. cn = nil if config.puppet_node # If a node name is given, we use that directly for the certname cn = config.puppet_node elsif @machine.config.vm.hostname # If a host name is given, we explicitly set the certname to # nil so that the hostname becomes the cert name. cn = nil else # Otherwise, we default to the name of the box. cn = @machine.config.vm.box end # Add the certname option if there is one options += ["--certname", cn] if cn # A shortcut to make things easier comm = @machine.communicate # If we have client certs specified, then upload them if config.client_cert_path && config.client_private_key_path @machine.ui.info( I18n.t("vagrant.provisioners.puppet_server.uploading_client_cert")) dirname = "/tmp/puppet-#{Time.now.to_i}-#{rand(1000)}" comm.sudo("mkdir -p #{dirname}") comm.sudo("mkdir -p #{dirname}/certs") comm.sudo("mkdir -p #{dirname}/private_keys") comm.sudo("chmod -R 0777 #{dirname}") comm.upload(config.client_cert_path, "#{dirname}/certs/#{cn}.pem") comm.upload(config.client_private_key_path, "#{dirname}/private_keys/#{cn}.pem") # Setup the options so that they point to our directories options << "--certdir=#{dirname}/certs" options << "--privatekeydir=#{dirname}/private_keys" end # Disable colors if we must if !@machine.env.ui.color? options << "--color=false" end # Build up the custom facts if we have any facter = "" if !config.facter.empty? facts = [] config.facter.each do |key, value| facts << "FACTER_#{key}='#{value}'" end facter = "#{facts.join(" ")} " end puppet_bin = "puppet" if @config.binary_path puppet_bin = File.join(@config.binary_path, puppet_bin) end options = options.join(" ") command = "#{facter} #{puppet_bin} agent --onetime --no-daemonize #{options} " + "--server #{config.puppet_server} --detailed-exitcodes || [ $? -eq 2 ]" @machine.ui.info I18n.t("vagrant.provisioners.puppet_server.running_puppetd") @machine.communicate.sudo(command) do |type, data| if !data.chomp.empty? @machine.ui.info(data.chomp) end end end end end end end vagrant-2.0.2/plugins/provisioners/salt/000077500000000000000000000000001323370221500203415ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/salt/bootstrap-salt.ps1000066400000000000000000000103641323370221500237500ustar00rootroot00000000000000Param( [string]$version, [string]$pythonVersion = "2", [string]$runservice, [string]$minion, [string]$master ) # Constants $ServiceName = "salt-minion" $startupType = "Manual" # Version to install - default to latest if there is an issue If ($version -notmatch "2\d{3}\.\d{1,2}\.\d+(\-\d{1})?"){ $version = '2017.7.1' } If ($pythonVersion -notmatch "\d+") { $pythonVersion = "2" Write-Host "Defaulting to minion Python version $pythonVersion" } If ($runservice.ToLower() -eq "true"){ Write-Host "Service is set to run." [bool]$runservice = $True } ElseIf ($runservice.ToLower() -eq "false"){ Write-Host "Service will be stopped and set to manual." [bool]$runservice = $False } Else { # Param passed in wasn't clear so defaulting to true. Write-Host "Service defaulting to run." [bool]$runservice = $True } # Create C:\tmp\ - if Vagrant doesn't upload keys and/or config it might not exist New-Item C:\tmp\ -ItemType directory -force | out-null # Copy minion keys & config to correct location New-Item C:\salt\conf\pki\minion\ -ItemType directory -force | out-null # Check if minion keys have been uploaded If (Test-Path C:\tmp\minion.pem) { cp C:\tmp\minion.pem C:\salt\conf\pki\minion\ cp C:\tmp\minion.pub C:\salt\conf\pki\minion\ } # Detect architecture If ([IntPtr]::Size -eq 4) { $arch = "x86" } Else { $arch = "AMD64" } # Download minion setup file $minionFilename = "Salt-Minion-$version-$arch-Setup.exe" $versionYear = [regex]::Match($version, "\d+").Value If ([convert]::ToInt32($versionYear) -ge 2017) { $minionFilename = "Salt-Minion-$version-Py$pythonVersion-$arch-Setup.exe" } Write-Host "Downloading Salt minion installer $minionFilename" $webclient = New-Object System.Net.WebClient $url = "https://repo.saltstack.com/windows/$minionFilename" $file = "C:\tmp\salt.exe" [int]$retries = 0 Do { try { $retries++ $ErrorActionPreference='Stop' $webclient.DownloadFile($url, $file) break } catch [Exception] { if($retries -eq 5) { $_ $_.GetType() $_.Exception $_.Exception.StackTrace Write-Host exit 1 } Write-Warning "Retrying download in 2 seconds. Retry # $retries" Start-Sleep -s 2 } } Until($retries -eq 5) # Install minion silently Write-Host "Installing Salt minion..." #Wait for process to exit before continuing... If($PSBoundParameters.ContainsKey('minion') -and $PSBoundParameters.ContainsKey('master')) { C:\tmp\salt.exe /S /minion-name=$minion /master=$master | Out-Null Write-Host C:\tmp\salt.exe /S /minion-name=$minion /master=$master | Out-Null } ElseIf($PSBoundParameters.ContainsKey('minion') -and !$PSBoundParameters.ContainsKey('master')) { C:\tmp\salt.exe /S /minion-name=$minion | Out-Null Write-Host C:\tmp\salt.exe /S /minion-name=$minion | Out-Null } ElseIf(!$PSBoundParameters.ContainsKey('minion') -and $PSBoundParameters.ContainsKey('master')) { C:\tmp\salt.exe /S /master=$master | Out-Null Write-Host C:\tmp\salt.exe /S /master=$master | Out-Null } Else { C:\tmp\salt.exe /S | Out-Null Write-Host C:\tmp\salt.exe /S | Out-Null } # Check if minion config has been uploaded If (Test-Path C:\tmp\minion) { cp C:\tmp\minion C:\salt\conf\ } # Wait for salt-minion service to be registered before trying to start it $service = Get-Service salt-minion -ErrorAction SilentlyContinue While (!$service) { Start-Sleep -s 2 $service = Get-Service salt-minion -ErrorAction SilentlyContinue } If($runservice) { # Start service Start-Service -Name "salt-minion" -ErrorAction SilentlyContinue # Check if service is started, otherwise retry starting the # service 4 times. $try = 0 While (($service.Status -ne "Running") -and ($try -ne 4)) { Start-Service -Name "salt-minion" -ErrorAction SilentlyContinue $service = Get-Service salt-minion -ErrorAction SilentlyContinue Start-Sleep -s 2 $try += 1 } # If the salt-minion service is still not running, something probably # went wrong and user intervention is required - report failure. If ($service.Status -eq "Stopped") { Write-Host "Failed to start Salt minion" exit 1 } } Else { Write-Host "Stopping salt minion" Set-Service "$ServiceName" -startupType "$startupType" Stop-Service "$ServiceName" } Write-Host "Salt minion successfully installed"vagrant-2.0.2/plugins/provisioners/salt/bootstrap-salt.sh000077500000000000000000000014121323370221500236540ustar00rootroot00000000000000#!/bin/sh - # We just download the bootstrap script by default and execute that. if [ -x /usr/bin/fetch ]; then /usr/bin/fetch -o bootstrap-salt.sh https://raw.githubusercontent.com/saltstack/salt-bootstrap/stable/bootstrap-salt.sh elif [ -x /usr/bin/curl ]; then /usr/bin/curl -L -O https://raw.githubusercontent.com/saltstack/salt-bootstrap/stable/bootstrap-salt.sh elif [ -x /usr/bin/wget ]; then /usr/bin/wget -O bootstrap-salt.sh https://raw.githubusercontent.com/saltstack/salt-bootstrap/stable/bootstrap-salt.sh else python -c 'import urllib; urllib.urlretrieve("https://raw.githubusercontent.com/saltstack/salt-bootstrap/stable/bootstrap-salt.sh", "bootstrap-salt.sh")' fi if [ -e bootstrap-salt.sh ]; then sh bootstrap-salt.sh "$@" else exit 1 fi vagrant-2.0.2/plugins/provisioners/salt/config.rb000066400000000000000000000154611323370221500221420ustar00rootroot00000000000000require "vagrant" require "vagrant/util/deep_merge" module VagrantPlugins module Salt class Config < Vagrant.plugin("2", :config) ## salty-vagrant options attr_accessor :minion_config attr_accessor :minion_key attr_accessor :minion_pub attr_accessor :master_config attr_accessor :master_key attr_accessor :master_pub attr_accessor :grains_config attr_accessor :run_highstate attr_accessor :run_overstate attr_accessor :orchestrations attr_accessor :always_install attr_accessor :bootstrap_script attr_accessor :verbose attr_accessor :seed_master attr_reader :pillar_data attr_accessor :colorize attr_accessor :log_level attr_accessor :masterless attr_accessor :minion_id attr_accessor :salt_call_args attr_accessor :salt_args ## bootstrap options attr_accessor :temp_config_dir attr_accessor :install_type attr_accessor :install_args attr_accessor :install_master attr_accessor :install_syndic attr_accessor :no_minion attr_accessor :bootstrap_options attr_accessor :version attr_accessor :python_version attr_accessor :run_service attr_accessor :master_id def initialize @minion_config = UNSET_VALUE @minion_key = UNSET_VALUE @minion_pub = UNSET_VALUE @master_config = UNSET_VALUE @master_key = UNSET_VALUE @master_pub = UNSET_VALUE @grains_config = UNSET_VALUE @run_highstate = UNSET_VALUE @run_overstate = UNSET_VALUE @always_install = UNSET_VALUE @bootstrap_script = UNSET_VALUE @verbose = UNSET_VALUE @seed_master = UNSET_VALUE @pillar_data = UNSET_VALUE @colorize = UNSET_VALUE @log_level = UNSET_VALUE @temp_config_dir = UNSET_VALUE @install_type = UNSET_VALUE @install_args = UNSET_VALUE @install_master = UNSET_VALUE @install_syndic = UNSET_VALUE @no_minion = UNSET_VALUE @bootstrap_options = UNSET_VALUE @masterless = UNSET_VALUE @minion_id = UNSET_VALUE @version = UNSET_VALUE @python_version = UNSET_VALUE @run_service = UNSET_VALUE @master_id = UNSET_VALUE @salt_call_args = UNSET_VALUE @salt_args = UNSET_VALUE end def finalize! @grains_config = nil if @grains_config == UNSET_VALUE @run_highstate = nil if @run_highstate == UNSET_VALUE @run_overstate = nil if @run_overstate == UNSET_VALUE @always_install = nil if @always_install == UNSET_VALUE @bootstrap_script = nil if @bootstrap_script == UNSET_VALUE @verbose = nil if @verbose == UNSET_VALUE @seed_master = nil if @seed_master == UNSET_VALUE @pillar_data = {} if @pillar_data == UNSET_VALUE @colorize = nil if @colorize == UNSET_VALUE @log_level = nil if @log_level == UNSET_VALUE @temp_config_dir = nil if @temp_config_dir == UNSET_VALUE @install_type = nil if @install_type == UNSET_VALUE @install_args = nil if @install_args == UNSET_VALUE @install_master = nil if @install_master == UNSET_VALUE @install_syndic = nil if @install_syndic == UNSET_VALUE @no_minion = nil if @no_minion == UNSET_VALUE @bootstrap_options = nil if @bootstrap_options == UNSET_VALUE @masterless = false if @masterless == UNSET_VALUE @minion_id = nil if @minion_id == UNSET_VALUE @version = nil if @version == UNSET_VALUE @python_version = nil if @python_version == UNSET_VALUE @run_service = nil if @run_service == UNSET_VALUE @master_id = nil if @master_id == UNSET_VALUE @salt_call_args = nil if @salt_call_args == UNSET_VALUE @salt_args = nil if @salt_args == UNSET_VALUE # NOTE: Optimistic defaults are set in the provisioner. UNSET_VALUEs # are converted there to allow proper detection of unset values. # @minion_config = nil if @minion_config == UNSET_VALUE # @minion_key = nil if @minion_key == UNSET_VALUE # @minion_pub = nil if @minion_pub == UNSET_VALUE # @master_config = nil if @master_config == UNSET_VALUE # @master_key = nil if @master_key == UNSET_VALUE # @master_pub = nil if @master_pub == UNSET_VALUE end def pillar(data) @pillar_data = {} if @pillar_data == UNSET_VALUE @pillar_data = Vagrant::Util::DeepMerge.deep_merge(@pillar_data, data) end def validate(machine) errors = _detected_errors if @minion_config && @minion_config != UNSET_VALUE expanded = Pathname.new(@minion_config).expand_path(machine.env.root_path) if !expanded.file? errors << I18n.t("vagrant.provisioners.salt.minion_config_nonexist", missing_config_file: expanded) end end if @master_config && @master_config != UNSET_VALUE expanded = Pathname.new(@master_config).expand_path(machine.env.root_path) if !expanded.file? errors << I18n.t("vagrant.provisioners.salt.master_config_nonexist", missing_config_file: expanded) end end if @minion_key || @minion_pub if !@minion_key || !@minion_pub errors << I18n.t("vagrant.provisioners.salt.missing_key") end end if @master_key || @master_pub if !@master_key || !@master_pub errors << I18n.t("vagrant.provisioners.salt.missing_key") end end if @grains_config expanded = Pathname.new(@grains_config).expand_path(machine.env.root_path) if !expanded.file? errors << I18n.t("vagrant.provisioners.salt.grains_config_nonexist") end end if @install_master && !@no_minion && !@seed_master && @run_highstate errors << I18n.t("vagrant.provisioners.salt.must_accept_keys") end if @salt_call_args && !@salt_call_args.is_a?(Array) errors << I18n.t("vagrant.provisioners.salt.args_array") end if @salt_args && !@salt_args.is_a?(Array) errors << I18n.t("vagrant.provisioners.salt.args_array") end if @python_version && @python_version.is_a?(String) && !@python_version.scan(/\D/).empty? errors << I18n.t("vagrant.provisioners.salt.python_version") end if @python_version && !(@python_version.is_a?(Integer) || @python_version.is_a?(String)) errors << I18n.t("vagrant.provisioners.salt.python_version") end return {"salt provisioner" => errors} end end end end vagrant-2.0.2/plugins/provisioners/salt/errors.rb000066400000000000000000000003171323370221500222030ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Salt module Errors class SaltError < Vagrant::Errors::VagrantError error_namespace("vagrant.provisioners.salt") end end end endvagrant-2.0.2/plugins/provisioners/salt/plugin.rb000066400000000000000000000007101323370221500221620ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Salt class Plugin < Vagrant.plugin("2") name "salt" description <<-DESC Provisions virtual machines using SaltStack DESC config(:salt, :provisioner) do require File.expand_path("../config", __FILE__) Config end provisioner(:salt) do require File.expand_path("../provisioner", __FILE__) Provisioner end end end end vagrant-2.0.2/plugins/provisioners/salt/provisioner.rb000066400000000000000000000412331323370221500232500ustar00rootroot00000000000000require 'json' module VagrantPlugins module Salt class Provisioner < Vagrant.plugin("2", :provisioner) # Default path values to set within configuration only # if configuration value is unset and local path exists OPTIMISTIC_PATH_DEFAULTS = Hash[*[ "minion_config", "salt/minion", "minion_key", "salt/key/minion.key", "minion_pub", "salt/key/minion.pub", "master_config", "salt/master", "master_key", "salt/key/master.key", "master_pub", "salt/key/master.pub" ].map(&:freeze)].freeze def provision set_default_configs upload_configs upload_keys run_bootstrap_script call_overstate call_highstate call_orchestrate end # Return a list of accepted keys def keys(group='minions') out = @machine.communicate.sudo("salt-key --out json") do |type, output| begin if type == :stdout out = JSON::load(output) break out[group] end end end return out end ## Utilities def expanded_path(rel_path) Pathname.new(rel_path).expand_path(@machine.env.root_path) end def binaries_found ## Determine States, ie: install vs configure desired_binaries = [] if !@config.no_minion if @machine.config.vm.communicator == :winrm desired_binaries.push('C:\\salt\\salt-minion.bat') desired_binaries.push('C:\\salt\\salt-call.bat') else desired_binaries.push('salt-minion') desired_binaries.push('salt-call') end end if @config.install_master if @machine.config.vm.communicator == :winrm raise Vagrant::Errors::ProvisionerWinRMUnsupported, name: "salt.install_master" else desired_binaries.push('salt-master') end end if @config.install_syndic if @machine.config.vm.communicator == :winrm raise Vagrant::Errors::ProvisionerWinRMUnsupported, name: "salt.install_syndic" else desired_binaries.push('salt-syndic') end end found = true for binary in desired_binaries @machine.env.ui.info "Checking if %s is installed" % binary if !@machine.communicate.test("which %s" % binary) @machine.env.ui.info "%s was not found." % binary found = false else @machine.env.ui.info "%s found" % binary end end return found end def need_configure @config.minion_config or @config.minion_key or @config.master_config or @config.master_key or @config.grains_config or @config.version end def need_install if @config.always_install return true else return !binaries_found() end end def temp_config_dir if @machine.config.vm.communicator == :winrm return @config.temp_config_dir || "C:\\tmp" else return @config.temp_config_dir || "/tmp" end end # Generates option string for bootstrap script def bootstrap_options(install, configure, config_dir) options = "" # Any extra options passed to bootstrap if @config.bootstrap_options options = "%s %s" % [options, @config.bootstrap_options] end if configure && @machine.config.vm.communicator != :winrm options = "%s -F -c %s" % [options, config_dir] end if @config.seed_master && @config.install_master && @machine.config.vm.communicator != :winrm seed_dir = "/tmp/minion-seed-keys" @machine.communicate.sudo("mkdir -p -m777 #{seed_dir}") @config.seed_master.each do |name, keyfile| sourcepath = expanded_path(keyfile).to_s dest = "#{seed_dir}/#{name}" @machine.communicate.upload(sourcepath, dest) end options = "#{options} -k #{seed_dir}" end if configure && !install && @machine.config.vm.communicator != :winrm options = "%s -C" % options end if @config.install_master && @machine.config.vm.communicator != :winrm options = "%s -M" % options end if @config.install_syndic && @machine.config.vm.communicator != :winrm options = "%s -S" % options end if @config.no_minion && @machine.config.vm.communicator != :winrm options = "%s -N" % options end if @config.install_type && @machine.config.vm.communicator != :winrm options = "%s %s" % [options, @config.install_type] end if @config.install_args && @machine.config.vm.communicator != :winrm options = "%s %s" % [options, @config.install_args] end if @config.verbose @machine.env.ui.info "Using Bootstrap Options: %s" % options end return options end ## Actions # Get pillar string to pass with the salt command def get_pillar " pillar='#{@config.pillar_data.to_json}'" if !@config.pillar_data.empty? end # Get colorization option string to pass with the salt command def get_colorize @config.colorize ? " --force-color" : " --no-color" end # Get log output level option string to pass with the salt command def get_loglevel log_levels = ["all", "garbage", "trace", "debug", "info", "warning", "error", "quiet"] if log_levels.include? @config.log_level " --log-level=#{@config.log_level}" else " --log-level=debug" end end # Get command-line options for masterless provisioning def get_masterless options = "" if @config.masterless options = " --local" if @config.minion_id options += " --id #{@config.minion_id}" end end return options end # Append additional arguments to the salt command def get_salt_args " " + Array(@config.salt_args).join(" ") end # Append additional arguments to the salt-call command def get_call_args " " + Array(@config.salt_call_args).join(" ") end # Copy master and minion configs to VM def upload_configs if @config.minion_config @machine.env.ui.info "Copying salt minion config to vm." @machine.communicate.upload(expanded_path(@config.minion_config).to_s, temp_config_dir + "/minion") end if @config.master_config @machine.env.ui.info "Copying salt master config to vm." @machine.communicate.upload(expanded_path(@config.master_config).to_s, temp_config_dir + "/master") end if @config.grains_config @machine.env.ui.info "Copying salt grains config to vm." @machine.communicate.upload(expanded_path(@config.grains_config).to_s, temp_config_dir + "/grains") end end # Copy master and minion keys to VM def upload_keys if @config.minion_key and @config.minion_pub @machine.env.ui.info "Uploading minion keys." @machine.communicate.upload(expanded_path(@config.minion_key).to_s, temp_config_dir + "/minion.pem") @machine.communicate.sudo("chmod u+w #{temp_config_dir}/minion.pem") @machine.communicate.upload(expanded_path(@config.minion_pub).to_s, temp_config_dir + "/minion.pub") end if @config.master_key and @config.master_pub @machine.env.ui.info "Uploading master keys." @machine.communicate.upload(expanded_path(@config.master_key).to_s, temp_config_dir + "/master.pem") @machine.communicate.sudo("chmod u+w #{temp_config_dir}/master.pem") @machine.communicate.upload(expanded_path(@config.master_pub).to_s, temp_config_dir + "/master.pub") end end # Get bootstrap file location, bundled or custom def get_bootstrap if @config.bootstrap_script bootstrap_abs_path = expanded_path(@config.bootstrap_script) else if @machine.config.vm.communicator == :winrm bootstrap_abs_path = Pathname.new("../bootstrap-salt.ps1").expand_path(__FILE__) else bootstrap_abs_path = Pathname.new("../bootstrap-salt.sh").expand_path(__FILE__) end end return bootstrap_abs_path end # Determine if we are configure and/or installing, then do either def run_bootstrap_script install = need_install() configure = need_configure() config_dir = temp_config_dir() options = bootstrap_options(install, configure, config_dir) if configure or install if configure and !install @machine.env.ui.info "Salt binaries found. Configuring only." else @machine.env.ui.info "Bootstrapping Salt... (this may take a while)" end bootstrap_path = get_bootstrap if @machine.config.vm.communicator == :winrm if @config.version options += " -version %s" % @config.version end if @config.python_version options += " -pythonVersion %s" % @config.python_version end if @config.run_service @machine.env.ui.info "Salt minion will be stopped after installing." options += " -runservice %s" % @config.run_service end if @config.minion_id @machine.env.ui.info "Setting minion to @config.minion_id." options += " -minion %s" % @config.minion_id end if @config.master_id @machine.env.ui.info "Setting master to @config.master_id." options += " -master %s" % @config.master_id end bootstrap_destination = File.join(config_dir, "bootstrap_salt.ps1") else bootstrap_destination = File.join(config_dir, "bootstrap_salt.sh") end if @machine.communicate.test("test -f %s" % bootstrap_destination) @machine.communicate.sudo("rm -f %s" % bootstrap_destination) end @machine.communicate.upload(bootstrap_path.to_s, bootstrap_destination) @machine.communicate.sudo("chmod +x %s" % bootstrap_destination) if @machine.config.vm.communicator == :winrm bootstrap = @machine.communicate.sudo("powershell.exe -NonInteractive -NoProfile -executionpolicy bypass -file %s %s" % [bootstrap_destination, options]) do |type, data| if data[0] == "\n" # Remove any leading newline but not whitespace. If we wanted to # remove newlines and whitespace we would have used data.lstrip data = data[1..-1] end if @config.verbose @machine.env.ui.info(data.rstrip) end end else bootstrap = @machine.communicate.sudo("%s %s" % [bootstrap_destination, options]) do |type, data| if data[0] == "\n" # Remove any leading newline but not whitespace. If we wanted to # remove newlines and whitespace we would have used data.lstrip data = data[1..-1] end if @config.verbose @machine.env.ui.info(data.rstrip) end end end if !bootstrap raise Salt::Errors::SaltError, :bootstrap_failed end if configure and !install @machine.env.ui.info "Salt successfully configured!" elsif configure and install @machine.env.ui.info "Salt successfully configured and installed!" elsif !configure and install @machine.env.ui.info "Salt successfully installed!" end else @machine.env.ui.info "Salt did not need installing or configuring." end end def call_overstate if @config.run_overstate # If verbose is on, do not duplicate a failed command's output in the error message. ssh_opts = {} if @config.verbose ssh_opts = { error_key: :ssh_bad_exit_status_muted } end if @config.install_master @machine.env.ui.info "Calling state.overstate... (this may take a while)" @machine.communicate.sudo("salt '*' saltutil.sync_all") @machine.communicate.sudo("salt-run state.over", ssh_opts) do |type, data| if @config.verbose @machine.env.ui.info(data) end end else @machine.env.ui.info "run_overstate does not make sense on a minion. Not running state.overstate." end else @machine.env.ui.info "run_overstate set to false. Not running state.overstate." end end def call_highstate if @config.run_highstate # If verbose is on, do not duplicate a failed command's output in the error message. ssh_opts = {} if @config.verbose ssh_opts = { error_key: :ssh_bad_exit_status_muted } end @machine.env.ui.info "Calling state.highstate... (this may take a while)" if @config.install_master unless @config.masterless? @machine.communicate.sudo("salt '*' saltutil.sync_all") end options = "#{get_masterless}#{get_loglevel}#{get_colorize}#{get_pillar}#{get_salt_args}" @machine.communicate.sudo("salt '*' state.highstate --verbose#{options}", ssh_opts) do |type, data| if @config.verbose @machine.env.ui.info(data.rstrip) end end else if @machine.config.vm.communicator == :winrm opts = { elevated: true } unless @config.masterless? @machine.communicate.execute("C:\\salt\\salt-call.bat saltutil.sync_all", opts) end # TODO: something equivalent to { error_key: :ssh_bad_exit_status_muted }? options = "#{get_masterless}#{get_loglevel}#{get_colorize}#{get_pillar}#{get_call_args}" @machine.communicate.execute("C:\\salt\\salt-call.bat state.highstate --retcode-passthrough#{options}", opts) do |type, data| if @config.verbose @machine.env.ui.info(data.rstrip) end end else unless @config.masterless? @machine.communicate.sudo("salt-call saltutil.sync_all") end options = "#{get_masterless}#{get_loglevel}#{get_colorize}#{get_pillar}#{get_call_args}" @machine.communicate.sudo("salt-call state.highstate --retcode-passthrough#{options}", ssh_opts) do |type, data| if @config.verbose @machine.env.ui.info(data.rstrip) end end end end else @machine.env.ui.info "run_highstate set to false. Not running state.highstate." end end def call_orchestrate if !@config.orchestrations @machine.env.ui.info "orchestrate is nil. Not running state.orchestrate." return end if !@config.install_master @machine.env.ui.info "orchestrate does not make sense on a minion. Not running state.orchestrate." return end log_output = lambda do |type, data| if @config.verbose @machine.env.ui.info(data) end end # If verbose is on, do not duplicate a failed command's output in the error message. ssh_opts = {} if @config.verbose ssh_opts = { error_key: :ssh_bad_exit_status_muted } end @machine.env.ui.info "Running the following orchestrations: #{@config.orchestrations}" @machine.env.ui.info "Running saltutil.sync_all before orchestrating" @machine.communicate.sudo("salt '*' saltutil.sync_all", ssh_opts, &log_output) @config.orchestrations.each do |orchestration| cmd = "salt-run -l info state.orchestrate #{orchestration}" @machine.env.ui.info "Calling #{cmd}... (this may take a while)" @machine.communicate.sudo(cmd, ssh_opts, &log_output) end end # Sets optimistic default values into config def set_default_configs OPTIMISTIC_PATH_DEFAULTS.each do |config_key, config_default| if config.send(config_key) == Config::UNSET_VALUE config_value = File.exist?(expanded_path(config_default)) ? config_default : nil config.send("#{config_key}=", config_value) end end end end end end vagrant-2.0.2/plugins/provisioners/shell/000077500000000000000000000000001323370221500205055ustar00rootroot00000000000000vagrant-2.0.2/plugins/provisioners/shell/config.rb000066400000000000000000000110631323370221500223000ustar00rootroot00000000000000require 'uri' module VagrantPlugins module Shell class Config < Vagrant.plugin("2", :config) attr_accessor :inline attr_accessor :path attr_accessor :md5 attr_accessor :sha1 attr_accessor :env attr_accessor :upload_path attr_accessor :args attr_accessor :privileged attr_accessor :binary attr_accessor :keep_color attr_accessor :name attr_accessor :sensitive attr_accessor :powershell_args attr_accessor :powershell_elevated_interactive def initialize @args = UNSET_VALUE @inline = UNSET_VALUE @path = UNSET_VALUE @md5 = UNSET_VALUE @sha1 = UNSET_VALUE @env = UNSET_VALUE @upload_path = UNSET_VALUE @privileged = UNSET_VALUE @binary = UNSET_VALUE @keep_color = UNSET_VALUE @name = UNSET_VALUE @sensitive = UNSET_VALUE @powershell_args = UNSET_VALUE @powershell_elevated_interactive = UNSET_VALUE end def finalize! @args = nil if @args == UNSET_VALUE @inline = nil if @inline == UNSET_VALUE @path = nil if @path == UNSET_VALUE @md5 = nil if @md5 == UNSET_VALUE @sha1 = nil if @sha1 == UNSET_VALUE @env = {} if @env == UNSET_VALUE @upload_path = "/tmp/vagrant-shell" if @upload_path == UNSET_VALUE @privileged = true if @privileged == UNSET_VALUE @binary = false if @binary == UNSET_VALUE @keep_color = false if @keep_color == UNSET_VALUE @name = nil if @name == UNSET_VALUE @sensitive = false if @sensitive == UNSET_VALUE @powershell_args = "-ExecutionPolicy Bypass" if @powershell_args == UNSET_VALUE @powershell_elevated_interactive = false if @powershell_elevated_interactive == UNSET_VALUE if @args && args_valid? @args = @args.is_a?(Array) ? @args.map { |a| a.to_s } : @args.to_s end if @sensitive @env.each do |_, v| Vagrant::Util::CredentialScrubber.sensitive(v) end end end def validate(machine) errors = _detected_errors # Validate that the parameters are properly set if path && inline errors << I18n.t("vagrant.provisioners.shell.path_and_inline_set") elsif !path && !inline errors << I18n.t("vagrant.provisioners.shell.no_path_or_inline") end # If it is not an URL, we validate the existence of a script to upload if path && !remote? expanded_path = Pathname.new(path).expand_path(machine.env.root_path) if !expanded_path.file? errors << I18n.t("vagrant.provisioners.shell.path_invalid", path: expanded_path) else data = expanded_path.read(16) if data && !data.valid_encoding? errors << I18n.t( "vagrant.provisioners.shell.invalid_encoding", actual: data.encoding.to_s, default: Encoding.default_external.to_s, path: expanded_path.to_s) end end end if !env.is_a?(Hash) errors << I18n.t("vagrant.provisioners.shell.env_must_be_a_hash") end # There needs to be a path to upload the script to if !upload_path errors << I18n.t("vagrant.provisioners.shell.upload_path_not_set") end if !args_valid? errors << I18n.t("vagrant.provisioners.shell.args_bad_type") end if powershell_elevated_interactive && !privileged errors << I18n.t("vagrant.provisioners.shell.interactive_not_elevated") end { "shell provisioner" => errors } end # Args are optional, but if they're provided we only support them as a # string or as an array. def args_valid? return true if !args return true if args.is_a?(String) return true if args.is_a?(Integer) if args.is_a?(Array) args.each do |a| return false if !a.kind_of?(String) && !a.kind_of?(Integer) end return true end end def remote? path =~ URI.regexp(["ftp", "http", "https"]) end end end end vagrant-2.0.2/plugins/provisioners/shell/plugin.rb000066400000000000000000000007621323370221500223350ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Shell class Plugin < Vagrant.plugin("2") name "shell" description <<-DESC Provides support for provisioning your virtual machines with shell scripts. DESC config(:shell, :provisioner) do require File.expand_path("../config", __FILE__) Config end provisioner(:shell) do require File.expand_path("../provisioner", __FILE__) Provisioner end end end end vagrant-2.0.2/plugins/provisioners/shell/provisioner.rb000066400000000000000000000247631323370221500234250ustar00rootroot00000000000000require "pathname" require "tempfile" require "vagrant/util/downloader" require "vagrant/util/retryable" module VagrantPlugins module Shell class Provisioner < Vagrant.plugin("2", :provisioner) include Vagrant::Util::Retryable def provision args = "" if config.args.is_a?(String) args = " #{config.args.to_s}" elsif config.args.is_a?(Array) args = config.args.map { |a| quote_and_escape(a) } args = " #{args.join(" ")}" end case @machine.config.vm.communicator when :winrm provision_winrm(args) when :winssh provision_winssh(args) else provision_ssh(args) end end protected # This handles outputting the communication data back to the UI def handle_comm(type, data) if [:stderr, :stdout].include?(type) # Output the data with the proper color based on the stream. color = type == :stdout ? :green : :red # Clear out the newline since we add one data = data.chomp return if data.empty? options = {} options[:color] = color if !config.keep_color @machine.ui.detail(data.chomp, options) end end # This is the provision method called if SSH is what is running # on the remote end, which assumes a POSIX-style host. def provision_ssh(args) env = config.env.map { |k,v| "#{k}=#{quote_and_escape(v.to_s)}" } env = env.join(" ") command = "chmod +x '#{config.upload_path}'" command << " &&" command << " #{env}" if !env.empty? command << " #{config.upload_path}#{args}" with_script_file do |path| # Upload the script to the machine @machine.communicate.tap do |comm| # Reset upload path permissions for the current ssh user info = nil retryable(on: Vagrant::Errors::SSHNotReady, tries: 3, sleep: 2) do info = @machine.ssh_info raise Vagrant::Errors::SSHNotReady if info.nil? end user = info[:username] comm.sudo("chown -R #{user} #{config.upload_path}", error_check: false) comm.upload(path.to_s, config.upload_path) if config.name @machine.ui.detail(I18n.t("vagrant.provisioners.shell.running", script: "script: #{config.name}")) elsif config.path @machine.ui.detail(I18n.t("vagrant.provisioners.shell.running", script: path.to_s)) else @machine.ui.detail(I18n.t("vagrant.provisioners.shell.running", script: "inline script")) end # Execute it with sudo comm.execute( command, sudo: config.privileged, error_key: :ssh_bad_exit_status_muted ) do |type, data| handle_comm(type, data) end end end end # This is the provision method called if Windows OpenSSH is what is running # on the remote end, which assumes a non-POSIX-style host. def provision_winssh(args) with_script_file do |path| # Upload the script to the machine @machine.communicate.tap do |comm| env = config.env.map{|k,v| comm.generate_environment_export(k, v)}.join upload_path = config.upload_path.to_s if File.extname(upload_path).empty? remote_ext = @machine.config.winssh.shell == "powershell" ? "ps1" : "bat" upload_path << ".#{remote_ext}" end if remote_ext == "ps1" # Copy powershell_args from configuration shell_args = config.powershell_args # For PowerShell scripts bypass the execution policy unless already specified shell_args += " -ExecutionPolicy Bypass" if config.powershell_args !~ /[-\/]ExecutionPolicy/i # CLIXML output is kinda useless, especially on non-windows hosts shell_args += " -OutputFormat Text" if config.powershell_args !~ /[-\/]OutputFormat/i command = "#{env}\npowershell #{shell_args} #{upload_path}#{args}" else command = "#{env}\n#{upload_path}#{args}" end # Reset upload path permissions for the current ssh user info = nil retryable(on: Vagrant::Errors::SSHNotReady, tries: 3, sleep: 2) do info = @machine.ssh_info raise Vagrant::Errors::SSHNotReady if info.nil? end comm.upload(path.to_s, upload_path) if config.name @machine.ui.detail(I18n.t("vagrant.provisioners.shell.running", script: "script: #{config.name}")) elsif config.path @machine.ui.detail(I18n.t("vagrant.provisioners.shell.running", script: path.to_s)) else @machine.ui.detail(I18n.t("vagrant.provisioners.shell.running", script: "inline script")) end # Execute it with sudo comm.execute( command, sudo: config.privileged, error_key: :ssh_bad_exit_status_muted ) do |type, data| handle_comm(type, data) end end end end # This provisions using WinRM, which assumes a PowerShell # console on the other side. def provision_winrm(args) if @machine.guest.capability?(:wait_for_reboot) @machine.guest.capability(:wait_for_reboot) end with_script_file do |path| @machine.communicate.tap do |comm| # Make sure that the upload path has an extension, since # having an extension is critical for Windows execution upload_path = config.upload_path.to_s if File.extname(upload_path) == "" upload_path += File.extname(path.to_s) end # Upload it comm.upload(path.to_s, upload_path) # Build the environment env = config.env.map { |k,v| "$env:#{k} = #{quote_and_escape(v.to_s)}" } env = env.join("; ") # Calculate the path that we'll be executing exec_path = upload_path exec_path.gsub!('/', '\\') exec_path = "c:#{exec_path}" if exec_path.start_with?("\\") # Copy powershell_args from configuration shell_args = config.powershell_args # For PowerShell scripts bypass the execution policy unless already specified shell_args += " -ExecutionPolicy Bypass" if config.powershell_args !~ /[-\/]ExecutionPolicy/i # CLIXML output is kinda useless, especially on non-windows hosts shell_args += " -OutputFormat Text" if config.powershell_args !~ /[-\/]OutputFormat/i command = "\"#{exec_path}\"#{args}" if File.extname(exec_path).downcase == ".ps1" command = "powershell #{shell_args.to_s} -file #{command}" else command = "cmd /q /c #{command}" end # Append the environment if !env.empty? command = "#{env}; #{command}" end if config.name @machine.ui.detail(I18n.t("vagrant.provisioners.shell.running", script: "script: #{config.name}")) elsif config.path @machine.ui.detail(I18n.t("vagrant.provisioners.shell.runningas", local: config.path.to_s, remote: exec_path)) else @machine.ui.detail(I18n.t("vagrant.provisioners.shell.running", script: "inline PowerShell script")) end # Execute it with sudo comm.sudo(command, { elevated: config.privileged, interactive: config.powershell_elevated_interactive }) do |type, data| handle_comm(type, data) end end end end # Quote and escape strings for shell execution, thanks to Capistrano. def quote_and_escape(text, quote = '"') "#{quote}#{text.gsub(/#{quote}/) { |m| "#{m}\\#{m}#{m}" }}#{quote}" end # This method yields the path to a script to upload and execute # on the remote server. This method will properly clean up the # script file if needed. def with_script_file ext = nil script = nil if config.remote? download_path = @machine.env.tmp_path.join( "#{@machine.id}-remote-script") download_path.delete if download_path.file? begin Vagrant::Util::Downloader.new( config.path, download_path, md5: config.md5, sha1: config.sha1 ).download! ext = File.extname(config.path) script = download_path.read ensure download_path.delete if download_path.file? end elsif config.path # Just yield the path to that file... root_path = @machine.env.root_path ext = File.extname(config.path) script = Pathname.new(config.path).expand_path(root_path).read else # The script is just the inline code... ext = ".ps1" script = config.inline end # Replace Windows line endings with Unix ones unless binary file # or we're running on Windows. if !config.binary && @machine.config.vm.communicator != :winrm begin script = script.gsub(/\r\n?$/, "\n") rescue ArgumentError script = script.force_encoding("ASCII-8BIT").gsub(/\r\n?$/, "\n") end end # Otherwise we have an inline script, we need to Tempfile it, # and handle it specially... file = Tempfile.new(['vagrant-shell', ext]) # Unless you set binmode, on a Windows host the shell script will # have CRLF line endings instead of LF line endings, causing havoc # when the guest executes it. This fixes [GH-1181]. file.binmode begin file.write(script) file.fsync file.close yield file.path ensure file.close file.unlink end end end end end vagrant-2.0.2/plugins/pushes/000077500000000000000000000000001323370221500161435ustar00rootroot00000000000000vagrant-2.0.2/plugins/pushes/atlas/000077500000000000000000000000001323370221500172475ustar00rootroot00000000000000vagrant-2.0.2/plugins/pushes/atlas/config.rb000066400000000000000000000107211323370221500210420ustar00rootroot00000000000000module VagrantPlugins module AtlasPush class Config < Vagrant.plugin("2", :config) # The address of the Atlas server to upload to. By default this will # be the public Atlas server. # # @return [String] attr_accessor :address # The Atlas token to use. If the user has run `vagrant login`, this will # use that token. If the environment variable `ATLAS_TOKEN` is set, the # uploader will use this value. By default, this is nil. # # @return [String, nil] attr_accessor :token # The name of the application to push to. This will be created (with # user confirmation) if it doesn't already exist. # # @return [String] attr_accessor :app # The base directory with file contents to upload. By default this # is the same directory as the Vagrantfile, but you can specify this # if you have a `src` folder or `bin` folder or some other folder # you want to upload. # # @return [String] attr_accessor :dir # Lists of files to include/exclude in what is uploaded. Exclude is # always the last run filter, so if a file is matched in both include # and exclude, it will be excluded. # # The value of the array elements should be a simple file glob relative # to the directory being packaged. # # @return [Array] attr_accessor :includes attr_accessor :excludes # If set to true, Vagrant will automatically use VCS data to determine # the files to upload. As a caveat: uncommitted changes will not be # deployed. # # @return [Boolean] attr_accessor :vcs # The path to the uploader binary to shell out to. This usually # is only set for debugging/development. If not set, the uploader # will be looked for within the Vagrant installer dir followed by # the PATH. # # @return [String] attr_accessor :uploader_path def initialize @address = UNSET_VALUE @token = UNSET_VALUE @app = UNSET_VALUE @dir = UNSET_VALUE @vcs = UNSET_VALUE @includes = [] @excludes = [] @uploader_path = UNSET_VALUE end def merge(other) super.tap do |result| result.includes = self.includes.dup.concat(other.includes).uniq result.excludes = self.excludes.dup.concat(other.excludes).uniq end end def finalize! @address = nil if @address == UNSET_VALUE @token = nil if @token == UNSET_VALUE @token = ENV["ATLAS_TOKEN"] if !@token && ENV["ATLAS_TOKEN"] != "" @app = nil if @app == UNSET_VALUE @dir = "." if @dir == UNSET_VALUE @uploader_path = nil if @uploader_path == UNSET_VALUE @vcs = true if @vcs == UNSET_VALUE end def validate(machine) errors = _detected_errors if missing?(@token) token = token_from_vagrant_login(machine.env) if missing?(token) errors << I18n.t("atlas_push.errors.missing_token") else @token = token end end if missing?(@app) errors << I18n.t("atlas_push.errors.missing_attribute", attribute: "app", ) end if missing?(@dir) errors << I18n.t("atlas_push.errors.missing_attribute", attribute: "dir", ) end { "Atlas push" => errors } end # Add the filepath to the list of includes # @param [String] filepath def include(filepath) @includes << filepath end alias_method :include=, :include # Add the filepath to the list of excludes # @param [String] filepath def exclude(filepath) @excludes << filepath end alias_method :exclude=, :exclude private # Determine if the given string is "missing" (blank) # @return [true, false] def missing?(obj) obj.to_s.strip.empty? end # Attempt to load the token from disk using the vagrant-login plugin. If # the constant is not defined, that means the user is operating in some # bespoke and unsupported Ruby environment. # # @param [Vagrant::Environment] env # # @return [String, nil] # the token, or nil if it does not exist def token_from_vagrant_login(env) client = VagrantPlugins::LoginCommand::Client.new(env) client.token end end end end vagrant-2.0.2/plugins/pushes/atlas/errors.rb000066400000000000000000000004151323370221500211100ustar00rootroot00000000000000module VagrantPlugins module AtlasPush module Errors class Error < Vagrant::Errors::VagrantError error_namespace("atlas_push.errors") end class UploaderNotFound < Error error_key(:uploader_not_found) end end end end vagrant-2.0.2/plugins/pushes/atlas/locales/000077500000000000000000000000001323370221500206715ustar00rootroot00000000000000vagrant-2.0.2/plugins/pushes/atlas/locales/en.yml000066400000000000000000000017271323370221500220250ustar00rootroot00000000000000en: atlas_push: errors: missing_attribute: |- Missing required attribute '%{attribute}'. The Vagrant Atlas Push plugin requires you set this attribute. Please set this attribute in your Vagrantfile, for example: config.push.define "atlas" do |push| push.%{attribute} = "..." end missing_token: |- Missing required configuration parameter 'token'. This is required for Vagrant to securely communicate with your Atlas account. To generate an access token, run 'vagrant login'. uploader_not_found: |- Vagrant was unable to find the Atlas uploader CLI. If your Vagrantfile specifies the path explicitly with "uploader_path", then make sure that path is valid. Otherwise, make sure that you have a valid install of Vagrant. If you installed Vagrant outside of the official installers, the "atlas-upload" binary must exist on your PATH. vagrant-2.0.2/plugins/pushes/atlas/plugin.rb000066400000000000000000000012501323370221500210700ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module AtlasPush autoload :Errors, File.expand_path("../errors", __FILE__) class Plugin < Vagrant.plugin("2") name "atlas" description <<-DESC Deploy using HashiCorp's Atlas service. DESC config(:atlas, :push) do require_relative "config" init! Config end push(:atlas) do require_relative "push" init! Push end protected def self.init! return if defined?(@_init) I18n.load_path << File.expand_path("../locales/en.yml", __FILE__) I18n.reload! @_init = true end end end end vagrant-2.0.2/plugins/pushes/atlas/push.rb000066400000000000000000000046321323370221500205600ustar00rootroot00000000000000require "vagrant/util/safe_exec" require "vagrant/util/subprocess" require "vagrant/util/which" module VagrantPlugins module AtlasPush class Push < Vagrant.plugin("2", :push) UPLOADER_BIN = "atlas-upload".freeze def push uploader = self.uploader_path # If we didn't find the uploader binary it is a critical error raise Errors::UploaderNotFound if !uploader # We found it. Build up the command and the args. execute(uploader) return 0 end # Executes the uploader with the proper flags based on the configuration. # This function shouldn't return since it will exec, but might return # if we're on a system that doesn't support exec, so handle that properly. def execute(uploader) cmd = [] cmd << "-debug" if !Vagrant.log_level.nil? cmd << "-vcs" if config.vcs cmd += config.includes.map { |v| ["-include", v] } cmd += config.excludes.map { |v| ["-exclude", v] } cmd += metadata.map { |k,v| ["-metadata", "#{k}=#{v}"] } cmd += ["-address", config.address] if config.address cmd += ["-token", config.token] if config.token cmd << config.app cmd << File.expand_path(config.dir, env.root_path) Vagrant::Util::SafeExec.exec(uploader, *cmd.flatten) end # This returns the path to the uploader binary, or nil if it can't # be found. # # @return [String] def uploader_path # Determine the uploader path if uploader = config.uploader_path return uploader end if Vagrant.in_installer? path = File.join( Vagrant.installer_embedded_dir, "bin", UPLOADER_BIN) return path if File.file?(path) end return Vagrant::Util::Which.which(UPLOADER_BIN) end # The metadata command for this push. # # @return [Array] def metadata box = env.vagrantfile.config.vm.box box_url = env.vagrantfile.config.vm.box_url result = {} if !box.nil? && !box.empty? result["box"] = box end if !box_url.nil? && !box_url.empty? result["box_url"] = Array(box_url).first end return result end include Vagrant::Util::CommandDeprecation::Complete def deprecation_command_name "push (atlas strategy)" end end end end vagrant-2.0.2/plugins/pushes/ftp/000077500000000000000000000000001323370221500167345ustar00rootroot00000000000000vagrant-2.0.2/plugins/pushes/ftp/adapter.rb000066400000000000000000000060001323370221500206750ustar00rootroot00000000000000require "pathname" module VagrantPlugins module FTPPush class Adapter attr_reader :host attr_reader :port attr_reader :username attr_reader :password attr_reader :options attr_reader :server def initialize(host, username, password, options = {}) @host, @port = parse_host(host) @username = username @password = password @options = options @server = nil end # Parse the host into it's url and port parts. # @return [Array] def parse_host(host) if host.include?(":") split = host.split(":", 2) [split[0], split[1].to_i] else [host, default_port] end end def default_port raise NotImplementedError end def connect(&block) raise NotImplementedError end def upload(local, remote) raise NotImplementedError end end # # The FTP Adapter # class FTPAdapter < Adapter def initialize(*) require "net/ftp" super end def default_port 21 end def connect(&block) @server = Net::FTP.new @server.passive = options.fetch(:passive, true) @server.connect(host, port) @server.login(username, password) begin yield self ensure @server.close end end def upload(local, remote) parent = File.dirname(remote) fullpath = Pathname.new(File.expand_path(parent, pwd)) # Create the parent directories if they does not exist (naive mkdir -p) fullpath.descend do |path| if !directory_exists?(path.to_s) @server.mkdir(path.to_s) end end # Upload the file @server.putbinaryfile(local, remote) end def directory_exists?(path) begin @server.chdir(path) return true rescue Net::FTPPermError return false end end private def pwd @pwd ||= @server.pwd end end # # The SFTP Adapter # class SFTPAdapter < Adapter def initialize(*) require "net/sftp" super @dirs = {} end def default_port 22 end def connect(&block) Net::SFTP.start(@host, @username, password: @password, port: @port) do |server| @server = server yield self end end def upload(local, remote) dir = File.dirname(remote) fullpath = Pathname.new(dir) fullpath.descend do |path| if @dirs[path.to_s].nil? begin @server.mkdir!(path.to_s) # Cache visited directories in a list to avoid duplicate requests @dirs[path.to_s] = true rescue Net::SFTP::StatusException => e # Directory exists, skip... end end end @server.upload!(local, remote) end end end end vagrant-2.0.2/plugins/pushes/ftp/config.rb000066400000000000000000000071331323370221500205320ustar00rootroot00000000000000module VagrantPlugins module FTPPush class Config < Vagrant.plugin("2", :config) # The (S)FTP host to use. # @return [String] attr_accessor :host # The username to use for authentication with the (S)FTP server. # @return [String] attr_accessor :username # The password to use for authentication with the (S)FTP server. # @return [String] attr_accessor :password # Use passive FTP (default is true). # @return [true, false] attr_accessor :passive # Use secure (SFTP) (default is false). # @return [true, false] attr_accessor :secure # The root destination on the target system to sync the files (default is # /). # @return [String] attr_accessor :destination # Lists of files to include/exclude in what is uploaded. Exclude is # always the last run filter, so if a file is matched in both include # and exclude, it will be excluded. # # The value of the array elements should be a simple file glob relative # to the directory being packaged. # @return [Array] attr_accessor :includes attr_accessor :excludes # The base directory with file contents to upload. By default this # is the same directory as the Vagrantfile, but you can specify this # if you have a `src` folder or `bin` folder or some other folder # you want to upload. # @return [String] attr_accessor :dir def initialize @host = UNSET_VALUE @username = UNSET_VALUE @password = UNSET_VALUE @passive = UNSET_VALUE @secure = UNSET_VALUE @destination = UNSET_VALUE @includes = [] @excludes = [] @dir = UNSET_VALUE end def merge(other) super.tap do |result| result.includes = self.includes.dup.concat(other.includes).uniq result.excludes = self.excludes.dup.concat(other.excludes).uniq end end def finalize! @host = nil if @host == UNSET_VALUE @username = nil if @username == UNSET_VALUE @password = nil if @password == UNSET_VALUE @passive = true if @passive == UNSET_VALUE @secure = false if @secure == UNSET_VALUE @destination = "/" if @destination == UNSET_VALUE @dir = "." if @dir == UNSET_VALUE end def validate(machine) errors = _detected_errors if missing?(@host) errors << I18n.t("ftp_push.errors.missing_attribute", attribute: "host", ) end if missing?(@username) errors << I18n.t("ftp_push.errors.missing_attribute", attribute: "username", ) end if missing?(@destination) errors << I18n.t("ftp_push.errors.missing_attribute", attribute: "destination", ) end if missing?(@dir) errors << I18n.t("ftp_push.errors.missing_attribute", attribute: "dir", ) end { "FTP push" => errors } end # Add the filepath to the list of includes # @param [String] filepath def include(filepath) @includes << filepath end alias_method :include=, :include # Add the filepath to the list of excludes # @param [String] filepath def exclude(filepath) @excludes << filepath end alias_method :exclude=, :exclude private # Determine if the given string is "missing" (blank) # @return [true, false] def missing?(obj) obj.to_s.strip.empty? end end end end vagrant-2.0.2/plugins/pushes/ftp/locales/000077500000000000000000000000001323370221500203565ustar00rootroot00000000000000vagrant-2.0.2/plugins/pushes/ftp/locales/en.yml000066400000000000000000000005341323370221500215050ustar00rootroot00000000000000en: ftp_push: errors: missing_attribute: |- Missing required attribute '%{attribute}'. The Vagrant FTP Push plugin requires you set this attribute. Please set this attribute in your Vagrantfile, for example: config.push.define "ftp" do |push| push.%{attribute} = "..." end vagrant-2.0.2/plugins/pushes/ftp/plugin.rb000066400000000000000000000012141323370221500205550ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module FTPPush class Plugin < Vagrant.plugin("2") name "ftp" description <<-DESC Deploy to a remote FTP or SFTP server. DESC config(:ftp, :push) do require File.expand_path("../config", __FILE__) init! Config end push(:ftp) do require File.expand_path("../push", __FILE__) init! Push end protected def self.init! return if defined?(@_init) I18n.load_path << File.expand_path("../locales/en.yml", __FILE__) I18n.reload! @_init = true end end end end vagrant-2.0.2/plugins/pushes/ftp/push.rb000066400000000000000000000073771323370221500202560ustar00rootroot00000000000000require "net/ftp" require "pathname" require_relative "adapter" module VagrantPlugins module FTPPush class Push < Vagrant.plugin("2", :push) IGNORED_FILES = %w(. ..).freeze DEFAULT_EXCLUDES = %w(.git .hg .svn .vagrant).freeze def initialize(*) super @logger = Log4r::Logger.new("vagrant::pushes::ftp") end def push # Grab files early so if there's an exception or issue, we don't have to # wait and close the (S)FTP connection as well files = Hash[*all_files.flat_map do |file| relative_path = relative_path_for(file, config.dir) destination = File.join(config.destination, relative_path) file = File.expand_path(file, env.root_path) [file, destination] end] ftp = "#{config.username}@#{config.host}:#{config.destination}" env.ui.info "Uploading #{env.root_path} to #{ftp}" connect do |ftp| files.each do |local, remote| @logger.info "Uploading #{local} => #{remote}" ftp.upload(local, remote) end end end # Helper method for creating the FTP or SFTP connection. # @yield [Adapter] def connect(&block) klass = config.secure ? SFTPAdapter : FTPAdapter ftp = klass.new(config.host, config.username, config.password, passive: config.passive) ftp.connect(&block) end # The list of all files that should be pushed by this push. This method # only returns **files**, not folders or symlinks! # @return [Array] def all_files files = glob("#{config.dir}/**/*") + includes_files filter_excludes!(files, config.excludes) files.reject! { |f| !File.file?(f) } files end # The list of files to include in addition to those specified in `dir`. # @return [Array] def includes_files includes = config.includes.flat_map do |i| path = absolute_path_for(i, config.dir) [path, "#{path}/**/*"] end glob("{#{includes.join(",")}}") end # Filter the excludes out of the given list. This method modifies the # given list in memory! # # @param [Array] list # the filepaths # @param [Array] excludes # the exclude patterns or files def filter_excludes!(list, excludes) excludes = Array(excludes) excludes = excludes + DEFAULT_EXCLUDES excludes = excludes.flat_map { |e| [e, "#{e}/*"] } list.reject! do |file| basename = relative_path_for(file, config.dir) # Handle the special case where the file is outside of the working # directory... if basename.start_with?("../") basename = file end excludes.any? { |e| File.fnmatch?(e, basename, File::FNM_DOTMATCH) } end end # Get the list of files that match the given pattern. # @return [Array] def glob(pattern) Dir.glob(pattern, File::FNM_DOTMATCH).sort.reject do |file| IGNORED_FILES.include?(File.basename(file)) end end # The absolute path to the given `path` and `parent`, unless the given # path is absolute. # @return [String] def absolute_path_for(path, parent) path = Pathname.new(path) return path if path.absolute? File.expand_path(path, parent) end # The relative path from the given `parent`. If files exist on another # device, this will probably blow up. # @return [String] def relative_path_for(path, parent) Pathname.new(path).relative_path_from(Pathname.new(parent)).to_s rescue ArgumentError return path end end end end vagrant-2.0.2/plugins/pushes/heroku/000077500000000000000000000000001323370221500174405ustar00rootroot00000000000000vagrant-2.0.2/plugins/pushes/heroku/config.rb000066400000000000000000000037211323370221500212350ustar00rootroot00000000000000module VagrantPlugins module HerokuPush class Config < Vagrant.plugin("2", :config) # The name of the Heroku application to push to. # @return [String] attr_accessor :app # The base directory with file contents to upload. By default this # is the same directory as the Vagrantfile, but you can specify this # if you have a `src` folder or `bin` folder or some other folder # you want to upload. This directory must be a git repository. # @return [String] attr_accessor :dir # The path to the git binary to shell out to. This usually is only set for # debugging/development. If not set, the git bin will be searched for # in the PATH. # @return [String] attr_accessor :git_bin # The Git remote to push to (default: "heroku"). # @return [String] attr_accessor :remote def initialize @app = UNSET_VALUE @dir = UNSET_VALUE @git_bin = UNSET_VALUE @remote = UNSET_VALUE end def finalize! @app = nil if @app == UNSET_VALUE @dir = "." if @dir == UNSET_VALUE @git_bin = "git" if @git_bin == UNSET_VALUE @remote = "heroku" if @remote == UNSET_VALUE end def validate(machine) errors = _detected_errors if missing?(@dir) errors << I18n.t("heroku_push.errors.missing_attribute", attribute: "dir", ) end if missing?(@git_bin) errors << I18n.t("heroku_push.errors.missing_attribute", attribute: "git_bin", ) end if missing?(@remote) errors << I18n.t("heroku_push.errors.missing_attribute", attribute: "remote", ) end { "Heroku push" => errors } end private # Determine if the given string is "missing" (blank) # @return [true, false] def missing?(obj) obj.to_s.strip.empty? end end end end vagrant-2.0.2/plugins/pushes/heroku/errors.rb000066400000000000000000000006431323370221500213040ustar00rootroot00000000000000module VagrantPlugins module HerokuPush module Errors class Error < Vagrant::Errors::VagrantError error_namespace("heroku_push.errors") end class CommandFailed < Error error_key(:command_failed) end class GitNotFound < Error error_key(:git_not_found) end class NotAGitRepo < Error error_key(:not_a_git_repo) end end end end vagrant-2.0.2/plugins/pushes/heroku/locales/000077500000000000000000000000001323370221500210625ustar00rootroot00000000000000vagrant-2.0.2/plugins/pushes/heroku/locales/en.yml000066400000000000000000000017241323370221500222130ustar00rootroot00000000000000en: heroku_push: errors: command_failed: |- The following command exited with a non-zero exit status: %{cmd} stdout: %{stdout} stderr: %{stderr} git_not_found: |- The Git binary '%{bin}' could not be found. Please ensure you have downloaded and installed the latest version of Git: https://git-scm.com/downloads missing_attribute: |- Missing required attribute '%{attribute}'. The Vagrant Heroku Push plugin requires you set this attribute. Please set this attribute in your Vagrantfile, for example: config.push.define "heroku" do |push| push.%{attribute} = "..." end not_a_git_repo: |- The following path is not a valid Git repository: %{path} Please ensure you are working in the correct directory. In order to use the Vagrant Heroku Push plugin, you must have a git repository. vagrant-2.0.2/plugins/pushes/heroku/plugin.rb000066400000000000000000000012041323370221500212600ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HerokuPush class Plugin < Vagrant.plugin("2") name "heroku" description <<-DESC Deploy to a Heroku DESC config(:heroku, :push) do require File.expand_path("../config", __FILE__) init! Config end push(:heroku) do require File.expand_path("../push", __FILE__) init! Push end protected def self.init! return if defined?(@_init) I18n.load_path << File.expand_path("../locales/en.yml", __FILE__) I18n.reload! @_init = true end end end end vagrant-2.0.2/plugins/pushes/heroku/push.rb000066400000000000000000000071461323370221500207540ustar00rootroot00000000000000require "vagrant/util/subprocess" require "vagrant/util/which" require_relative "errors" module VagrantPlugins module HerokuPush class Push < Vagrant.plugin("2", :push) def push # Expand any paths relative to the root dir = File.expand_path(config.dir, env.root_path) # Verify git is installed verify_git_bin!(config.git_bin) # Verify we are operating in a git repo verify_git_repo!(dir) # Get the current branch branch = git_branch(dir) # Get the name of the app app = config.app || interpret_app(dir) # Check if we need to add the git remote if !has_git_remote?(config.remote, dir) add_heroku_git_remote(config.remote, app, dir) end # Push to Heroku git_push_heroku(config.remote, branch, dir) end # Verify that git is installed. # @raise [Errors::GitNotFound] def verify_git_bin!(path) if Vagrant::Util::Which.which(path).nil? raise Errors::GitNotFound, bin: path end end # Verify that the given path is a git directory. # @raise [Errors::NotAGitRepo] # @param [String] def verify_git_repo!(path) if !File.directory?(git_dir(path)) raise Errors::NotAGitRepo, path: path end end # Interpret the name of the Heroku application from the given path. # @param [String] path # @return [String] def interpret_app(path) File.basename(path) end # The git directory for the given path. # @param [String] path # @return [String] def git_dir(path) "#{path}/.git" end # The name of the current git branch. # @param [String] path # @return [String] def git_branch(path) result = execute!("git", "--git-dir", git_dir(path), "--work-tree", path, "symbolic-ref", "HEAD", ) # Returns something like "* master" result.stdout.sub("*", "").strip end # Push to the Heroku remote. # @param [String] remote # @param [String] branch def git_push_heroku(remote, branch, path) execute!("git", "--git-dir", git_dir(path), "--work-tree", path, "push", remote, "#{branch}:master", ) end # Check if the git remote has the given remote. # @param [String] remote # @return [true, false] def has_git_remote?(remote, path) result = execute!("git", "--git-dir", git_dir(path), "--work-tree", path, "remote", ) remotes = result.stdout.split(/\r?\n/).map(&:strip) remotes.include?(remote.to_s) end # Add the Heroku to the current repository. # @param [String] remote # @param [String] app def add_heroku_git_remote(remote, app, path) execute!("git", "--git-dir", git_dir(path), "--work-tree", path, "remote", "add", remote, heroku_git_url(app), ) end # The URL for this project on Heroku. # @return [String] def heroku_git_url(app) "git@heroku.com:#{app}.git" end # Execute the command, raising an exception if it fails. # @return [Vagrant::Util::Subprocess::Result] def execute!(*cmd) result = Vagrant::Util::Subprocess.execute(*cmd) if result.exit_code != 0 raise Errors::CommandFailed, cmd: cmd.join(" "), stdout: result.stdout, stderr: result.stderr end result end end end end vagrant-2.0.2/plugins/pushes/local-exec/000077500000000000000000000000001323370221500201575ustar00rootroot00000000000000vagrant-2.0.2/plugins/pushes/local-exec/config.rb000066400000000000000000000037761323370221500217660ustar00rootroot00000000000000module VagrantPlugins module LocalExecPush class Config < Vagrant.plugin("2", :config) # The path (relative to the machine root) to a local script that will be # executed. # @return [String] attr_accessor :script # The command (as a string) to execute. # @return [String] attr_accessor :inline # The arguments to provide when executing the script. # @return [Array] attr_accessor :args def initialize @script = UNSET_VALUE @inline = UNSET_VALUE @args = UNSET_VALUE end def finalize! @script = nil if @script == UNSET_VALUE @inline = nil if @inline == UNSET_VALUE @args = nil if @args == UNSET_VALUE if @args && args_valid? @args = @args.is_a?(Array) ? @args.map { |a| a.to_s } : @args.to_s end end def validate(machine) errors = _detected_errors if missing?(@script) && missing?(@inline) errors << I18n.t("local_exec_push.errors.missing_attribute", attribute: "script", ) end if !missing?(@script) && !missing?(@inline) errors << I18n.t("local_exec_push.errors.cannot_specify_script_and_inline") end if !args_valid? errors << I18n.t("local_exec_push.errors.args_bad_type") end { "Local Exec push" => errors } end private # Determine if the given string is "missing" (blank) # @return [true, false] def missing?(obj) obj.to_s.strip.empty? end # Args are optional, but if they're provided we only support them as a # string or as an array. def args_valid? return true if !args return true if args.is_a?(String) return true if args.is_a?(Integer) if args.is_a?(Array) args.each do |a| return false if !a.kind_of?(String) && !a.kind_of?(Integer) end return true end end end end end vagrant-2.0.2/plugins/pushes/local-exec/errors.rb000066400000000000000000000004171323370221500220220ustar00rootroot00000000000000module VagrantPlugins module LocalExecPush module Errors class Error < Vagrant::Errors::VagrantError error_namespace("local_exec_push.errors") end class CommandFailed < Error error_key(:command_failed) end end end end vagrant-2.0.2/plugins/pushes/local-exec/locales/000077500000000000000000000000001323370221500216015ustar00rootroot00000000000000vagrant-2.0.2/plugins/pushes/local-exec/locales/en.yml000066400000000000000000000014631323370221500227320ustar00rootroot00000000000000en: local_exec_push: errors: cannot_specify_script_and_inline: |- You have specified both the 'script' and 'inline' attributes for the Vagrant Local Exec Push plugin. You may only specify one of these attributes. command_failed: |- The following command exited with a non-zero exit status: %{cmd} stdout: %{stdout} stderr: %{stderr} missing_attribute: |- Missing required attribute '%{attribute}'. The Vagrant Local Exec Push plugin requires you set this attribute. Please set this attribute in your Vagrantfile, for example: config.push.define "local-exec" do |push| push.%{attribute} = "..." end args_bad_type: "Local-exec push `args` must be a string or array."vagrant-2.0.2/plugins/pushes/local-exec/plugin.rb000066400000000000000000000012521323370221500220020ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module LocalExecPush class Plugin < Vagrant.plugin("2") name "local-exec" description <<-DESC Run a local command or script to push DESC config(:"local-exec", :push) do require File.expand_path("../config", __FILE__) init! Config end push(:"local-exec") do require File.expand_path("../push", __FILE__) init! Push end protected def self.init! return if defined?(@_init) I18n.load_path << File.expand_path("../locales/en.yml", __FILE__) I18n.reload! @_init = true end end end end vagrant-2.0.2/plugins/pushes/local-exec/push.rb000066400000000000000000000043641323370221500214720ustar00rootroot00000000000000require "fileutils" require "tempfile" require "vagrant/util/safe_exec" require_relative "errors" module VagrantPlugins module LocalExecPush class Push < Vagrant.plugin("2", :push) def push if config.inline execute_inline!(config.inline, config.args) else execute_script!(config.script, config.args) end end # Execute the inline script by writing it to a tempfile and executing. def execute_inline!(inline, args) script = Tempfile.new(["vagrant-local-exec-script", ".sh"]) script.write(inline) script.rewind script.close execute_script!(script.path, args) ensure if script script.close script.unlink end end # Execute the script, expanding the path relative to the current env root. def execute_script!(path, args) path = File.expand_path(path, env.root_path) FileUtils.chmod("+x", path) if args.is_a?(String) args = " #{args.to_s}" elsif args.is_a?(Array) args = args.map { |a| quote_and_escape(a) } args = " #{args.join(" ")}" end execute!("#{path}#{args}") end # Execute the script, raising an exception if it fails. def execute!(*cmd) if Vagrant::Util::Platform.windows? execute_subprocess!(*cmd) else execute_exec!(*cmd) end end private # Quote and escape strings for shell execution, thanks to Capistrano. def quote_and_escape(text, quote = '"') "#{quote}#{text.gsub(/#{quote}/) { |m| "#{m}\\#{m}#{m}" }}#{quote}" end # Run the command as exec (unix). def execute_exec!(*cmd) Vagrant::Util::SafeExec.exec(cmd[0], *cmd[1..-1]) end # Run the command as a subprocess (windows). def execute_subprocess!(*cmd) cmd = cmd.dup << { notify: [:stdout, :stderr] } result = Vagrant::Util::Subprocess.execute(*cmd) do |type, data| if type == :stdout @env.ui.info(data, new_line: false) elsif type == :stderr @env.ui.warn(data, new_line: false) end end Kernel.exit(result.exit_code) end end end end vagrant-2.0.2/plugins/pushes/noop/000077500000000000000000000000001323370221500171165ustar00rootroot00000000000000vagrant-2.0.2/plugins/pushes/noop/config.rb000066400000000000000000000004251323370221500207110ustar00rootroot00000000000000module VagrantPlugins module NoopDeploy class Config < Vagrant.plugin("2", :config) def initialize end def finalize! end def validate(machine) errors = _detected_errors { "Noop push" => errors } end end end end vagrant-2.0.2/plugins/pushes/noop/plugin.rb000066400000000000000000000006331323370221500207430ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module NoopDeploy class Plugin < Vagrant.plugin("2") name "noop" description <<-DESC Literally do nothing DESC config(:noop, :push) do require File.expand_path("../config", __FILE__) Config end push(:noop) do require File.expand_path("../push", __FILE__) Push end end end end vagrant-2.0.2/plugins/pushes/noop/push.rb000066400000000000000000000002271323370221500204230ustar00rootroot00000000000000module VagrantPlugins module NoopDeploy class Push < Vagrant.plugin("2", :push) def push puts "pushed" end end end end vagrant-2.0.2/plugins/synced_folders/000077500000000000000000000000001323370221500176375ustar00rootroot00000000000000vagrant-2.0.2/plugins/synced_folders/nfs/000077500000000000000000000000001323370221500204255ustar00rootroot00000000000000vagrant-2.0.2/plugins/synced_folders/nfs/action_cleanup.rb000066400000000000000000000014331323370221500237370ustar00rootroot00000000000000require "log4r" module VagrantPlugins module SyncedFolderNFS class ActionCleanup def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::synced_folders::nfs") end def call(env) if !env[:nfs_valid_ids] @logger.warn("nfs_valid_ids not set, cleanup cannot occur") return @app.call(env) end if !env[:machine].env.host.capability?(:nfs_prune) @logger.info("Host doesn't support pruning NFS. Skipping.") return @app.call(env) end @logger.info("NFS pruning. Valid IDs: #{env[:nfs_valid_ids].inspect}") env[:machine].env.host.capability( :nfs_prune, env[:machine].ui, env[:nfs_valid_ids]) @app.call(env) end end end end vagrant-2.0.2/plugins/synced_folders/nfs/config.rb000066400000000000000000000013561323370221500222240ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module SyncedFolderNFS class Config < Vagrant.plugin("2", :config) attr_accessor :functional attr_accessor :map_uid attr_accessor :map_gid attr_accessor :verify_installed def initialize super @functional = UNSET_VALUE @map_uid = UNSET_VALUE @map_gid = UNSET_VALUE @verify_installed = UNSET_VALUE end def finalize! @functional = true if @functional == UNSET_VALUE @map_uid = :auto if @map_uid == UNSET_VALUE @map_gid = :auto if @map_gid == UNSET_VALUE @verify_installed = true if @verify_installed == UNSET_VALUE end def to_s "NFS" end end end end vagrant-2.0.2/plugins/synced_folders/nfs/plugin.rb000066400000000000000000000027221323370221500222530ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module SyncedFolderNFS # This plugin implements NFS synced folders. In order to take advantage # of NFS synced folders, some provider-specific assistance is required. # Within the middleware sequences, some data must be put into the # environment state bag: # # * `nfs_host_ip` (string) - The IP of the host machine that the NFS # client in the machine should talk to. # * `nfs_machine_ip` (string) - The IP of the guest machine that the NFS # server should serve the folders to. # * `nfs_valid_ids` (array of strings) - A list of IDs that are "valid" # and should not be pruned. The synced folder implementation will # regularly prune NFS exports of invalid IDs. # # If any of these variables are not set, an internal exception will be # raised. # class Plugin < Vagrant.plugin("2") name "NFS synced folders" description <<-EOF The NFS synced folders plugin enables you to use NFS as a synced folder implementation. EOF config("nfs") do require_relative "config" Config end synced_folder("nfs", 5) do require_relative "synced_folder" SyncedFolder end action_hook("nfs_cleanup") do |hook| require_relative "action_cleanup" hook.before( Vagrant::Action::Builtin::SyncedFolderCleanup, ActionCleanup) end end end end vagrant-2.0.2/plugins/synced_folders/nfs/synced_folder.rb000066400000000000000000000123711323370221500235760ustar00rootroot00000000000000require 'fileutils' require 'thread' require 'zlib' require "log4r" require "vagrant/util/platform" module VagrantPlugins module SyncedFolderNFS # This synced folder requires that two keys be set on the environment # within the middleware sequence: # # - `:nfs_host_ip` - The IP of where to mount the NFS folder from. # - `:nfs_machine_ip` - The IP of the machine where the NFS folder # will be mounted. # class SyncedFolder < Vagrant.plugin("2", :synced_folder) @@lock = Mutex.new def initialize(*args) super @logger = Log4r::Logger.new("vagrant::synced_folders::nfs") end def usable?(machine, raise_error=false) # If the machine explicitly said NFS is not supported, then # it isn't supported. if !machine.config.nfs.functional return false end return true if machine.env.host.capability(:nfs_installed) return false if !raise_error raise Vagrant::Errors::NFSNotSupported end def prepare(machine, folders, opts) # Nothing is necessary to do before VM boot. end def enable(machine, folders, nfsopts) raise Vagrant::Errors::NFSNoHostIP if !nfsopts[:nfs_host_ip] raise Vagrant::Errors::NFSNoGuestIP if !nfsopts[:nfs_machine_ip] if machine.config.nfs.verify_installed if machine.guest.capability?(:nfs_client_installed) installed = machine.guest.capability(:nfs_client_installed) if !installed can_install = machine.guest.capability?(:nfs_client_install) raise Vagrant::Errors::NFSClientNotInstalledInGuest if !can_install machine.ui.info I18n.t("vagrant.actions.vm.nfs.installing") machine.guest.capability(:nfs_client_install) end end end machine_ip = nfsopts[:nfs_machine_ip] machine_ip = [machine_ip] if !machine_ip.is_a?(Array) # Prepare the folder, this means setting up various options # and such on the folder itself. folders.each { |id, opts| prepare_folder(machine, opts) } # Determine what folders we'll export export_folders = folders.dup export_folders.keys.each do |id| opts = export_folders[id] if opts.key?(:nfs_export) && !opts[:nfs_export] export_folders.delete(id) end end # Update the exports when there are actually exports [GH-4148] if !export_folders.empty? # Export the folders. We do this with a class-wide lock because # NFS exporting often requires sudo privilege and we don't want # overlapping input requests. [GH-2680] @@lock.synchronize do begin machine.env.lock("nfs-export") do machine.ui.info I18n.t("vagrant.actions.vm.nfs.exporting") machine.env.host.capability( :nfs_export, machine.ui, machine.id, machine_ip, export_folders) end rescue Vagrant::Errors::EnvironmentLockedError sleep 1 retry end end end # Mount machine.ui.info I18n.t("vagrant.actions.vm.nfs.mounting") # Only mount folders that have a guest path specified. mount_folders = {} folders.each do |id, opts| mount_folders[id] = opts.dup if opts[:guestpath] end # Mount them! if machine.guest.capability?(:nfs_pre) machine.guest.capability(:nfs_pre) end machine.guest.capability(:mount_nfs_folder, nfsopts[:nfs_host_ip], mount_folders) if machine.guest.capability?(:nfs_post) machine.guest.capability(:nfs_post) end end def cleanup(machine, opts) ids = opts[:nfs_valid_ids] raise Vagrant::Errors::NFSNoValidIds if !ids # Prune any of the unused machines @logger.info("NFS pruning. Valid IDs: #{ids.inspect}") machine.env.host.capability(:nfs_prune, machine.ui, ids) end protected def prepare_folder(machine, opts) opts[:map_uid] = prepare_permission(machine, :uid, opts) opts[:map_gid] = prepare_permission(machine, :gid, opts) opts[:nfs_udp] = true if !opts.key?(:nfs_udp) opts[:nfs_version] ||= 3 if opts[:nfs_version].to_s.start_with?('4') && opts[:nfs_udp] machine.ui.info I18n.t("vagrant.actions.vm.nfs.v4_with_udp_warning") end # We use a CRC32 to generate a 32-bit checksum so that the # fsid is compatible with both old and new kernels. opts[:uuid] = Zlib.crc32(opts[:hostpath]).to_s end # Prepares the UID/GID settings for a single folder. def prepare_permission(machine, perm, opts) key = "map_#{perm}".to_sym return nil if opts.key?(key) && opts[key].nil? # The options on the hash get priority, then the default # values value = opts.key?(key) ? opts[key] : machine.config.nfs.send(key) return value if value != :auto # Get UID/GID from folder if we've made it this far # (value == :auto) stat = File.stat(opts[:hostpath]) return stat.send(perm) end end end end vagrant-2.0.2/plugins/synced_folders/rsync/000077500000000000000000000000001323370221500207755ustar00rootroot00000000000000vagrant-2.0.2/plugins/synced_folders/rsync/command/000077500000000000000000000000001323370221500224135ustar00rootroot00000000000000vagrant-2.0.2/plugins/synced_folders/rsync/command/rsync.rb000066400000000000000000000042221323370221500240760ustar00rootroot00000000000000require 'optparse' require "vagrant/action/builtin/mixin_synced_folders" require_relative "../helper" module VagrantPlugins module SyncedFolderRSync module Command class Rsync < Vagrant.plugin("2", :command) include Vagrant::Action::Builtin::MixinSyncedFolders def self.synopsis "syncs rsync synced folders to remote machine" end def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant rsync [vm-name]" o.separator "" o.separator "This command forces any synced folders with type 'rsync' to sync." o.separator "RSync is not an automatic sync so a manual command is used." o.separator "" o.separator "Options:" o.separator "" end # Parse the options and return if we don't have any target. argv = parse_options(opts) return if !argv # Go through each machine and perform the rsync error = false with_target_vms(argv) do |machine| if machine.provider.capability?(:proxy_machine) proxy = machine.provider.capability(:proxy_machine) if proxy machine.ui.warn(I18n.t( "vagrant.rsync_proxy_machine", name: machine.name.to_s, provider: machine.provider_name.to_s)) machine = proxy end end if !machine.communicate.ready? machine.ui.error(I18n.t("vagrant.rsync_communicator_not_ready")) error = true next end # Determine the rsync synced folders for this machine folders = synced_folders(machine, cached: true)[:rsync] next if !folders || folders.empty? # Get the SSH info for this machine so we can access it ssh_info = machine.ssh_info # Sync them! folders.each do |id, folder_opts| RsyncHelper.rsync_single(machine, ssh_info, folder_opts) end end return error ? 1 : 0 end end end end end vagrant-2.0.2/plugins/synced_folders/rsync/command/rsync_auto.rb000066400000000000000000000203011323370221500251220ustar00rootroot00000000000000require "log4r" require 'optparse' require "thread" require "vagrant/action/builtin/mixin_synced_folders" require "vagrant/util/busy" require "vagrant/util/platform" require_relative "../helper" # This is to avoid a bug in nio 1.0.0. Remove around nio 1.0.1 if Vagrant::Util::Platform.windows? ENV["NIO4R_PURE"] = "1" end require "listen" module VagrantPlugins module SyncedFolderRSync module Command class RsyncAuto < Vagrant.plugin("2", :command) include Vagrant::Action::Builtin::MixinSyncedFolders def self.synopsis "syncs rsync synced folders automatically when files change" end def execute @logger = Log4r::Logger.new("vagrant::commands::rsync-auto") options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant rsync-auto [vm-name]" o.separator "" o.separator "Options:" o.separator "" o.on("--[no-]poll", "Force polling filesystem (slow)") do |poll| options[:poll] = poll end end # Parse the options and return if we don't have any target. argv = parse_options(opts) return if !argv # Build up the paths that we need to listen to. paths = {} ignores = [] with_target_vms(argv) do |machine| next if machine.state.id == :not_created cwd = machine.env.cwd.to_s if machine.provider.capability?(:proxy_machine) proxy = machine.provider.capability(:proxy_machine) if proxy machine.ui.warn(I18n.t( "vagrant.rsync_proxy_machine", name: machine.name.to_s, provider: machine.provider_name.to_s)) machine = proxy end end cached = synced_folders(machine, cached: true) fresh = synced_folders(machine) diff = synced_folders_diff(cached, fresh) if !diff[:added].empty? machine.ui.warn(I18n.t("vagrant.rsync_auto_new_folders")) end folders = cached[:rsync] next if !folders || folders.empty? # NOTE: This check is required with boot2docker since all containers # share the same virtual machine. This prevents rsync-auto from # syncing all known containers with rsync to the boot2docker vm # and only syncs the current working dirs folders. sync_folders = {} # Still sync existing synced folders from vagrantfile config_synced_folders = machine.config.vm.synced_folders.values.map { |x| x[:hostpath] } config_synced_folders.map! { |x| File.expand_path(x, machine.env.root_path) } folders.each do |id, folder_opts| if cwd != folder_opts[:hostpath] && !config_synced_folders.include?(folder_opts[:hostpath]) machine.ui.info(I18n.t("vagrant.rsync_auto_remove_folder", folder: folder_opts[:hostpath])) else sync_folders[id] = folder_opts end end folders = sync_folders # Get the SSH info for this machine so we can do an initial # sync to the VM. ssh_info = machine.ssh_info if ssh_info machine.ui.info(I18n.t("vagrant.rsync_auto_initial")) folders.each do |id, folder_opts| RsyncHelper.rsync_single(machine, ssh_info, folder_opts) end end folders.each do |id, folder_opts| # If we marked this folder to not auto sync, then # don't do it. next if folder_opts.key?(:auto) && !folder_opts[:auto] hostpath = folder_opts[:hostpath] hostpath = File.expand_path(hostpath, machine.env.root_path) paths[hostpath] ||= [] paths[hostpath] << { id: id, machine: machine, opts: folder_opts, } if folder_opts[:exclude] Array(folder_opts[:exclude]).each do |pattern| ignores << RsyncHelper.exclude_to_regexp(hostpath, pattern.to_s) end end end end # Exit immediately if there is nothing to watch if paths.empty? @env.ui.info(I18n.t("vagrant.rsync_auto_no_paths")) return 1 end # Output to the user what paths we'll be watching paths.keys.sort.each do |path| paths[path].each do |path_opts| path_opts[:machine].ui.info(I18n.t( "vagrant.rsync_auto_path", path: path.to_s, )) end end @logger.info("Listening to paths: #{paths.keys.sort.inspect}") @logger.info("Ignoring #{ignores.length} paths:") ignores.each do |ignore| @logger.info(" -- #{ignore.to_s}") end @logger.info("Listening via: #{Listen::Adapter.select.inspect}") callback = method(:callback).to_proc.curry[paths] listopts = { ignore: ignores, force_polling: !!options[:poll] } listener = Listen.to(*paths.keys, listopts, &callback) # Create the callback that lets us know when we've been interrupted queue = Queue.new callback = lambda do # This needs to execute in another thread because Thread # synchronization can't happen in a trap context. Thread.new { queue << true } end # Run the listener in a busy block so that we can cleanly # exit once we receive an interrupt. Vagrant::Util::Busy.busy(callback) do listener.start queue.pop listener.stop if listener.state != :stopped end 0 end # This is the callback that is called when any changes happen def callback(paths, modified, added, removed) @logger.info("File change callback called!") @logger.info(" - Modified: #{modified.inspect}") @logger.info(" - Added: #{added.inspect}") @logger.info(" - Removed: #{removed.inspect}") tosync = [] paths.each do |hostpath, folders| # Find out if this path should be synced found = catch(:done) do [modified, added, removed].each do |changed| changed.each do |listenpath| throw :done, true if listenpath.start_with?(hostpath) end end # Make sure to return false if all else fails so that we # don't sync to this machine. false end # If it should be synced, store it for later tosync << folders if found end # Sync all the folders that need to be synced tosync.each do |folders| folders.each do |opts| # Reload so we get the latest ID opts[:machine].reload if !opts[:machine].id || opts[:machine].id == "" # Skip since we can't get SSH info without an ID next end ssh_info = opts[:machine].ssh_info begin start = Time.now RsyncHelper.rsync_single(opts[:machine], ssh_info, opts[:opts]) finish = Time.now @logger.info("Time spent in rsync: #{finish-start} (in seconds)") rescue Vagrant::Errors::MachineGuestNotReady # Error communicating to the machine, probably a reload or # halt is happening. Just notify the user but don't fail out. opts[:machine].ui.error(I18n.t( "vagrant.rsync_communicator_not_ready_callback")) rescue Vagrant::Errors::RSyncError => e # Error executing rsync, so show an error opts[:machine].ui.error(I18n.t( "vagrant.rsync_auto_rsync_error", message: e.to_s)) end end end end end end end end vagrant-2.0.2/plugins/synced_folders/rsync/default_unix_cap.rb000066400000000000000000000025531323370221500246410ustar00rootroot00000000000000require "shellwords" module VagrantPlugins module SyncedFolderRSync # This module provides default rsync capabilities for # unix type operating systems. module DefaultUnixCap def rsync_installed(machine) machine.communicate.test("which rsync") end def rsync_command(machine) "sudo rsync" end def rsync_pre(machine, opts) guest_path = Shellwords.escape(opts[:guestpath]) machine.communicate.sudo("mkdir -p #{guest_path}") end def rsync_post(machine, opts) if opts.key?(:chown) && !opts[:chown] return end machine.communicate.sudo(build_rsync_chown(opts)) end def build_rsync_chown(opts) guest_path = Shellwords.escape(opts[:guestpath]) if(opts[:exclude]) 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) "-path #{Shellwords.escape(exclude_base.join(ex_path))} -prune" end.join(" -o ") + " -o " end "find #{guest_path} #{exclusions}" \ "'!' -type l -a " \ "'(' ! -user #{opts[:owner]} -or ! -group #{opts[:group]} ')' -exec " \ "chown #{opts[:owner]}:#{opts[:group]} '{}' +" end end end end vagrant-2.0.2/plugins/synced_folders/rsync/helper.rb000066400000000000000000000170041323370221500226030ustar00rootroot00000000000000require "ipaddr" require "shellwords" require "vagrant/util/platform" require "vagrant/util/subprocess" module VagrantPlugins module SyncedFolderRSync # This is a helper that abstracts out the functionality of rsyncing # folders so that it can be called from anywhere. class RsyncHelper # This converts an rsync exclude pattern to a regular expression # we can send to Listen. def self.exclude_to_regexp(path, exclude) start_anchor = false if exclude.start_with?("/") start_anchor = true exclude = exclude[1..-1] end path = "#{path}/" if !path.end_with?("/") regexp = "^#{Regexp.escape(path)}" regexp += ".*" if !start_anchor # This is REALLY ghetto, but its a start. We can improve and # keep unit tests passing in the future. exclude = exclude.gsub("**", "|||GLOBAL|||") exclude = exclude.gsub("*", "|||PATH|||") exclude = exclude.gsub("|||PATH|||", "[^/]*") exclude = exclude.gsub("|||GLOBAL|||", ".*") regexp += exclude Regexp.new(regexp) end def self.rsync_single(machine, ssh_info, opts) # Folder info guestpath = opts[:guestpath] hostpath = opts[:hostpath] hostpath = File.expand_path(hostpath, machine.env.root_path) hostpath = Vagrant::Util::Platform.fs_real_path(hostpath).to_s # if the guest has a guest path scrubber capability, use it if machine.guest.capability?(:rsync_scrub_guestpath) guestpath = machine.guest.capability(:rsync_scrub_guestpath, opts) end # Shellescape guestpath = Shellwords.escape(guestpath) if Vagrant::Util::Platform.windows? # rsync for Windows expects cygwin style paths, always. hostpath = Vagrant::Util::Platform.cygwin_path(hostpath) end # Make sure the host path ends with a "/" to avoid creating # a nested directory... if !hostpath.end_with?("/") hostpath += "/" end # Folder options opts[:owner] ||= ssh_info[:username] opts[:group] ||= ssh_info[:username] # set log level log_level = ssh_info[:log_level] || "FATAL" # Connection information # make it better match lib/vagrant/util/ssh.rb command_options style and logic username = ssh_info[:username] host = ssh_info[:host] proxy_command = "" if ssh_info[:proxy_command] proxy_command = "-o ProxyCommand='#{ssh_info[:proxy_command]}' " end # Create the path for the control sockets. We used to do this # in the machine data dir but this can result in paths that are # too long for unix domain sockets. control_options = "" unless Vagrant::Util::Platform.windows? controlpath = File.join(Dir.tmpdir, "ssh.#{rand(1000)}") control_options = "-o ControlMaster=auto -o ControlPath=#{controlpath} -o ControlPersist=10m " end # rsh cmd option rsh = [ "ssh", "-p", "#{ssh_info[:port]}", "-o", "LogLevel=#{log_level}", proxy_command, control_options, ] # Solaris/OpenSolaris/Illumos uses SunSSH which doesn't support the # IdentitiesOnly option. Also, we don't enable it if keys_only is false # so that SSH properly searches our identities and tries to do it itself. if !Vagrant::Util::Platform.solaris? && ssh_info[:keys_only] rsh += ["-o", "IdentitiesOnly=yes"] end # no strict hostkey checking unless paranoid if ! ssh_info[:verify_host_key] rsh += [ "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null"] end # If specified, attach the private key paths. if ssh_info[:private_key_path] rsh += ssh_info[:private_key_path].map { |p| "-i '#{p}'" } end # Exclude some files by default, and any that might be configured # by the user. excludes = ['.vagrant/'] excludes += Array(opts[:exclude]).map(&:to_s) if opts[:exclude] excludes.uniq! # Get the command-line arguments args = nil args = Array(opts[:args]).dup if opts[:args] args ||= ["--verbose", "--archive", "--delete", "-z", "--copy-links"] # On Windows, we have to set a default chmod flag to avoid permission issues if Vagrant::Util::Platform.windows? && !args.any? { |arg| arg.start_with?("--chmod=") } # Ensures that all non-masked bits get enabled args << "--chmod=ugo=rwX" # Remove the -p option if --archive is enabled (--archive equals -rlptgoD) # otherwise new files will not have the destination-default permissions args << "--no-perms" if args.include?("--archive") || args.include?("-a") end # Disable rsync's owner/group preservation (implied by --archive) unless # specifically requested, since we adjust owner/group to match shared # folder setting ourselves. args << "--no-owner" unless args.include?("--owner") || args.include?("-o") args << "--no-group" unless args.include?("--group") || args.include?("-g") # Tell local rsync how to invoke remote rsync with sudo rsync_path = opts[:rsync_path] if !rsync_path && machine.guest.capability?(:rsync_command) rsync_path = machine.guest.capability(:rsync_command) end if rsync_path args << "--rsync-path"<< rsync_path end # If the remote host is an IPv6 address reformat begin if IPAddr.new(host).ipv6? host = "[#{host}]" end rescue IPAddr::Error # Ignore end # Build up the actual command to execute command = [ "rsync", args, "-e", rsh.flatten.join(" "), excludes.map { |e| ["--exclude", e] }, hostpath, "#{username}@#{host}:#{guestpath}", ].flatten # The working directory should be the root path command_opts = {} command_opts[:workdir] = machine.env.root_path.to_s machine.ui.info(I18n.t( "vagrant.rsync_folder", guestpath: guestpath, hostpath: hostpath)) if excludes.length > 1 machine.ui.info(I18n.t( "vagrant.rsync_folder_excludes", excludes: excludes.inspect)) end if opts.include?(:verbose) machine.ui.info(I18n.t("vagrant.rsync_showing_output")); end # If we have tasks to do before rsyncing, do those. if machine.guest.capability?(:rsync_pre) machine.guest.capability(:rsync_pre, opts) end if opts.include?(:verbose) command_opts[:notify] = [:stdout, :stderr] r = Vagrant::Util::Subprocess.execute(*(command + [command_opts])) { |io_name,data| data.each_line { |line| machine.ui.info("rsync[#{io_name}] -> #{line}") } } else r = Vagrant::Util::Subprocess.execute(*(command + [command_opts])) end if r.exit_code != 0 raise Vagrant::Errors::RSyncError, command: command.map(&:inspect).join(" "), guestpath: guestpath, hostpath: hostpath, stderr: r.stderr end # If we have tasks to do after rsyncing, do those. if machine.guest.capability?(:rsync_post) machine.guest.capability(:rsync_post, opts) end end end end end vagrant-2.0.2/plugins/synced_folders/rsync/plugin.rb000066400000000000000000000012371323370221500226230ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module SyncedFolderRSync # This plugin implements synced folders via rsync. class Plugin < Vagrant.plugin("2") name "RSync synced folders" description <<-EOF The Rsync synced folder plugin will sync folders via rsync. EOF command("rsync", primary: false) do require_relative "command/rsync" Command::Rsync end command("rsync-auto", primary: false) do require_relative "command/rsync_auto" Command::RsyncAuto end synced_folder("rsync", 5) do require_relative "synced_folder" SyncedFolder end end end end vagrant-2.0.2/plugins/synced_folders/rsync/synced_folder.rb000066400000000000000000000027301323370221500241440ustar00rootroot00000000000000require "log4r" require "vagrant/util/subprocess" require "vagrant/util/which" require_relative "helper" module VagrantPlugins module SyncedFolderRSync class SyncedFolder < Vagrant.plugin("2", :synced_folder) include Vagrant::Util def initialize(*args) super @logger = Log4r::Logger.new("vagrant::synced_folders::rsync") end def usable?(machine, raise_error=false) rsync_path = Which.which("rsync") return true if rsync_path return false if !raise_error raise Vagrant::Errors::RSyncNotFound end def prepare(machine, folders, opts) # Nothing is necessary to do before VM boot. end def enable(machine, folders, opts) if machine.guest.capability?(:rsync_installed) installed = machine.guest.capability(:rsync_installed) if !installed can_install = machine.guest.capability?(:rsync_install) raise Vagrant::Errors::RSyncNotInstalledInGuest if !can_install machine.ui.info I18n.t("vagrant.rsync_installing") machine.guest.capability(:rsync_install) end end ssh_info = machine.ssh_info if ssh_info[:private_key_path].empty? && ssh_info[:password] machine.ui.warn(I18n.t("vagrant.rsync_ssh_password")) end folders.each do |id, folder_opts| RsyncHelper.rsync_single(machine, ssh_info, folder_opts) end end end end end vagrant-2.0.2/plugins/synced_folders/smb/000077500000000000000000000000001323370221500204205ustar00rootroot00000000000000vagrant-2.0.2/plugins/synced_folders/smb/config.rb000066400000000000000000000005651323370221500222200ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module SyncedFolderSMB class Config < Vagrant.plugin("2", :config) attr_accessor :functional def initialize super @functional = UNSET_VALUE end def finalize! @functional = true if @functional == UNSET_VALUE end def to_s "SMB" end end end end vagrant-2.0.2/plugins/synced_folders/smb/errors.rb000066400000000000000000000026061323370221500222650ustar00rootroot00000000000000module VagrantPlugins module SyncedFolderSMB module Errors # A convenient superclass for all our errors. class SMBError < Vagrant::Errors::VagrantError error_namespace("vagrant_sf_smb.errors") end class SMBNotSupported < SMBError error_key(:not_supported) end class SMBStartFailed < SMBError error_key(:start_failed) end class SMBCredentialsMissing < SMBError error_key(:credentials_missing) end class SMBListFailed < SMBError error_key(:list_failed) end class SMBNameError < SMBError error_key(:name_error) end class CredentialsRequestError < SMBError error_key(:credentials_request_error) end class DefineShareFailed < SMBError error_key(:define_share_failed) end class PruneShareFailed < SMBError error_key(:prune_share_failed) end class NoHostIPAddr < SMBError error_key(:no_routable_host_addr) end class PowershellError < SMBError error_key(:powershell_error) end class PowershellVersion < SMBError error_key(:powershell_version) end class WindowsHostRequired < SMBError error_key(:windows_host_required) end class WindowsAdminRequired < SMBError error_key(:windows_admin_required) end end end end vagrant-2.0.2/plugins/synced_folders/smb/plugin.rb000066400000000000000000000015551323370221500222510ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module SyncedFolderSMB autoload :Errors, File.expand_path("../errors", __FILE__) # This plugin implements SMB synced folders. class Plugin < Vagrant.plugin("2") name "SMB synced folders" description <<-EOF The SMB synced folders plugin enables you to use SMB folders on Windows or macOS and share them to guest machines. EOF config("smb") do require_relative "config" Config end synced_folder("smb", 7) do require_relative "synced_folder" init! SyncedFolder end protected def self.init! return if defined?(@_init) I18n.load_path << File.expand_path( "templates/locales/synced_folder_smb.yml", Vagrant.source_root) I18n.reload! @_init = true end end end end vagrant-2.0.2/plugins/synced_folders/smb/scripts/000077500000000000000000000000001323370221500221075ustar00rootroot00000000000000vagrant-2.0.2/plugins/synced_folders/smb/scripts/mount_share.ps1000066400000000000000000000044541323370221500250670ustar00rootroot00000000000000param ( [string]$share_name = $(throw "-share_name is required."), [string]$guest_path = $(throw "-guest_path is required."), [string]$guest_ip = $(throw "-guest_ip is required."), [string]$username = $(throw "-username is required."), [string]$password = $(throw "-password is required."), [string]$host_ip = $(throw "-host_ip is required."), [string]$host_share_username = $(throw "-host_share_username is required."), [string]$host_share_password = $(throw "-host_share_password is required.") ) # Include the following modules $presentDir = Split-Path -parent $PSCommandPath $modules = @() $modules += $presentDir + "\utils\create_session.ps1" $modules += $presentDir + "\utils\write_messages.ps1" forEach ($module in $modules) { . $module } try { function Mount-File($share_name, $guest_path, $host_path, $host_share_username, $host_share_password) { try { # TODO: Check for folder exist. # Use net use and prompt for password $guest_path = $guest_path.replace("/", "\") # Map a network drive to the guest machine $result = net use * $host_path /user:$host_share_username $host_share_password /persistent:yes $mapped_drive = (($result -match "\w:") -split (" "))[1] Write-Host cmd /c mklink /d $guest_path $mapped_drive # If a folder exist remove it. if (Test-Path $guest_path) { $junction = Get-Item $guest_path $junction.Delete() } cmd /c mklink /d $guest_path $mapped_drive } catch { return $_ } } $response = Create-Remote-Session $guest_ip $username $password if (!$response["session"] -and $response["error"]) { Write-Error-Message $response["error"] return } $host_path = "\\$host_ip\$share_name" $host_share_username = "$host_ip\$host_share_username" $result = Invoke-Command -Session $response["session"] -ScriptBlock ${function:Mount-File} -ArgumentList $share_name, $guest_path, $host_path, $host_share_username, $host_share_password -ErrorAction "stop" Remove-PSSession -Id $response["session"].Id Write-Error-Message $result } catch { Write-Error-Message "Failed to mount files VM $_" return } vagrant-2.0.2/plugins/synced_folders/smb/scripts/ps_version.ps1000066400000000000000000000000551323370221500247230ustar00rootroot00000000000000Write-Output $PSVersionTable.PSVersion.Major vagrant-2.0.2/plugins/synced_folders/smb/synced_folder.rb000066400000000000000000000113021323370221500235620ustar00rootroot00000000000000require "digest/md5" require "json" require "log4r" require "vagrant/util/platform" require "vagrant/util/powershell" require_relative "errors" module VagrantPlugins module SyncedFolderSMB class SyncedFolder < Vagrant.plugin("2", :synced_folder) # Maximum number of times to retry requesting username/password CREDENTIAL_RETRY_MAX = 5 def initialize(*args) super @logger = Log4r::Logger.new("vagrant::synced_folders::smb") end def usable?(machine, raise_error=false) # If the machine explicitly states SMB is not supported, then # believe it return false if !machine.config.smb.functional return true if machine.env.host.capability?(:smb_installed) && machine.env.host.capability(:smb_installed) return false if !raise_error raise Errors::SMBNotSupported end def prepare(machine, folders, opts) machine.ui.output(I18n.t("vagrant_sf_smb.preparing")) smb_username = smb_password = nil # If we need auth information, then ask the user. have_auth = false folders.each do |id, data| if data[:smb_username] && data[:smb_password] smb_username = data[:smb_username] smb_password = data[:smb_password] have_auth = true break end end if !have_auth machine.ui.detail(I18n.t("vagrant_sf_smb.warning_password") + "\n ") retries = 0 while retries < CREDENTIAL_RETRY_MAX do smb_username = machine.ui.ask("Username: ") smb_password = machine.ui.ask("Password (will be hidden): ", echo: false) auth_success = true if machine.env.host.capability?(:smb_validate_password) Vagrant::Util::CredentialScrubber.sensitive(smb_password) auth_success = machine.env.host.capability(:smb_validate_password, smb_username, smb_password) end break if auth_success machine.ui.output(I18n.t("vagrant_sf_smb.incorrect_credentials") + "\n ") retries += 1 end if retries >= CREDENTIAL_RETRY_MAX raise Errors::CredentialsRequestError end end # Check if this host can start and SMB service if machine.env.host.capability?(:smb_start) machine.env.host.capability(:smb_start) end folders.each do |id, data| data[:smb_username] ||= smb_username data[:smb_password] ||= smb_password # Register password as sensitive Vagrant::Util::CredentialScrubber.sensitive(data[:smb_password]) end machine.env.host.capability(:smb_prepare, machine, folders, opts) end def enable(machine, folders, opts) machine.ui.output(I18n.t("vagrant_sf_smb.mounting")) # Make sure that this machine knows this dance if !machine.guest.capability?(:mount_smb_shared_folder) raise Vagrant::Errors::GuestCapabilityNotFound, cap: "mount_smb_shared_folder", guest: machine.guest.name.to_s end # Setup if we have it if machine.guest.capability?(:smb_install) machine.guest.capability(:smb_install) end # Detect the host IP for this guest if one wasn't specified # for every folder. host_ip = nil need_host_ip = false folders.each do |id, data| if !data[:smb_host] need_host_ip = true break end end if need_host_ip candidate_ips = machine.env.host.capability(:configured_ip_addresses) @logger.debug("Potential host IPs: #{candidate_ips.inspect}") host_ip = machine.guest.capability( :choose_addressable_ip_addr, candidate_ips) if !host_ip raise Errors::NoHostIPAddr end end # This is used for defaulting the owner/group ssh_info = machine.ssh_info folders.each do |id, data| data[:smb_host] ||= host_ip # Default the owner/group of the folder to the SSH user data[:owner] ||= ssh_info[:username] data[:group] ||= ssh_info[:username] machine.ui.detail(I18n.t( "vagrant_sf_smb.mounting_single", host: data[:hostpath].to_s, guest: data[:guestpath].to_s)) machine.guest.capability( :mount_smb_shared_folder, data[:smb_id], data[:guestpath], data) end end def cleanup(machine, opts) if machine.env.host.capability?(:smb_cleanup) machine.env.host.capability(:smb_cleanup, machine, opts) end end end end end vagrant-2.0.2/plugins/synced_folders/unix_mount_helpers.rb000066400000000000000000000105741323370221500241220ustar00rootroot00000000000000require "shellwords" require "vagrant/util/retryable" module VagrantPlugins module SyncedFolder module UnixMountHelpers def self.extended(klass) if !klass.class_variable_defined?(:@@logger) klass.class_variable_set(:@@logger, Log4r::Logger.new(klass.name.downcase)) end klass.extend Vagrant::Util::Retryable end def detect_owner_group_ids(machine, guest_path, mount_options, options) mount_uid = find_mount_options_id("uid", mount_options) mount_gid = find_mount_options_id("gid", mount_options) if mount_uid.nil? if options[:owner].to_i.to_s == options[:owner].to_s mount_uid = options[:owner] self.class_variable_get(:@@logger).debug("Owner user ID (provided): #{mount_uid}") else output = {stdout: '', stderr: ''} uid_command = "id -u #{options[:owner]}" machine.communicate.execute(uid_command, error_class: Vagrant::Errors::VirtualBoxMountFailed, error_key: :virtualbox_mount_failed, command: uid_command, output: output[:stderr] ) { |type, data| output[type] << data if output[type] } mount_uid = output[:stdout].chomp self.class_variable_get(:@@logger).debug("Owner user ID (lookup): #{options[:owner]} -> #{mount_uid}") end else machine.ui.warn "Detected mount owner ID within mount options. (uid: #{mount_uid} guestpath: #{guest_path})" end if mount_gid.nil? if options[:group].to_i.to_s == options[:group].to_s mount_gid = options[:group] self.class_variable_get(:@@logger).debug("Owner group ID (provided): #{mount_gid}") else begin output = {stdout: '', stderr: ''} gid_command = "getent group #{options[:group]}" machine.communicate.execute(gid_command, error_class: Vagrant::Errors::VirtualBoxMountFailed, error_key: :virtualbox_mount_failed, command: gid_command, output: output[:stderr] ) { |type, data| output[type] << data if output[type] } mount_gid = output[:stdout].split(':').at(2).to_s.chomp self.class_variable_get(:@@logger).debug("Owner group ID (lookup): #{options[:group]} -> #{mount_gid}") rescue Vagrant::Errors::VirtualBoxMountFailed if options[:owner] == options[:group] self.class_variable_get(:@@logger).debug("Failed to locate group `#{options[:group]}`. Group name matches owner. Fetching effective group ID.") output = {stdout: ''} result = machine.communicate.execute("id -g #{options[:owner]}", error_check: false ) { |type, data| output[type] << data if output[type] } mount_gid = output[:stdout].chomp if result == 0 self.class_variable_get(:@@logger).debug("Owner group ID (effective): #{mount_gid}") end raise unless mount_gid end end else machine.ui.warn "Detected mount group ID within mount options. (gid: #{mount_gid} guestpath: #{guest_path})" end {:gid => mount_gid, :uid => mount_uid} end def find_mount_options_id(id_name, mount_options) id_line = mount_options.detect{|line| line.include?("#{id_name}=")} if id_line match = id_line.match(/,?#{Regexp.escape(id_name)}=(?\d+),?/) found_id = match["option_id"] updated_id_line = [ match.pre_match, match.post_match ].find_all{|string| !string.empty?}.join(',') if updated_id_line.empty? mount_options.delete(id_line) else idx = mount_options.index(id_line) mount_options.delete(idx) mount_options.insert(idx, updated_id_line) end end found_id end def emit_upstart_notification(machine, guest_path) # Emit an upstart event if we can machine.communicate.sudo <<-EOH.gsub(/^ {12}/, "") if command -v /sbin/init && /sbin/init 2>/dev/null --version | grep upstart; then /sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{guest_path} fi EOH end end end end vagrant-2.0.2/scripts/000077500000000000000000000000001323370221500146425ustar00rootroot00000000000000vagrant-2.0.2/scripts/sign.sh000077500000000000000000000011511323370221500161370ustar00rootroot00000000000000#!/bin/bash set -e # Get the parent directory of where this script is. SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" # Change into that dir because we expect that cd $DIR # Get the version from the command line VERSION=$1 if [ -z $VERSION ]; then echo "Please specify a version." exit 1 fi # Make the checksums pushd ./pkg/dist shasum -a256 * > ./vagrant_${VERSION}_SHA256SUMS if [ -z $NOSIGN ]; then echo "==> Signing..." gpg --default-key 348FFC4C --detach-sig ./vagrant_${VERSION}_SHA256SUMS fi popd vagrant-2.0.2/scripts/website_push_docs.sh000077500000000000000000000015751323370221500207220ustar00rootroot00000000000000#!/bin/bash # Set the tmpdir if [ -z "$TMPDIR" ]; then TMPDIR="/tmp" fi # Create a temporary build dir and make sure we clean it up. For # debugging, comment out the trap line. DEPLOY=`mktemp -d /tmp/vagrant-docs-XXXXXX` trap "rm -rf $DEPLOY" INT TERM EXIT # Get the parent directory of where this script is. SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" # Copy into tmpdir shopt -s dotglob cp -R $DIR/website/docs/* $DEPLOY/ # Change into that directory cd $DEPLOY # Ignore some stuff touch .gitignore echo ".sass-cache" >> .gitignore echo "build" >> .gitignore echo "vendor" >> .gitignore # Add everything git init . git add . git commit -q -m "Deploy by $USER" git remote add heroku git@heroku.com:vagrantup-docs-2.git git push -f heroku master # Cleanup the deploy rm -rf $DEPLOY vagrant-2.0.2/scripts/website_push_www.sh000077500000000000000000000015721323370221500206130ustar00rootroot00000000000000#!/bin/bash # Set the tmpdir if [ -z "$TMPDIR" ]; then TMPDIR="/tmp" fi # Create a temporary build dir and make sure we clean it up. For # debugging, comment out the trap line. DEPLOY=`mktemp -d /tmp/vagrant-www-XXXXXX` trap "rm -rf $DEPLOY" INT TERM EXIT # Get the parent directory of where this script is. SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" # Copy into tmpdir shopt -s dotglob cp -R $DIR/website/www/* $DEPLOY/ # Change into that directory cd $DEPLOY # Ignore some stuff touch .gitignore echo ".sass-cache" >> .gitignore echo "build" >> .gitignore echo "vendor" >> .gitignore # Add everything git init . git add . git commit -q -m "Deploy by $USER" git remote add heroku git@heroku.com:vagrantup-www-2.git git push -f heroku master # Cleanup the deploy rm -rf $DEPLOY vagrant-2.0.2/tasks/000077500000000000000000000000001323370221500143005ustar00rootroot00000000000000vagrant-2.0.2/tasks/acceptance.rake000066400000000000000000000007241323370221500172350ustar00rootroot00000000000000namespace :acceptance do desc "shows components that can be tested" task :components do exec("vagrant-spec components --config=vagrant-spec.config.rb") end desc "runs acceptance tests" task :run do args = [ "--config=vagrant-spec.config.rb", ] if ENV["COMPONENTS"] args << "--components=\"#{ENV["COMPONENTS"]}\"" end command = "vagrant-spec test #{args.join(" ")}" puts command puts exec(command) end end vagrant-2.0.2/tasks/bundler.rake000066400000000000000000000001511323370221500165740ustar00rootroot00000000000000# This installs the tasks that help with gem creation and # publishing. Bundler::GemHelper.install_tasks vagrant-2.0.2/tasks/test.rake000066400000000000000000000003051323370221500161210ustar00rootroot00000000000000require 'rake/testtask' require 'rspec/core/rake_task' namespace :test do RSpec::Core::RakeTask.new(:unit) do |t| t.pattern = "test/unit/**/*_test.rb" t.rspec_opts = "--color" end end vagrant-2.0.2/templates/000077500000000000000000000000001323370221500151515ustar00rootroot00000000000000vagrant-2.0.2/templates/commands/000077500000000000000000000000001323370221500167525ustar00rootroot00000000000000vagrant-2.0.2/templates/commands/init/000077500000000000000000000000001323370221500177155ustar00rootroot00000000000000vagrant-2.0.2/templates/commands/init/Vagrantfile.erb000066400000000000000000000063551323370221500226620ustar00rootroot00000000000000# -*- mode: ruby -*- # vi: set ft=ruby : # All Vagrant configuration is done below. The "2" in Vagrant.configure # configures the configuration version (we support older styles for # backwards compatibility). Please don't change it unless you know what # you're doing. Vagrant.configure("2") do |config| # The most common configuration options are documented and commented below. # For a complete reference, please see the online documentation at # https://docs.vagrantup.com. # Every Vagrant development environment requires a box. You can search for # boxes at https://vagrantcloud.com/search. config.vm.box = "<%= box_name %>" <% if box_version -%> config.vm.box_version = "<%= box_version %>" <% end -%> <% if box_url -%> # The url from where the 'config.vm.box' box will be fetched if it # doesn't already exist on the user's system. config.vm.box_url = "<%= box_url %>" <% else -%> # Disable automatic box update checking. If you disable this, then # boxes will only be checked for updates when the user runs # `vagrant box outdated`. This is not recommended. # config.vm.box_check_update = false <% end -%> # Create a forwarded port mapping which allows access to a specific port # within the machine from a port on the host machine. In the example below, # accessing "localhost:8080" will access port 80 on the guest machine. # NOTE: This will enable public access to the opened port # config.vm.network "forwarded_port", guest: 80, host: 8080 # Create a forwarded port mapping which allows access to a specific port # within the machine from a port on the host machine and only allow access # via 127.0.0.1 to disable public access # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1" # Create a private network, which allows host-only access to the machine # using a specific IP. # config.vm.network "private_network", ip: "192.168.33.10" # Create a public network, which generally matched to bridged network. # Bridged networks make the machine appear as another physical device on # your network. # config.vm.network "public_network" # Share an additional folder to the guest VM. The first argument is # the path on the host to the actual folder. The second argument is # the path on the guest to mount the folder. And the optional third # argument is a set of non-required options. # config.vm.synced_folder "../data", "/vagrant_data" # Provider-specific configuration so you can fine-tune various # backing providers for Vagrant. These expose provider-specific options. # Example for VirtualBox: # # config.vm.provider "virtualbox" do |vb| # # Display the VirtualBox GUI when booting the machine # vb.gui = true # # # Customize the amount of memory on the VM: # vb.memory = "1024" # end # # View the documentation for the provider you are using for more # information on available options. # Enable provisioning with a shell script. Additional provisioners such as # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the # documentation for more information about their specific syntax and use. # config.vm.provision "shell", inline: <<-SHELL # apt-get update # apt-get install -y apache2 # SHELL end vagrant-2.0.2/templates/commands/init/Vagrantfile.min.erb000066400000000000000000000003471323370221500234370ustar00rootroot00000000000000Vagrant.configure("2") do |config| config.vm.box = "<%= box_name %>" <% if box_version -%> config.vm.box_version = "<%= box_version %>" <% end -%> <% if box_url -%> config.vm.box_url = "<%= box_url %>" <% end -%> end vagrant-2.0.2/templates/commands/ssh_config/000077500000000000000000000000001323370221500210745ustar00rootroot00000000000000vagrant-2.0.2/templates/commands/ssh_config/config.erb000066400000000000000000000013311323370221500230310ustar00rootroot00000000000000Host <%= host_key %> HostName <%= ssh_host %> User <%= ssh_user %> Port <%= ssh_port %> <% if ! verify_host_key -%> UserKnownHostsFile /dev/null StrictHostKeyChecking no <% end -%> PasswordAuthentication no <% if private_key_path -%> <% private_key_path.each do |path| %> <% if path.include?(" ") -%> IdentityFile "<%= path %>" <% else -%> IdentityFile <%= path %> <% end -%> <% end -%> <% end -%> <% if keys_only -%> IdentitiesOnly yes <% end -%> <% if log_level -%> LogLevel <%= log_level %> <% else -%> LogLevel FATAL <% end -%> <% if forward_agent -%> ForwardAgent yes <% end -%> <% if forward_x11 -%> ForwardX11 yes <% end -%> <% if proxy_command -%> ProxyCommand <%= proxy_command %> <% end -%> vagrant-2.0.2/templates/config/000077500000000000000000000000001323370221500164165ustar00rootroot00000000000000vagrant-2.0.2/templates/config/messages.erb000066400000000000000000000003241323370221500207160ustar00rootroot00000000000000<% if !warnings.empty? -%> Warnings: <% warnings.each do |warning| -%> * <%= warning %> <% end -%> <% end -%> <% if !errors.empty? -%> Errors: <% errors.each do |error| -%> * <%= error %> <% end -%> <% end -%> vagrant-2.0.2/templates/config/validation_failed.erb000066400000000000000000000001701323370221500225440ustar00rootroot00000000000000<% errors.each do |section, list| -%> <%= section %>: <% list.each do |error| -%> * <%= error %> <% end -%> <% end -%> vagrant-2.0.2/templates/guests/000077500000000000000000000000001323370221500164635ustar00rootroot00000000000000vagrant-2.0.2/templates/guests/alt/000077500000000000000000000000001323370221500172435ustar00rootroot00000000000000vagrant-2.0.2/templates/guests/alt/network_dhcp.erb000066400000000000000000000003041323370221500224210ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. TYPE=eth NM_CONTROLLED=<%= options.fetch(:nm_controlled, "no") %> BOOTPROTO=dhcp ONBOOT=yes #VAGRANT-END vagrant-2.0.2/templates/guests/alt/network_ipv4address.erb000066400000000000000000000001111323370221500237270ustar00rootroot00000000000000#VAGRANT-BEGIN <%= options[:ip] %>/<%= options[:netmask] %> #VAGRANT-END vagrant-2.0.2/templates/guests/alt/network_ipv4route.erb000066400000000000000000000001461323370221500234500ustar00rootroot00000000000000#VAGRANT-BEGIN <% if options[:gateway] %> default via <%= options[:gateway] %> <% end %> #VAGRANT-END vagrant-2.0.2/templates/guests/alt/network_static.erb000066400000000000000000000003061323370221500227740ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. TYPE=eth NM_CONTROLLED=<%= options.fetch(:nm_controlled, "no") %> BOOTPROTO=static ONBOOT=yes #VAGRANT-END vagrant-2.0.2/templates/guests/arch/000077500000000000000000000000001323370221500174005ustar00rootroot00000000000000vagrant-2.0.2/templates/guests/arch/network_dhcp.erb000066400000000000000000000001551323370221500225620ustar00rootroot00000000000000Description='A basic dhcp ethernet connection' Interface=<%= options[:device] %> Connection=ethernet IP=dhcp vagrant-2.0.2/templates/guests/arch/network_static.erb000066400000000000000000000003631323370221500231340ustar00rootroot00000000000000Connection=ethernet Description='A basic static ethernet connection' Interface=<%= options[:device] %> IP=static Address=('<%= options[:ip]%>/<%= options[:netmask] %>') <% if options[:gateway] -%> Gateway='<%= options[:gateway] %>' <% end -%> vagrant-2.0.2/templates/guests/arch/network_static6.erb000066400000000000000000000003641323370221500232230ustar00rootroot00000000000000Connection=ethernet Description='A basic IPv6 ethernet connection' Interface=<%= options[:device] %> IP6=static Address6=('<%= options[:ip]%>/<%= options[:netmask] %>') <% if options[:gateway] -%> Gateway6='<%= options[:gateway] %>' <% end -%> vagrant-2.0.2/templates/guests/coreos/000077500000000000000000000000001323370221500177555ustar00rootroot00000000000000vagrant-2.0.2/templates/guests/coreos/etcd.service.erb000066400000000000000000000004331323370221500230250ustar00rootroot00000000000000[Unit] Description=Clustered etcd #After=docker.service [Service] Restart=always ExecStart=/usr/bin/etcd -c 4001 -s 7001 -h <%= options[:my_ip] %> <% if options[:connection_string] %>-C <%= options[:connection_string] %><% end %> -d /home/core/etcd [Install] WantedBy=local.target vagrant-2.0.2/templates/guests/debian/000077500000000000000000000000001323370221500177055ustar00rootroot00000000000000vagrant-2.0.2/templates/guests/debian/network_dhcp.erb000066400000000000000000000007651323370221500230760ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. auto <%= options[:device] %> iface <%= options[:device] %> 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 <%= options[:root_device] %> || true post-up dhclient $IFACE pre-down route add default dev <%= options[:root_device] %> <% end %> #VAGRANT-END vagrant-2.0.2/templates/guests/debian/network_static.erb000066400000000000000000000005041323370221500234360ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. auto <%= options[:device] %> iface <%= options[:device] %> inet static address <%= options[:ip] %> netmask <%= options[:netmask] %> <% if options[:gateway] %> gateway <%= options[:gateway] %> <% end %> #VAGRANT-END vagrant-2.0.2/templates/guests/debian/network_static6.erb000066400000000000000000000005051323370221500235250ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. auto <%= options[:device] %> iface <%= options[:device] %> inet6 static address <%= options[:ip] %> netmask <%= options[:netmask] %> <% if options[:gateway] %> gateway <%= options[:gateway] %> <% end %> #VAGRANT-END vagrant-2.0.2/templates/guests/freebsd/000077500000000000000000000000001323370221500200755ustar00rootroot00000000000000vagrant-2.0.2/templates/guests/freebsd/network_dhcp.erb000066400000000000000000000001371323370221500232570ustar00rootroot00000000000000#VAGRANT-BEGIN ifconfig_<%= options[:device] %>="DHCP" synchronous_dhclient="YES" #VAGRANT-END vagrant-2.0.2/templates/guests/freebsd/network_static.erb000066400000000000000000000003101323370221500236210ustar00rootroot00000000000000#VAGRANT-BEGIN ifconfig_<%= options[:device] %>="inet <%= options[:ip] %> netmask <%= options[:netmask] %>" <% if options[:gateway] %> default_router="<%= options[:gateway] %>" <% end %> #VAGRANT-END vagrant-2.0.2/templates/guests/freebsd/network_static6.erb000066400000000000000000000003251323370221500237150ustar00rootroot00000000000000#VAGRANT-BEGIN ifconfig_<%= options[:device] %>_ipv6="inet6 <%= options[:ip] %> prefixlen <%= options[:netmask] %>" <% if options[:gateway] %> ipv6_default_router="<%= options[:gateway] %>" <% end %> #VAGRANT-END vagrant-2.0.2/templates/guests/funtoo/000077500000000000000000000000001323370221500177755ustar00rootroot00000000000000vagrant-2.0.2/templates/guests/funtoo/network_dhcp.erb000066400000000000000000000001701323370221500231540ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. template='dhcp' #VAGRANT-END vagrant-2.0.2/templates/guests/funtoo/network_static.erb000066400000000000000000000005261323370221500235320ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. template='interface' ipaddr='<%= options[:ip] %>/<%= options[:netmask] %>' <% [:gateway, :nameservers, :domain, :route, :gateway6, :route6, :mtu].each do |key| %> <% if options[key] %> <%= key %>='<%= options[key] %>' <% end %> <% end %> #VAGRANT-END vagrant-2.0.2/templates/guests/funtoo/network_static6.erb000066400000000000000000000004121323370221500236120ustar00rootroot00000000000000#VAGRANT-BEGIN template='interface' ipaddr='<%= options[:ip] %>/<%= options[:netmask] %>' <% [:gateway, :nameservers, :domain, :route, :gateway6, :route6, :mtu].each do |key| %> <% if options[key] %> <%= key %>='<%= options[key] %>' <% end %> <% end %> #VAGRANT-END vagrant-2.0.2/templates/guests/gentoo/000077500000000000000000000000001323370221500177565ustar00rootroot00000000000000vagrant-2.0.2/templates/guests/gentoo/network_dhcp.erb000066400000000000000000000002161323370221500231360ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. config_<%= options[:device] %>="dhcp" #VAGRANT-END vagrant-2.0.2/templates/guests/gentoo/network_static.erb000066400000000000000000000004431323370221500235110ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. config_<%= options[:device] %>=("<%= options[:ip] %> netmask <%= options[:netmask] %>") <% if options[:gateway] -%> gateways_<%= options[:device] %>="<%= options[:gateway] %>" <% end -%> #VAGRANT-END vagrant-2.0.2/templates/guests/gentoo/network_static6.erb000066400000000000000000000004311323370221500235740ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. config_<%= options[:device] %>="<%= options[:ip] %>/<%= options[:netmask] %>" <% if options[:gateway] -%> gateways_<%= options[:device] %>="<%= options[:gateway] %>" <% end -%> #VAGRANT-END vagrant-2.0.2/templates/guests/gentoo/network_systemd.erb000066400000000000000000000005051323370221500237110ustar00rootroot00000000000000[Match] Name=<%= networks[0][:device] %> [Network] <%- gateway = nil %> <%- networks.each do |net| %> <%- if net[:type] == 'dhcp' %> DHCP=yes <%- elsif net[:ip] %> Address=<%= net[:ip] %>/<%= net[:netmask] %> <%- end %> <%- gateway ||= net[:gateway] %> <%- end %> <%- if gateway %> Gateway=<%= gateway %> <%- end %> vagrant-2.0.2/templates/guests/netbsd/000077500000000000000000000000001323370221500177425ustar00rootroot00000000000000vagrant-2.0.2/templates/guests/netbsd/network_dhcp.erb000066400000000000000000000001071323370221500231210ustar00rootroot00000000000000#VAGRANT-BEGIN ifconfig_wm<%= options[:interface] %>=dhcp #VAGRANT-END vagrant-2.0.2/templates/guests/netbsd/network_static.erb000066400000000000000000000002221323370221500234700ustar00rootroot00000000000000#VAGRANT-BEGIN ifconfig_wm<%= options[:interface] %>="media autoselect up;inet <%= options[:ip] %> netmask <%= options[:netmask] %>" #VAGRANT-END vagrant-2.0.2/templates/guests/nixos/000077500000000000000000000000001323370221500176235ustar00rootroot00000000000000vagrant-2.0.2/templates/guests/nixos/hostname.erb000066400000000000000000000001361323370221500221330ustar00rootroot00000000000000{ config, pkgs, ... }: { <% if name %> networking.hostName = "<%= name %>"; <% end %> } vagrant-2.0.2/templates/guests/nixos/network.erb000066400000000000000000000006251323370221500220110ustar00rootroot00000000000000{ config, pkgs, ... }: { networking.interfaces = [ <% networks.select {|n| n[:device]}.each do |network| %> { name = "<%= network[:device] %>"; <% if network[:type] == :static %> ipAddress = "<%= network[:ip] %>"; <% end %> <% if network[:prefix_length] %> prefixLength = <%= network[:prefix_length] %>; <% end %> } <% end %> ]; } vagrant-2.0.2/templates/guests/openbsd/000077500000000000000000000000001323370221500201155ustar00rootroot00000000000000vagrant-2.0.2/templates/guests/openbsd/network_dhcp.erb000066400000000000000000000000051323370221500232710ustar00rootroot00000000000000dhcp vagrant-2.0.2/templates/guests/openbsd/network_static.erb000066400000000000000000000000671323370221500236520ustar00rootroot00000000000000inet <%= options[:ip] %> <%= options[:netmask] %> NONE vagrant-2.0.2/templates/guests/redhat/000077500000000000000000000000001323370221500177325ustar00rootroot00000000000000vagrant-2.0.2/templates/guests/redhat/network_dhcp.erb000066400000000000000000000003321323370221500231110ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. BOOTPROTO=dhcp ONBOOT=yes DEVICE=<%= options[:device] %> NM_CONTROLLED=<%= options.fetch(:nm_controlled, "no") %> #VAGRANT-END vagrant-2.0.2/templates/guests/redhat/network_static.erb000066400000000000000000000006641323370221500234720ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. NM_CONTROLLED=<%= options.fetch(:nm_controlled, "no") %> BOOTPROTO=none ONBOOT=yes IPADDR=<%= options[:ip] %> NETMASK=<%= options[:netmask] %> DEVICE=<%= options[:device] %> <% if options[:gateway] %> GATEWAY=<%= options[:gateway] %> <% end %> <% if options[:mac_address] %> HWADDR=<%= options[:mac_address] %> <% end %> PEERDNS=no #VAGRANT-END vagrant-2.0.2/templates/guests/redhat/network_static6.erb000066400000000000000000000005551323370221500235570ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. NM_CONTROLLED=<%= options.fetch(:nm_controlled, "no") %> BOOTPROTO=static ONBOOT=yes DEVICE=<%= options[:device] %> IPV6INIT=yes IPV6ADDR=<%= options[:ip] %>/<%= options[:netmask] %> <% if options[:gateway] -%> IPV6_DEFAULTGW=<%= options[:gateway] %> <% end %> #VAGRANT-END vagrant-2.0.2/templates/guests/slackware/000077500000000000000000000000001323370221500204375ustar00rootroot00000000000000vagrant-2.0.2/templates/guests/slackware/network_dhcp.erb000066400000000000000000000003071323370221500236200ustar00rootroot00000000000000#VAGRANT-BEGIN # Config for eth<%= i %> USE_DHCP[<%= i %>]="yes" DHCP_HOSTNAME[<%= i %>]="" <% if options[:gateway] -%> GATEWAY="<%= options[:gateway] %>" <% end -%> DEBUG_ETH_UP="no" #VAGRANT-END vagrant-2.0.2/templates/guests/slackware/network_static.erb000066400000000000000000000004231323370221500241700ustar00rootroot00000000000000#VAGRANT-BEGIN # Config for eth<%= i %> IPADDR[<%= i %>]="<%= options[:ip] %>" NETMASK[<%= i %>]="<%= options[:ip] %>" USE_DHCP[<%= i %>]="" DHCP_HOSTNAME[<%= i %>]="" <% if options[:gateway] -%> GATEWAY="<%= options[:gateway] %>" <% end -%> DEBUG_ETH_UP="no" #VAGRANT-END vagrant-2.0.2/templates/guests/suse/000077500000000000000000000000001323370221500174425ustar00rootroot00000000000000vagrant-2.0.2/templates/guests/suse/network_dhcp.erb000066400000000000000000000002531323370221500226230ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. BOOTPROTO='dhcp' STARTMODE='auto' DEVICE='<%= options[:device] %>' #VAGRANT-END vagrant-2.0.2/templates/guests/suse/network_static.erb000066400000000000000000000005251323370221500231760ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. BOOTPROTO='static' IPADDR='<%= options[:ip] %>' NETMASK='<%= options[:netmask] %>' DEVICE='<%= options[:device] %>' <% if options[:gateway] -%> GATEWAY='<%= options[:gateway] %>' <% end -%> PEERDNS='no' STARTMODE='auto' USERCONTROL='no' #VAGRANT-END vagrant-2.0.2/templates/locales/000077500000000000000000000000001323370221500165735ustar00rootroot00000000000000vagrant-2.0.2/templates/locales/comm_winrm.yml000066400000000000000000000065321323370221500214730ustar00rootroot00000000000000en: vagrant_winrm: errors: authentication_failed: |- An authorization error occurred while connecting to WinRM. User: %{user} Endpoint: %{endpoint} Message: %{message} winrm_bad_exit_status: |- The following WinRM command responded with a non-zero exit status. Vagrant assumes that this means the command failed! %{command} Stdout from the command: %{stdout} Stderr from the command: %{stderr} execution_error: |- An error occurred executing a remote WinRM command. Shell: %{shell} Command: %{command} Message: %{message} invalid_shell: |- %{shell} is not a supported type of Windows shell. invalid_transport: |- %{transport} is not a supported WinRM transport. ssl_error: |- An SSL error occurred while connecting to WinRM. This usually occurs when you are using a self-signed certificate and have not set the WinRM `ssl_peer_verification` config setting to false. Message: %{message} winrm_not_ready: |- The box is not able to report an address for WinRM to connect to yet. WinRM cannot access this Vagrant environment. Please wait for the Vagrant environment to be running and try again. winrm_file_transfer_error: |- Failed to transfer a file between the host and guest From: %{from} To: %{to} Message: %{message} connection_refused: |- WinRM connection was refused! This usually happens if the VM failed to boot properly. Some steps to try to fix this: First, try reloading your VM with `vagrant reload`, since a simple restart sometimes fixes things. If that doesn't work, destroy your VM and recreate it with a `vagrant destroy` followed by a `vagrant up`. If that doesn't work, contact a Vagrant maintainer (support channels listed on the website) for more assistance. connection_reset: |- WinRM connection was reset! This usually happens when the machine is taking too long to reboot. First, try reloading your machine with `vagrant reload`, since a simple restart sometimes fixes things. If that doesn't work, destroy your machine and recreate it with a `vagrant destroy` followed by a `vagrant up`. If that doesn't work, contact support. connection_timeout: |- Vagrant timed out while attempting to connect via WinRM. This usually means that the VM booted, but there are issues with the WinRM configuration or network connectivity issues. Please try to `vagrant reload` or `vagrant up` again. disconnected: |- The WinRM connection was unexpectedly closed by the remote end. This usually indicates that WinRM within the guest machine was unable to properly start up. Please boot the VM in GUI mode to check whether it is booting properly. no_route: |- While attempting to connect with WinRM, a "no route to host" (EHOSTUNREACH) error was received. Please verify your network settings are correct and try again. host_down: |- While attempting to connect with WinRM, a "host is down" (EHOSTDOWN) error was received. Please verify your WinRM settings are correct and try again. vagrant-2.0.2/templates/locales/command_ps.yml000066400000000000000000000016251323370221500214420ustar00rootroot00000000000000en: vagrant_ps: detecting: |- Detecting if a remote PowerShell connection can be made with the guest... reseting: |- Resetting WinRM TrustedHosts to their original value. errors: host_unsupported: |- Your host does not support PowerShell. A remote PowerShell connection can only be made from a windows host. ps_remoting_undetected: |- Unable to establish a remote PowerShell connection with the guest. Check if the firewall rules on the guest allow connections to the Windows remote management service. powershell_error: |- An error occurred while executing a PowerShell script. This error is shown below. Please read the error message and see if this is a configuration error with your system. If it is not, then please report a bug. Script: %{script} Error: %{stderr} vagrant-2.0.2/templates/locales/command_rdp.yml000066400000000000000000000021331323370221500216000ustar00rootroot00000000000000en: vagrant_rdp: detecting: |- Detecting RDP info... connecting: |- Vagrant will now launch your RDP client with the connection parameters above. If the connection fails, verify that the information above is correct. Additionally, make sure the RDP server is configured and running in the guest machine (it is disabled by default on Windows). Also, verify that the firewall is open to allow RDP connections. errors: host_unsupported: |- Vagrant doesn't support running an RDP client on your host OS. If you wish for the OS you're running to support launching an RDP client, please contribute this functionality back into Vagrant. At the very least, open an issue on how it could be done and we can handle the integration. rdp_undetected: |- RDP connection information for this machine could not be detected. This is typically caused when we can't find the IP or port to connect to for RDP. Please verify you're forwarding an RDP port and that your machine is accessible. vagrant-2.0.2/templates/locales/en.yml000066400000000000000000003541351323370221500177330ustar00rootroot00000000000000en: vagrant: alert: |- [%{date}]: %{message} - %{url} boot_completed: |- Machine booted and ready! boot_waiting: |- Waiting for machine to boot. This may take a few minutes... box_auto_adding: |- Box '%{name}' could not be found. Attempting to find and install... box_add_choose_provider: |- This box can work with multiple providers! The providers that it can work with are listed below. Please review the list and choose the provider you will be working with. %{options} Enter your choice: box_add_choose_provider_again: |- Invalid choice. Try again: box_add_with_version: |- Adding box '%{name}' (v%{version}) for provider: %{providers} box_added: |- Successfully added box '%{name}' (v%{version}) for '%{provider}'! box_adding_direct: |- Box file was not detected as metadata. Adding it directly... box_add_url_warn: |- It looks like you attempted to add a box with a URL for the name... Instead, use box_url instead of box for box URLs. box_downloading: |- Downloading: %{url} box_download_error: |- Error downloading: %{message} box_unpacking: |- Unpacking necessary files from: %{url} box_expanding_url: |- URL: %{url} box_loading_metadata: |- Loading metadata for box '%{name}' box_outdated: |- * '%{name}' for '%{provider}' is outdated! Current: %{current}. Latest: %{latest} box_outdated_checking_with_refresh: |- Checking if box '%{name}' is up to date... box_outdated_local: |- A newer version of the box '%{name}' is available and already installed, but your Vagrant machine is running against version '%{old}'. To update to version '%{new}', destroy and recreate your machine. box_outdated_metadata_download_error: |- There was a problem while downloading the metadata for your box to check for updates. This is not an error, since it is usually due to temporary network problems. This is just a warning. The problem encountered was: %{message} If you want to check for box updates, verify your network connection is valid and try again. box_outdated_metadata_error_single: |- Error loading box metadata while attempting to check for updates: %{message} box_outdated_single: |- A newer version of the box '%{name}' for provider '%{provider}' is available! You currently have version '%{current}'. The latest is version '%{latest}'. Run `vagrant box update` to update. box_outdated_metadata_error: |- * '%{name}' for '%{provider}': Error loading metadata: %{message} box_outdated_no_metadata: |- * '%{name}' for '%{provider}' wasn't added from a catalog, no version information box_updating: |- Updating '%{name}' with provider '%{provider}' from version '%{old}' to '%{new}'... box_update_checking: |- Checking for updates to '%{name}' box_up_to_date: |- * '%{name}' for '%{provider}' (v%{version}) is up to date box_up_to_date_single: |- Box '%{name}' (v%{version}) is running the latest version. cfengine_bootstrapping: |- Bootstrapping CFEngine with policy server: %{policy_server}... cfengine_bootstrapping_policy_hub: |- Performing additional bootstrap for policy hubs... cfengine_cant_detect: |- Vagrant doesn't support detecting whether CFEngine is installed for the guest OS running in the machine. Vagrant will assume it is installed and attempt to continue. cfengine_detected_ip: |- Detected policy server IP address: %{address}... cfengine_installing: |- Installing CFEngine onto machine... cfengine_installing_files_path: |- Copying the 'files_path' files... cfengine_no_bootstrap: |- CFEngine doesn't require bootstrap. Not bootstrapping. cfengine_single_run: |- CFEngine running in "single run" mode. Will execute one file. cfengine_single_run_execute: |- Executing run file for CFEngine... chef_cant_detect: |- Vagrant does not support detecting whether Chef is installed for the guest OS running in the machine. Vagrant will assume it is installed and attempt to continue. chef_already_installed: |- Detected Chef (%{version}) is already installed chef_installing: |- Installing Chef (%{version})... chef_client_cleanup_failed: |- Cleaning up the '%{deletable}' for Chef failed. The stdout and stderr are shown below. Vagrant will continue destroying the machine, so please clean up these resources manually. stdout: %{stdout} stderr: %{stderr} chef_config_knife_not_found: |- The `knife` application was not found! This is required by Vagrant to automatically delete Chef nodes and clients. chef_run_list_empty: |- Warning: Chef run list is empty. This may not be what you want. cli_interrupt: |- Exiting due to interrupt. docker_auto_start_not_available: |- Unable to configure automatic restart of Docker containers on the guest machine docker_cant_detect: |- Vagrant doesn't support detecting whether Docker is installed for the guest OS running in the machine. Vagrant will assume it is installed and attempt to continue. docker_configure_autostart: |- Configuring Docker to autostart containers... docker_installing: |- Installing Docker onto machine... docker_pulling_images: Pulling Docker images... docker_pulling_single: |- -- Image: %{name} docker_building_single: |- -- Path: %{path} docker_building_images: Building Docker images... docker_running: |- -- Container: %{name} docker_restarting_container_args: |- -- Detected changes to container '%{name}' args, restarting... docker_restarting_container_image: |- -- Detected newer image for container '%{name}', restarting... docker_starting_containers: |- Starting Docker containers... inserted_key: |- Key inserted! Disconnecting and reconnecting using new SSH key... inserting_insecure_detected: |- Vagrant insecure key detected. Vagrant will automatically replace this with a newly generated keypair for better security. inserting_random_key: |- Inserting generated public key within guest... inserting_remove_key: |- Removing insecure key from the guest if it's present... installing_provider: |- Provider '%{provider}' not found. We'll automatically install it now... installing_provider_detail: |- The installation process will start below. Human interaction may be required at some points. If you're uncomfortable with automatically installing this provider, you can safely Ctrl-C this process and install it manually. list_commands: |- Below is a listing of all available Vagrant commands and a brief description of what they do. %{list} moved_cwd: |- This machine used to live in %{old_wd} but it's now at %{current_wd}. Depending on your current provider you may need to change the name of the machine to run it as a different machine. guest_deb_installing_smb: |- Installing SMB "mount.cifs"... global_status_footer: |- The above shows information about all known Vagrant environments on this machine. This data is cached and may not be completely up-to-date. To interact with any of the machines, you can go to that directory and run Vagrant, or you can use the ID directly with Vagrant commands from any directory. For example: "vagrant destroy 1a2b3c4d" global_status_none: |- There are no active Vagrant environments on this computer! Or, you haven't destroyed and recreated Vagrant environments that were started with an older version of Vagrant. plugin_needs_reinstall: |- The following plugins were installed with a version of Vagrant that had different versions of underlying components. Because these component versions were changed (which rarely happens), the plugins must be uninstalled and reinstalled. To ensure that all the dependencies are properly updated as well it is _highly recommended_ to do a `vagrant plugin uninstall` prior to reinstalling. This message will not go away until all the plugins below are either uninstalled or uninstalled then reinstalled. The plugins below will not be loaded until they're uninstalled and reinstalled: %{names} post_up_message: |- Machine '%{name}' has a post `vagrant up` message. This is a message from the creator of the Vagrantfile, and not from Vagrant itself: %{message} provisioner_cleanup: |- Running cleanup tasks for '%{name}' provisioner... rsync_auto_initial: |- Doing an initial rsync... rsync_auto_new_folders: |- New synced folders were added to the Vagrantfile since running `vagrant reload`. If these new synced folders are backed by rsync, they won't be automatically synced until a `vagrant reload` is run. rsync_auto_no_paths: |- There are no paths to watch! This is either because you have no synced folders using rsync, or any rsync synced folders you have have specified `rsync_auto` to be false. rsync_auto_path: |- Watching: %{path} rsync_auto_remove_folder: |- Not syncing %{folder} as it is not part of the current working directory. rsync_auto_rsync_error: |- There was an error while executing rsync. The error is shown below. This may not be critical since rsync sometimes fails, but if this message repeats, then please fix the issue: %{message} rsync_communicator_not_ready: |- The machine is reporting that it is not ready for rsync to communicate with it. Verify that this machine is properly running. rsync_communicator_not_ready_callback: |- Failed to connect to remote machine. This is usually caused by the machine rebooting or being halted. Please make sure the machine is running, and modify a file to try again. rsync_folder: |- Rsyncing folder: %{hostpath} => %{guestpath} rsync_folder_excludes: " - Exclude: %{excludes}" rsync_installing: "Installing rsync to the VM..." rsync_proxy_machine: |- The provider ('%{provider}') for the machine '%{name}' is using a proxy machine. RSync will sync to this proxy instead of directly to the environment itself. rsync_showing_output: "Showing rsync output..." rsync_ssh_password: |- The machine you're rsyncing folders to is configured to use password-based authentication. Vagrant can't script rsync to automatically enter this password, so you'll likely be prompted for a password shortly. If you don't want to have to do this, please enable automatic key insertion using `config.ssh.insert_key`. ssh_exec_password: |- The machine you're attempting to SSH into is configured to use password-based authentication. Vagrant can't script entering the password for you. If you're prompted for a password, please enter the same password you have configured in the Vagrantfile. stdin_cant_hide_input: |- Error! Your console doesn't support hiding input. We'll ask for input again below, but we WILL NOT be able to hide input. If this is a problem for you, ctrl-C to exit and fix your stdin. up_no_machines: |- No machines to bring up. This is usually because all machines are set to `autostart: false`, which means you have to explicitly specify the name of the machine to bring up. upgrading_home_path_v1_5: |- Vagrant is upgrading some internal state for the latest version. Please do not quit Vagrant at this time. While upgrading, Vagrant will need to copy all your boxes, so it will use a considerable amount of disk space. After it is done upgrading, the temporary disk space will be freed. Press ctrl-c now to exit if you want to remove some boxes or free up some disk space. Press the Enter or Return key to continue. version_current: |- Installed Version: %{version} version_latest: |- Latest Version: %{version} version_latest_installed: |- You're running an up-to-date version of Vagrant! version_no_checkpoint: |- Vagrant was unable to check for the latest version of Vagrant. Please check manually at https://www.vagrantup.com version_upgrade_available: |- A new version of Vagrant is available: %{latest_version}! To upgrade visit: https://www.vagrantup.com/downloads.html version_upgrade_howto: |- To upgrade to the latest version, visit the downloads page and download and install the latest version of Vagrant from the URL below: https://www.vagrantup.com/downloads.html If you're curious what changed in the latest release, view the CHANGELOG below: https://github.com/mitchellh/vagrant/blob/v%{version}/CHANGELOG.md cfengine_config: classes_array: |- The 'classes' configuration must be an array. files_path_not_directory: |- The 'files_path' must point to a valid directory. invalid_mode: |- The mode must be 'bootstrap' or 'single_run' policy_server_address: |- The policy server address must be set for bootstrapping. run_file_not_found: |- The 'run_file' specified could not be found. virtualbox: checking_guest_additions: |- Checking for guest additions in VM... network_adapter: |- Adapter %{adapter}: %{type}%{extra} config: id_in_pre_import: |- The ':id' parameter is not available in "pre-import" customizations. intnet_on_bad_type: |- VirtualBox internal networks can only be enabled on "private_network" invalid_event: |- %{event} is not a valid event for customization. Valid events are: %{valid_events} warning: shared_folder_symlink_create: |- Vagrant is currently configured to create VirtualBox synced folders with the `SharedFoldersEnableSymlinksCreate` option enabled. If the Vagrant guest is not trusted, you may want to disable this option. For more information on this option, please refer to the VirtualBox manual: https://www.virtualbox.org/manual/ch04.html#sharedfolders This option can be disabled globally with an environment variable: VAGRANT_DISABLE_VBOXSYMLINKCREATE=1 or on a per folder basis within the Vagrantfile: config.vm.synced_folder '/host/path', '/guest/path', SharedFoldersEnableSymlinksCreate: false general: batch_notify_error: |- An error occurred. The error will be shown after all tasks complete. batch_unexpected_error: |- An unexpected error occurred when executing the action on the '%{machine}' machine. Please report this as a bug: %{message} batch_vagrant_error: |- An error occurred while executing the action on the '%{machine}' machine. Please handle this error then try again: %{message} config_upgrade_messages: |- There were warnings and/or errors while loading your Vagrantfile for the machine '%{name}'. Your Vagrantfile was written for an earlier version of Vagrant, and while Vagrant does the best it can to remain backwards compatible, there are some cases where things have changed significantly enough to warrant a message. These messages are shown below. %{output} not_in_installer: |- You appear to be running Vagrant outside of the official installers. Note that the installers are what ensure that Vagrant has all required dependencies, and Vagrant assumes that these dependencies exist. By running outside of the installer environment, Vagrant may not function properly. To remove this warning, install Vagrant using one of the official packages from vagrantup.com. upgraded_v1_dotfile: |- A Vagrant 1.0.x state file was found for this environment. Vagrant has gone ahead and auto-upgraded this to the latest format. Everything should continue working as normal. Beware, however, that older versions of Vagrant may no longer be used with this environment. However, in case anything went wrong, the old dotfile was backed up to the location below. If everything is okay, it is safe to remove this backup. Backup: %{backup_path} #------------------------------------------------------------------------------- # Translations for exception classes #------------------------------------------------------------------------------- errors: active_machine_with_different_provider: |- An active machine was found with a different provider. Vagrant currently allows each machine to be brought up with only a single provider at a time. A future version will remove this limitation. Until then, please destroy the existing machine to up with a new provider. Machine name: %{name} Active provider: %{active_provider} Requested provider: %{requested_provider} batch_multi_error: |- An error occurred while executing multiple actions in parallel. Any errors that occurred are shown below. %{message} box_add_no_matching_provider: |- The box you're attempting to add doesn't support the provider you requested. Please find an alternate box or use an alternate provider. Double-check your requested provider to verify you didn't simply misspell it. If you're adding a box from HashiCorp's Vagrant Cloud, make sure the box is released. Name: %{name} Address: %{url} Requested provider: %{requested} box_add_no_matching_version: |- The box you're attempting to add has no available version that matches the constraints you requested. Please double-check your settings. Also verify that if you specified version constraints, that the provider you wish to use is available for these constraints. Box: %{name} Address: %{url} Constraints: %{constraints} Available versions: %{versions} box_add_short_not_found: |- The box '%{name}' could not be found or could not be accessed in the remote catalog. If this is a private box on HashiCorp's Vagrant Cloud, please verify you're logged in via `vagrant login`. Also, please double-check the name. The expanded URL and error message are shown below: URL: %{url} Error: %{error} boot_bad_state: |- The guest machine entered an invalid state while waiting for it to boot. Valid states are '%{valid}'. The machine is in the '%{invalid}' state. Please verify everything is configured properly and try again. If the provider you're using has a GUI that comes with it, it is often helpful to open that and watch the machine, since the GUI often has more helpful error messages than Vagrant can retrieve. For example, if you're using VirtualBox, run `vagrant up` while the VirtualBox GUI is open. The primary issue for this error is that the provider you're using is not properly configured. This is very rarely a Vagrant issue. boot_timeout: |- Timed out while waiting for the machine to boot. This means that Vagrant was unable to communicate with the guest machine within the configured ("config.vm.boot_timeout" value) time period. If you look above, you should be able to see the error(s) that Vagrant had when attempting to connect to the machine. These errors are usually good hints as to what may be wrong. If you're using a custom box, make sure that networking is properly working and you're able to connect to the machine. It is a common problem that networking isn't setup properly in these boxes. Verify that authentication configurations are also setup properly, as well. If the box appears to be booting properly, you may want to increase the timeout ("config.vm.boot_timeout") value. box_add_exists: |- The box you're attempting to add already exists. Remove it before adding it again or add it with the `--force` flag. Name: %{name} Provider: %{provider} Version: %{version} box_add_direct_version: |- You specified a box version constraint with a direct box file path. Box version constraints only work with boxes from Vagrant Cloud or a custom box host. Please remove the version constraint and try again. box_add_metadata_multi_url: |- Multiple URLs for a box can't be specified when adding versioned boxes. Please specify a single URL to the box metadata (JSON) information. The full list of URLs you specified is shown below: %{urls} box_add_name_mismatch: |- The box you're adding has a name different from the name you requested. For boxes with metadata, you cannot override the name. If you're adding a box using `vagrant box add`, don't specify the `--name` parameter. If the box is being added via a Vagrantfile, change the `config.vm.box` value to match the name below. Requested name: %{requested_name} Actual name: %{actual_name} box_add_name_required: |- A name is required when adding a box file directly. Please pass the `--name` parameter to `vagrant box add`. See `vagrant box add -h` for more help. box_checksum_invalid_type: |- The specified checksum type is not supported by Vagrant: %{type}. Vagrant supports the following checksum types: md5, sha1, sha256 box_checksum_mismatch: |- The checksum of the downloaded box did not match the expected value. Please verify that you have the proper URL setup and that you're downloading the proper file. Expected: %{expected} Received: %{actual} box_config_changing_box: |- While loading the Vagrantfile, the provider override specified a new box. This box, in turn, specified a different box. This isn't allowed, as it could lead to infinite recursion of Vagrant configuration loading. Please fix this. box_metadata_corrupted: |- The metadata associated with the box '%{name}' appears corrupted. This is most often caused by a disk issue or system crash. Please remove the box, re-add it, and try again. box_metadata_download_error: |- There was an error while downloading the metadata for this box. The error message is shown below: %{message} box_metadata_file_not_found: |- The "metadata.json" file for the box '%{name}' was not found. Boxes require this file in order for Vagrant to determine the provider it was made for. If you made the box, please add a "metadata.json" file to it. If someone else made the box, please notify the box creator that the box is corrupt. Documentation for box file format can be found at the URL below: https://www.vagrantup.com/docs/boxes/format.html box_metadata_malformed: |- The metadata for the box was malformed. The exact error is shown below. Please contact the maintainer of the box so that this issue can be fixed. %{error} box_metadata_malformed_version: |- A version of the box you're loading is formatted in a way that Vagrant cannot parse: '%{version}'. Please reformat the version to be properly formatted. It should be in the format of X.Y.Z. box_not_found: |- The box '%{name}' does not exist. Please double check and try again. You can see the boxes that are installed with `vagrant box list`. box_not_found_with_provider: |- The box '%{name}' isn't installed for the provider '%{provider}'. Please double-check and try again. The installed providers for the box are shown below: %{providers} box_not_found_with_provider_and_version: |- The box '%{name}' (v%{version}) with provider '%{provider}' could not be found. Please double check and try again. You can see all the boxes that are installed with `vagrant box list`. box_provider_doesnt_match: |- The box you attempted to add doesn't match the provider you specified. Provider expected: %{expected} Provider of box: %{actual} box_remove_multi_provider: |- You requested to remove the box '%{name}'. This box has multiple providers. You must explicitly select a single provider to remove with `--provider`. Available providers: %{providers} box_remove_multi_version: |- You requested to remove the box '%{name}' with provider '%{provider}'. This box has multiple versions. You must explicitly specify which version you want to remove with the `--box-version` flag or specify the `--all` flag to remove all versions. The available versions for this box are: %{versions} box_remove_not_found: |- The box you requested to be removed could not be found. No boxes named '%{name}' could be found. box_remove_provider_not_found: |- You requested to remove the box '%{name}' with provider '%{provider}'. The box '%{name}' exists but not with the provider specified. Please double-check and try again. The providers for this are: %{providers} box_remove_version_not_found: |- You requested to remove the box '%{name}' version '%{version}' with provider '%{provider}', but that specific version of the box is not installed. Please double-check and try again. The available versions for this box are: %{versions} box_server_not_set: |- A URL to a Vagrant Cloud server is not set, so boxes cannot be added with a shorthand ("mitchellh/precise64") format. You may also be seeing this error if you meant to type in a path to a box file which doesn't exist locally on your system. To set a URL to a Vagrant Cloud server, set the `VAGRANT_SERVER_URL` environmental variable. Or, if you meant to use a file path, make sure the path to the file is valid. box_update_multi_provider: |- You requested to update the box '%{name}'. This box has multiple providers. You must explicitly select a single provider to remove with `--provider`. Available providers: %{providers} box_update_no_box: |- Box '%{name}' not installed, can't check for updates. box_update_no_metadata: |- The box '%{name}' is not a versioned box. The box was added directly instead of from a box catalog. Vagrant can only check the versions of boxes that were added from a catalog such as from the public Vagrant Server. box_update_no_name: |- This machine doesn't have a box. Won't update anything. bundler_disabled: |- Vagrant's built-in bundler management mechanism is disabled because Vagrant is running in an external bundler environment. In these cases, plugin management does not work with Vagrant. To install plugins, use your own Gemfile. To load plugins, either put the 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: %{message} cant_read_mac_addresses: |- The provider you are using ('%{provider}') doesn't support the "nic_mac_addresses" provider capability which is required for advanced networking to work with this guest OS. Please inform the author of the provider to add this feature. Until then, you must remove any networking configurations other than forwarded ports from your Vagrantfile for Vagrant to continue. capability_host_explicit_not_detected: |- The explicit capability host specified of '%{value}' could not be found. This is an internal error that users should never see. Please report a bug. capability_host_not_detected: |- The capability host could not be detected. This is an internal error that users should never see. Please report a bug. capability_invalid: |- The capability '%{cap}' is invalid. This is an internal error that users should never see. Please report a bug. capability_not_found: |- The capability '%{cap}' could not be found. This is an internal error that users should never see. Please report a bug. cfengine_bootstrap_failed: |- Failed to bootstrap CFEngine. Please see the output above to see what went wrong and address the issue. cfengine_cant_autodetect_ip: |- Vagrant was unable to automatically detect the IP address of the running machine to use as the policy server address. Please specify the policy server address manually, or verify that the networks are configured properly internally. cfengine_install_failed: |- After installing CFEngine, Vagrant still can't detect a proper CFEngine installation. Please verify that CFEngine was properly installed, as the installation may have failed. cfengine_not_installed: |- CFEngine appears not to be installed on the guest machine. Vagrant can attempt to install CFEngine for you if you set the "install" setting to "true", but this setting was not set. Please install CFEngine on the guest machine or enable the "install" setting for Vagrant to attempt to install it for you. cli_invalid_options: |- An invalid option was specified. The help for this command is available below. %{help} cli_invalid_usage: |- This command was not invoked properly. The help for this command is available below. %{help} clone_not_found: |- The specified Vagrantfile to clone from was not found. Please verify the `config.vm.clone` setting points to a valid Vagrantfile. clone_machine_not_found: |- The clone environment hasn't been created yet. To clone from another Vagrantfile, it must already be created with `vagrant up`. It doesn't need to be running. Additionally, the created environment must be started with a provider matching this provider. For example, if you're using VirtualBox, the clone environment must also be using VirtualBox. command_deprecated: |- The command 'vagrant %{name}' has been deprecated and is no longer functional within Vagrant. command_unavailable: |- The executable '%{file}' Vagrant is trying to run was not found in the PATH variable. This is an error. Please verify this software is installed and on the path. command_unavailable_windows: |- The executable '%{file}' Vagrant is trying to run was not found in the %PATH% variable. This is an error. Please verify this software is installed and on the path. communicator_not_found: |- The requested communicator '%{comm}' could not be found. Please verify the name is correct and try again. config_invalid: |- There are errors in the configuration of this machine. Please fix the following errors and try again: %{errors} config_upgrade_errors: |- Because there were errors upgrading your Vagrantfiles, Vagrant can no longer continue. Please fix the errors above and try again. copy_private_key_failed: |- Vagrant failed to copy the default insecure private key into your home directory. This is usually caused by a permissions error. Please make sure the permissions of the source is readable and the destination is writable. Source: %{source} Destination: %{destination} corrupt_machine_index: |- The machine index which stores all required information about running Vagrant environments has become corrupt. This is usually caused by external tampering of the Vagrant data folder. Vagrant cannot manage any Vagrant environments if the index is corrupt. Please attempt to manually correct it. If you are unable to manually correct it, then remove the data file at the path below. This will leave all existing Vagrant environments "orphaned" and they'll have to be destroyed manually. Path: %{path} destroy_requires_force: |- Destroy doesn't have a TTY to ask for confirmation. Please pass the `--force` flag to force a destroy, otherwise attach a TTY so that the destroy can be confirmed. dotfile_upgrade_json_error: |- A Vagrant 1.0.x local state file was found. Vagrant is able to upgrade this to the latest format automatically, however various checks are put in place to verify data isn't incorrectly deleted. In this case, the old state file was not valid JSON. Vagrant 1.0.x would store state as valid JSON, meaning that this file was probably tampered with or manually edited. Vagrant's auto-upgrade process cannot continue in this case. In most cases, this can be resolve by simply removing the state file. Note however though that if Vagrant was previously managing virtual machines, they may be left in an "orphan" state. That is, if they are running or exist, they'll have to manually be removed. If you're unsure what to do, ask the Vagrant mailing list or contact support. State file path: %{state_file} downloader_error: |- An error occurred while downloading the remote file. The error message, if any, is reproduced below. Please fix this error and try again. %{message} downloader_interrupted: |- The download was interrupted by an external signal. It did not complete. downloader_checksum_error: |- The calculated checksum of the requested file does not match the expected checksum! File source: %{source} Checsum type: %{type} Expected checksum: %{expected_checksum} Calculated checksum: %{actual_checksum} env_inval: |- Vagrant received an "EINVAL" error while attempting to set some environment variables. This is usually caused by the total size of your environment variables being too large. Vagrant sets a handful of environment variables to function and requires this to work. Please delete some environment variables prior to executing Vagrant to fix this. environment_locked: |- Vagrant attempted to acquire a lock named '%{name}', but this lock is being held by another instance of Vagrant already. Please wait and try again. environment_non_existent_cwd: |- The working directory for Vagrant doesn't exist! This is the specified working directory: %{cwd} forward_port_adapter_not_found: |- The adapter to attach a forwarded port to was not found. Please verify that the given adapter is setup on the machine as a NAT interface. Host port: %{host} Guest port: %{guest} Adapter: %{adapter} freebsd_nfs_whitespace: |- FreeBSD hosts do not support sharing directories with whitespace in their path. Please adjust your path accordingly. guest_capability_invalid: |- The registered guest capability '%{cap}' for the detected guest OS '%{guest}' is invalid. The capability does not implement the proper method. This is a bug with Vagrant or the plugin that implements this capability. Please report a bug. guest_capability_not_found: |- Vagrant attempted to execute the capability '%{cap}' on the detect guest OS '%{guest}', but the guest doesn't support that capability. This capability is required for your configuration of Vagrant. Please either reconfigure Vagrant to avoid this capability or fix the issue by creating the capability. guest_explicit_not_detected: |- The guest implementation explicitly specified in your Vagrantfile ("%{value}") could not be found. Please verify that the plugin is installed which implements this guest and that the value you used for `config.vm.guest` is correct. guest_not_detected: |- The guest operating system of the machine could not be detected! Vagrant requires this knowledge to perform specific tasks such as mounting shared folders and configuring networks. Please add the ability to detect this guest operating system to Vagrant by creating a plugin or reporting a bug. home_dir_later_version: |- It appears that a newer version of Vagrant was run on this machine at some point. The current version of Vagrant is unable to read the configuration structure of this newer version. Please upgrade to the latest version of Vagrant. home_dir_not_accessible: |- The home directory you specified is not accessible. The home directory that Vagrant uses must be both readable and writable. You specified: %{home_path} home_dir_unknown_version: |- The Vagrant app data directory (%{path}) is in a structure Vagrant doesn't understand. This is a rare exception. Please report an issue or ask the mailing list for help. host_explicit_not_detected: |- The host implementation explicitly specified in your Vagrantfile ("%{value}") could not be found. Please verify that the plugin is installed which implements this host and that the value you used for `config.vagrant.host` is correct. interrupted: |- Vagrant exited after cleanup due to external interrupt. local_data_dir_not_accessible: |- The directory Vagrant will use to store local environment-specific state is not accessible. The directory specified as the local data directory must be both readable and writable for the user that is running Vagrant. Local data directory: %{local_data_path} linux_mount_failed: |- Failed to mount folders in Linux guest. This is usually because the "vboxsf" file system is not available. Please verify that the guest additions are properly installed in the guest and can work properly. The command attempted was: %{command} The error output from the last command was: %{output} linux_rdp_client_not_found: |- An appropriate RDP client was not found. Vagrant requires either `xfreerdp` or `rdesktop` in order to connect via RDP to the Vagrant environment. Please ensure one of these applications is installed and available on the path and try again. machine_action_locked: |- An action '%{action}' was attempted on the machine '%{name}', but another process is already executing an action on the machine. Vagrant locks each machine for access by only one process at a time. Please wait until the other Vagrant process finishes modifying this machine, then try again. If you believe this message is in error, please check the process listing for any "ruby" or "vagrant" processes and kill them. Then try again. machine_guest_not_ready: |- Guest-specific operations were attempted on a machine that is not ready for guest communication. This should not happen and a bug should be reported. machine_locked: |- Vagrant can't use the requested machine because it is locked! This means that another Vagrant process is currently reading or modifying the machine. Please wait for that Vagrant process to end and try again. Details about the machine are shown below: Name: %{name} Provider: %{provider} machine_not_found: |- The machine with the name '%{name}' was not found configured for this Vagrant environment. machine_state_invalid: |- An internal error has occurred! The provider of the machine you're trying to work with reported an invalid state. This is a bug with the provider you're using, and not with Vagrant itself or with any configuration you may have done. Please report this bug to the proper location. multi_vm_target_required: |- This command requires a specific VM name to target in a multi-VM environment. net_ssh_exception: |- An error occurred in the underlying SSH library that Vagrant uses. The error message is shown below. In many cases, errors from this library are caused by ssh-agent issues. Try disabling your SSH agent or removing some keys and try again. If the problem persists, please report a bug to the net-ssh project. %{message} network_type_not_supported: |- The %{type} network type is not supported for this box or guest. network_address_invalid: |- The IP address '%{ip}' is not valid. Please review the error message below to help resolve the issue: %{error_msg} network_manager_not_installed: |- Vagrant was instructed to configure the %{device} network device to be managed by NetworkManager. However, the configured guest VM does not have NetworkManager installed. To fix this error please remove the `nm_controlled` setting from local Vagantfile. If NetworkManager is required to manage the network devices, please use a box with NetworkManager installed. nfs_bad_exports: |- NFS is reporting that your exports file is invalid. Vagrant does this check before making any changes to the file. Please correct the issues below and execute "vagrant reload": %{output} nfs_exports_failed: |- Vagrant failed to install an updated NFS exports file. This may be due to overly restrictive permissions on your NFS exports file. Please validate them and try again. command: %{command} stdout: %{stdout} stderr: %{stderr} nfs_dupe_permissions: |- You have attempted to export the same nfs host path at %{hostpath} with different nfs permissions. Please pick one permission and reload your guest. nfs_cant_read_exports: |- Vagrant can't read your current NFS exports! The exports file should be readable by any user. This is usually caused by invalid permissions on your NFS exports file. Please fix them and try again. nfs_mount_failed: |- Mounting NFS shared folders failed. This is most often caused by the NFS client software not being installed on the guest machine. Please verify that the NFS client software is properly installed, and consult any resources specific to the linux distro you're using for more information on how to do this. nfs_no_guest_ip: |- No guest IP was given to the Vagrant core NFS helper. This is an internal error that should be reported as a bug. nfs_no_host_ip: |- No host IP was given to the Vagrant core NFS helper. This is an internal error that should be reported as a bug. nfs_no_hostonly_network: |- NFS requires a host-only network to be created. Please add a host-only network to the machine (with either DHCP or a static IP) for NFS to work. nfs_no_valid_ids: |- No valid IDs were given to the NFS synced folder implementation to prune. This is an internal bug with Vagrant and an issue should be filed. nfs_not_supported: |- It appears your machine doesn't support NFS, or there is not an adapter to enable NFS on this machine for Vagrant. Please verify that `nfsd` is installed on your machine, and try again. If you're on Windows, NFS isn't supported. If the problem persists, please contact Vagrant support. nfs_client_not_installed_in_guest: |- No NFS client was detected as installed in your guest machine. This is required for NFS synced folders to work. In addition to this, Vagrant doesn't know how to automatically install NFS for your machine, so you must do this manually. If this message is erroneous, you may disable this check by setting `config.nfs.verify_installed` to `false` in your Vagrantfile. no_default_provider: |- No usable default provider could be found for your system. Vagrant relies on interactions with 3rd party systems, known as "providers", to provide Vagrant with resources to run development environments. Examples are VirtualBox, VMware, Hyper-V. The easiest solution to this message is to install VirtualBox, which is available for free on all major platforms. If you believe you already have a provider available, make sure it is properly installed and configured. You can see more details about why a particular provider isn't working by forcing usage with `vagrant up --provider=PROVIDER`, which should give you a more specific error message for that particular provider. no_default_synced_folder_impl: |- No synced folder implementation is available for your synced folders! Please consult the documentation to learn why this may be the case. You may force a synced folder implementation by specifying a "type:" option for the synced folders. Available synced folder implementations are listed below. %{types} no_env: |- A Vagrant environment or target machine is required to run this command. Run `vagrant init` to create a new Vagrant environment. Or, get an ID of a target machine from `vagrant global-status` to run this command on. A final option is to change to a directory with a Vagrantfile and to try again. plugin_gem_not_found: |- The plugin '%{name}' could not be installed because it could not be found. Please double check the name and try again. plugin_install_license_not_found: |- The license file to install could not be found. Please verify the path you gave is correct. The path to the license file given was: '%{path}' plugin_install_space: |- The directory where plugins are installed (the Vagrant home directory) has a space in it. On Windows, there is a bug in Ruby when compiling plugins into directories with spaces. Please move your Vagrant home directory to a path without spaces and try again. plugin_install_version_conflict: |- The plugin(s) can't be installed due to the version conflicts below. This means that the plugins depend on a library version that conflicts with other plugins or Vagrant itself, creating an impossible situation where Vagrant wouldn't be able to load the plugins. You can fix the issue by either removing a conflicting plugin or by contacting a plugin author to see if they can address the conflict. %{conflicts} plugin_init_error: |- The plugins failed to initialize correctly. This may be due to manual modifications made within the Vagrant home directory. Vagrant can attempt to automatically correct this issue by running: vagrant plugin repair If Vagrant was recently updated, this error may be due to incompatible versions of dependencies. To fix this problem please remove and re-install all plugins. Vagrant can attempt to do this automatically by running: vagrant plugin expunge --reinstall Or you may want to try updating the installed plugins to their latest versions: vagrant plugin update Error message given during initialization: %{message} plugin_load_error: |- The plugins failed to load properly. The error message given is shown below. %{message} plugin_not_installed: |- The plugin '%{name}' is not installed. Please install it first. plugin_state_file_not_parsable: |- Failed to parse the state file "%{path}": %{message} Please remove the file and reinstall the plugins. If this error recurs, please report a bug. plugin_uninstall_system: |- The plugin you're attempting to uninstall ('%{name}') is a system plugin. This means that the plugin is part of the installation of Vagrant. These plugins cannot be removed. You can however, install a plugin with the same name to replace these plugins. User-installed plugins take priority over system-installed plugins. plugin_source_error: |- Vagrant failed to load a configured plugin source. This can be caused by a variety of issues including: transient connectivity issues, proxy filtering rejecting access to a configured plugin source, or a configured plugin source not responding correctly. Please review the error message below to help resolve the issue: %{error_msg} Source: %{source} powershell_not_found: |- Failed to locate the powershell executable on the available PATH. Please ensure powershell is installed and available on the local PATH, then run the command again. powershell_invalid_version: |- The version of powershell currently installed on this host is less than the required minimum version. Please upgrade the installed version of powershell to the minimum required version and run the command again. Installed version: %{installed_version} Minimum required version: %{minimum_version} pushes_not_defined: |- The Vagrantfile does not define any 'push' strategies. In order to use `vagrant push`, you must define at least one push strategy: config.push.define "ftp" do |push| # ... push-specific options end push_strategy_not_defined: |- The push strategy '%{name}' is not defined in the Vagrantfile. Defined strategy names are: %{pushes} push_strategy_not_loaded: |- There are no push strategies named '%{name}'. Please make sure you spelled it correctly. If you are using an external push strategy, you may need to install a plugin. Loaded push strategies are: %{pushes} push_strategy_not_provided: |- The Vagrantfile defines more than one 'push' strategy. Please specify a strategy. Defined strategy names are: %{pushes} package_include_symlink: |- A file or directory you're attempting to include with your packaged box has symlinks in it. Vagrant cannot include symlinks in the resulting package. Please remove the symlinks and try again. provider_cant_install: |- The provider '%{provider}' doesn't support automatic installation. This is a limitation of this provider. Please report this as a feature request to the provider in question. To install this provider, you'll have to do so manually. provider_checksum_mismatch: |- The checksum of the downloaded provider '%{provider}' did not match the expected value. If the problem persists, please install the provider manually. Expected: %{expected} Received: %{actual} provider_install_failed: |- Installation of the provider '%{provider}' failed! The stdout and stderr are shown below. Please read the error output, resolve it, and try again. If problem persists, please install the provider manually. Stdout: %{stdout} Stderr: %{stderr} provider_not_found: |- The provider '%{provider}' could not be found, but was requested to back the machine '%{machine}'. Please use a provider that exists. provider_not_usable: |- The provider '%{provider}' that was requested to back the machine '%{machine}' is reporting that it isn't usable on this system. The reason is shown below: %{message} provisioner_flag_invalid: |- '%{name}' is not a known provisioner. Please specify a valid provisioner. provisioner_winrm_unsupported: |- The provisioner '%{name}' doesn't support provisioning on Windows guests via WinRM. This is likely not a limitation of the provisioner itself but rather that Vagrant doesn't know how to run this provisioner over WinRM. If you'd like this provisioner to work over WinRM, please take a look at the Vagrant source code linked below and try to contribute back support. Thank you! https://github.com/mitchellh/vagrant rsync_error: |- There was an error when attempting to rsync a synced folder. Please inspect the error message below for more info. Host path: %{hostpath} Guest path: %{guestpath} Command: %{command} Error: %{stderr} rsync_guest_install_error: |- Installation of rsync into the guest has failed! The stdout and stderr are shown below. Please read the error output, resolve it and try again. If the problem persists, please install rsync manually within the guest. Command: %{command} Stdout: %{stdout} Stderr: %{stderr} rsync_not_found: |- "rsync" could not be found on your PATH. Make sure that rsync is properly installed on your system and available on the PATH. rsync_not_installed_in_guest: |- "rsync" was not detected as installed in your guest machine. This is required for rsync synced folders to work. In addition to this, Vagrant doesn't know how to automatically install rsync for your machine, so you must do this manually. scp_permission_denied: |- Failed to upload a file to the guest VM via SCP due to a permissions error. This is normally because the SSH user doesn't have permission to write to the destination location. Alternately, the user running Vagrant on the host machine may not have permission to read the file. Source: %{from} Dest: %{to} scp_unavailable: |- SSH server on the guest doesn't support SCP. Please install the necessary software to enable SCP on your guest operating system. shared_folder_create_failed: |- Failed to create the following shared folder on the host system. This is usually because Vagrant does not have sufficient permissions to create the folder. %{path} Please create the folder manually or specify another path to share. shell_expand_failed: |- Vagrant failed to determine the shell expansion of a guest path (probably for one of your shared folders). This is an extremely rare error case and most likely indicates an unusual configuration of the guest system. Please report a bug with your Vagrantfile and debug log. snapshot_force: |- You must include the `--force` option to replace an existing snapshot. snapshot_not_supported: |- This provider doesn't support snapshots. This may be intentional or this may be a bug. If this provider should support snapshots, then please report this as a bug to the maintainer of the provider. snapshot_not_found: |- The snapshot name `%{snapshot_name}` was not found for the virtual machine `%{machine}`. ssh_authentication_failed: |- SSH authentication failed! This is typically caused by the public/private keypair for the SSH user not being properly set on the guest VM. Please verify that the guest VM is setup with the proper public key, and that the private key path for Vagrant is setup properly as well. ssh_bad_exit_status: |- The following SSH command responded with a non-zero exit status. Vagrant assumes that this means the command failed! %{command} Stdout from the command: %{stdout} Stderr from the command: %{stderr} ssh_bad_exit_status_muted: |- The SSH command responded with a non-zero exit status. Vagrant assumes that this means the command failed. The output for this command should be in the log above. Please read the output to determine what went wrong. ssh_channel_open_fail: |- Failed to open an SSH channel on the remote end! This typically means that the maximum number of active sessions was hit on the SSH server. Please configure your remote SSH server to resolve this issue. ssh_connect_eacces: |- SSH is getting permission denied errors when attempting to connect to the IP for SSH. This is usually caused by network rules and not being able to connect to the specified IP. Please try changing the IP on which the guest machine binds to for SSH. ssh_connection_refused: |- SSH connection was refused! This usually happens if the VM failed to boot properly. Some steps to try to fix this: First, try reloading your VM with `vagrant reload`, since a simple restart sometimes fixes things. If that doesn't work, destroy your VM and recreate it with a `vagrant destroy` followed by a `vagrant up`. If that doesn't work, contact a Vagrant maintainer (support channels listed on the website) for more assistance. ssh_connection_aborted: |- SSH connection was aborted! This usually happens when the machine is taking too long to reboot or the SSH daemon is not properly configured on the VM. First, try reloading your machine with `vagrant reload`, since a simple restart sometimes fixes things. If that doesn't work, destroy your machine and recreate it with a `vagrant destroy` followed by a `vagrant up`. If that doesn't work, contact support. ssh_connection_reset: |- SSH connection was reset! This usually happens when the machine is taking too long to reboot. First, try reloading your machine with `vagrant reload`, since a simple restart sometimes fixes things. If that doesn't work, destroy your machine and recreate it with a `vagrant destroy` followed by a `vagrant up`. If that doesn't work, contact support. ssh_connection_timeout: |- Vagrant timed out while attempting to connect via SSH. This usually means that the VM booted, but there are issues with the SSH configuration or network connectivity issues. Please try to `vagrant reload` or `vagrant up` again. ssh_disconnected: |- The SSH connection was unexpectedly closed by the remote end. This usually indicates that SSH within the guest machine was unable to properly start up. Please boot the VM in GUI mode to check whether it is booting properly. ssh_no_route: |- While attempting to connect with SSH, a "no route to host" (EHOSTUNREACH) error was received. Please verify your network settings are correct and try again. ssh_host_down: |- While attempting to connect with SSH, a "host is down" (EHOSTDOWN) error was received. Please verify your SSH settings are correct and try again. ssh_invalid_shell: |- The configured shell (config.ssh.shell) is invalid and unable to properly execute commands. The most common cause for this is using a shell that is unavailable on the system. Please verify you're using the full path to the shell and that the shell is executable by the SSH user. ssh_insert_key_unsupported: |- Vagrant is configured to generate a random keypair and insert it onto the guest machine, but it appears Vagrant doesn't know how to do this with your guest OS. Please disable key insertion by setting `config.ssh.insert_key = false` in the Vagrantfile. After doing so, run `vagrant reload` for the setting to take effect. If you'd like Vagrant to learn how to insert keys on this OS, please open an issue with details about your environment. ssh_is_putty_link: |- The `ssh` executable found in the PATH is a PuTTY Link SSH client. Vagrant is only compatible with OpenSSH SSH clients. Please install an OpenSSH SSH client or manually SSH in using your existing client using the information below. Host: %{host} Port: %{port} Username: %{username} Private key: %{key_path} ssh_key_bad_owner: |- The private key to connect to the machine via SSH must be owned by the user running Vagrant. This is a strict requirement from SSH itself. Please fix the following key to be owned by the user running Vagrant: %{key_path} ssh_key_bad_permissions: |- The private key to connect to this box via SSH has invalid permissions set on it. The permissions of the private key should be set to 0600, otherwise SSH will ignore the key. Vagrant tried to do this automatically for you but failed. Please set the permissions on the following file to 0600 and then try running this command again: %{key_path} Note that this error occurs after Vagrant automatically tries to do this for you. The likely cause of this error is a lack of filesystem permissions or even filesystem functionality. For example, if your Vagrant data is on a USB stick, a common case is that chmod is not supported. The key will need to be moved to a filesystem that supports chmod. ssh_key_type_not_supported: |- The private key you're attempting to use with this Vagrant box uses an unsupported encryption type. The SSH library Vagrant uses does not support this key type. Please use `ssh-rsa` or `ssh-dss` instead. Note that sometimes keys in your ssh-agent can interfere with this as well, so verify the keys are valid there in addition to standard file paths. ssh_not_ready: |- The provider for this Vagrant-managed machine is reporting that it is not yet ready for SSH. Depending on your provider this can carry different meanings. Make sure your machine is created and running and try again. Additionally, check the output of `vagrant status` to verify that the machine is in the state that you expect. If you continue to get this error message, please view the documentation for the provider you're using. ssh_run_requires_keys: |- Using `vagrant ssh -c` requires key-based SSH authentication, but your Vagrant environment is configured to use only password-based authentication. Please configure your Vagrantfile with a private key to use this feature. Note that Vagrant can automatically insert a keypair and use that keypair for you. Just set `config.ssh.insert_key = true` in your Vagrantfile. ssh_unavailable: "`ssh` binary could not be found. Is an SSH client installed?" ssh_unavailable_windows: |- `ssh` executable not found in any directories in the %PATH% variable. Is an SSH client installed? Try installing Cygwin, MinGW or Git, all of which contain an SSH client. Or use your favorite SSH client with the following authentication information shown below: Host: %{host} Port: %{port} Username: %{username} Private key: %{key_path} synced_folder_unusable: |- The synced folder type '%{type}' is reporting as unusable for your current setup. Please verify you have all the proper prerequisites for using this shared folder type and try again. ui_expects_tty: |- Vagrant is attempting to interface with the UI in a way that requires a TTY. Most actions in Vagrant that require a TTY have configuration switches to disable this requirement. Please do that or run Vagrant with TTY. unimplemented_provider_action: |- Vagrant attempted to call the action '%{action}' on the provider '%{provider}', but this provider doesn't support this action. This is probably a bug in either the provider or the plugin calling this action, and should be reported. vagrantfile_exists: |- `Vagrantfile` already exists in this directory. Remove it before running `vagrant init`. vagrantfile_load_error: |- There was an error loading a Vagrantfile. The file being loaded and the error message are shown below. This is usually caused by a syntax error. Path: %{path} Line number: %{line} Message: %{exception_class}: %{message} vagrantfile_name_error: |- There was an error loading a Vagrantfile. The file being loaded and the error message are shown below. This is usually caused by an invalid or undefined variable. Path: %{path} Line number: %{line} Message: %{message} vagrantfile_syntax_error: |- There is a syntax error in the following Vagrantfile. The syntax error message is reproduced below for convenience: %{file} vagrantfile_template_not_found_error: |- The Vagrantfile template '%{path}' does not exist. Please double check the template path and try again. vagrantfile_write_error: |- The user that is running Vagrant doesn't have the proper permissions to write a Vagrantfile to the specified location. Please ensure that you call `vagrant init` in a location where the proper permissions are in place to create a Vagrantfile. vagrant_version_bad: |- This Vagrant environment has specified that it requires the Vagrant version to satisfy the following version requirements: %{requirements} You are running Vagrant %{version}, which does not satisfy these requirements. Please change your Vagrant version or update the Vagrantfile to allow this Vagrant version. However, be warned that if the Vagrantfile has specified another version, it probably has good reason to do so, and changing that may cause the environment to not function properly. vboxmanage_error: |- There was an error while executing `VBoxManage`, a CLI used by Vagrant for controlling VirtualBox. The command and stderr is shown below. Command: %{command} Stderr: %{stderr} vboxmanage_launch_error: |- There was an error running VBoxManage. This is usually a permissions problem or installation problem with VirtualBox itself, and not Vagrant. Please note the error message below (if any), resolve the issue, and try Vagrant again. %{message} vboxmanage_not_found_error: |- The "VBoxManage" command or one of its dependencies could not be found. Please verify VirtualBox is properly installed. You can verify everything is okay by running "VBoxManage --version" and verifying that the VirtualBox version is outputted. If you're on Windows and just installed VirtualBox, you have to log out and log back in for the new environmental variables to take effect. If you're on Linux or Mac, verify your PATH contains the folder that has VBoxManage in it. vboxmanage_not_found_wsl_error: |- The "VBoxManage.exe" command or one of its dependencies could not be found. Please verify VirtualBox is properly installed. You can verify everything is okay by running "VBoxManage.exe --version" and verifying that the VirtualBox version is outputted. If you just installed VirtualBox, you have to log out and log back in for the new environmental variables to take effect. Using the VirtualBox provider within the WSL requires VirtualBox executables to be available on the system PATH. virtualbox_broken_version_040214: |- Vagrant detected you have VirtualBox 4.2.14 installed. VirtualBox 4.2.14 contains a critical bug which prevents it from working with Vagrant. VirtualBox 4.2.16+ fixes this problem. Please upgrade VirtualBox. virtualbox_guest_property_not_found: |- Could not find a required VirtualBox guest property: %{guest_property} This is an internal error that should be reported as a bug. virtualbox_invalid_version: |- Vagrant has detected that you have a version of VirtualBox installed that is not supported by this version of Vagrant. Please install one of the supported versions listed below to use Vagrant: %{supported_versions} A Vagrant update may also be available that adds support for the version you specified. Please check www.vagrantup.com/downloads.html to download the latest version. virtualbox_kernel_module_not_loaded: |- VirtualBox is complaining that the kernel module is not loaded. Please run `VBoxManage --version` or open the VirtualBox GUI to see the error message which should contain instructions on how to fix this error. virtualbox_install_incomplete: |- VirtualBox is complaining that the installation is incomplete. Please run `VBoxManage --version` to see the error message which should contain instructions on how to fix this error. virtualbox_mount_failed: |- Vagrant was unable to mount VirtualBox shared folders. This is usually because the filesystem "vboxsf" is not available. This filesystem is made available via the VirtualBox Guest Additions and kernel module. Please verify that these guest additions are properly installed in the guest. This is not a bug in Vagrant and is usually caused by a faulty Vagrant box. For context, the command attempted was: %{command} The error output from the command was: %{output} virtualbox_mount_not_supported_bsd: |- Vagrant is not able to mount VirtualBox shared folders on BSD-based guests. BSD-based guests do not support the VirtualBox filesystem at this time. To change the type of the default synced folder, specify the type as rsync or nfs: config.vm.synced_folder ".", "/vagrant", type: "nfs" # or "rsync" Alternatively, if you do not need to mount the default synced folder, you can also disable it entirely: config.vm.synced_folder ".", "/vagrant", disabled: true You can read more about Vagrant's synced folder types and the various configuration options on the Vagrant website. This is not a bug in Vagrant. virtualbox_name_exists: |- The name of your virtual machine couldn't be set because VirtualBox is reporting another VM with that name already exists. Most of the time, this is because of an error with VirtualBox not cleaning up properly. To fix this, verify that no VMs with that name do exist (by opening the VirtualBox GUI). If they don't, then look at the folder in the error message from VirtualBox below and remove it if there isn't any information you need in there. VirtualBox error: %{stderr} virtualbox_no_name: |- Vagrant was unable to determine the recommended name for your VirtualBox VM. This is usually an issue with VirtualBox. The output from VirtualBox is shown below, which may contain an error to fix. The best way to fix this issue is usually to uninstall VirtualBox, restart your computer, then reinstall VirtualBox. VirtualBox output: %{output} virtualbox_no_room_for_high_level_network: |- There is no available slots on the VirtualBox VM for the configured high-level network interfaces. "private_network" and "public_network" network configurations consume a single network adapter slot on the VirtualBox VM. VirtualBox limits the number of slots to 8, and it appears that every slot is in use. Please lower the number of used network adapters. virtualbox_not_detected: |- Vagrant could not detect VirtualBox! Make sure VirtualBox is properly installed. Vagrant uses the `VBoxManage` binary that ships with VirtualBox, and requires this to be available on the PATH. If VirtualBox is installed, please find the `VBoxManage` binary and add it to the PATH environmental variable. virtualbox_user_mismatch: |- The VirtualBox VM was created with a user that doesn't match the current user running Vagrant. VirtualBox requires that the same user be used to manage the VM that was created. Please re-run Vagrant with that user. This is not a Vagrant issue. The UID used to create the VM was: %{original_uid} Your UID is: %{uid} virtualbox_version_empty: |- Vagrant detected that VirtualBox appears installed on your system, but calls to detect the version are returning empty. This is often indicative of installation issues with VirtualBox. Please verify that VirtualBox is properly installed. As a final verification, please run the following command manually and verify a version is outputted: %{vboxmanage} --version vm_creation_required: |- VM must be created before running this command. Run `vagrant up` first. vm_inaccessible: |- Your VM has become "inaccessible." Unfortunately, this is a critical error with VirtualBox that Vagrant can not cleanly recover from. Please open VirtualBox and clear out your inaccessible virtual machines or find a way to fix them. vm_name_exists: |- A VirtualBox machine with the name '%{name}' already exists. Please use another name or delete the machine with the existing name, and try again. vm_no_match: |- No virtual machines matched the regular expression given. vm_not_found: |- A VM by the name of %{name} was not found. vm_not_running: |- VM must be running to open SSH connection. Run `vagrant up` to start the virtual machine. test_key: "test value" wsl_vagrant_version_mismatch: |- Vagrant cannot currently enable access to manage machines within the Windows environment because the version of Vagrant installed on Windows does not match this version of Vagrant running within the Windows Subsystem for Linux. Please ensure both installation of Vagrant are the same. If you do not want update your Vagrant installations you can disable Windows access by unsetting the `VAGRANT_WSL_ACCESS_WINDOWS_USER` environment variable. Windows Vagrant version: %{windows_version} Windows Subsystem for Linux Vagrant version: %{wsl_version} wsl_vagrant_access_error: |- Vagrant will not operate outside the Windows Subsystem for Linux unless explicitly instructed. Due to the inability to enforce expected Linux file ownership and permissions on the Windows system, Vagrant will not make modifications to prevent unexpected errors. To learn more about this, and the options that are available, please refer to the Vagrant documentation: https://www.vagrantup.com/docs/other/wsl.html wsl_virtualbox_windows_access: |- Vagrant is unable to use the VirtualBox provider from the Windows Subsystem for Linux without access to the Windows environment. Enabling this access must be done with caution and an understanding of the implications. For more information on enabing Windows access and using VirtualBox from the Windows Subsystem for Linux, please refer to the Vagrant documentation: https://www.vagrantup.com/docs/other/wsl.html #------------------------------------------------------------------------------- # Translations for config validation errors #------------------------------------------------------------------------------- config: common: bad_field: "The following settings shouldn't exist: %{fields}" chef: cookbooks_path_empty: |- Missing required value for `chef.cookbooks_path'. nodes_path_empty: |- Missing required value for `chef.nodes_path'. environment_path_missing: |- Environment path not found: %{path} nodes_path_missing: |- Path specified for `nodes_path` does not exist: %{path} environment_path_required: |- When 'environment' is specified, you must provide 'environments_path'. cookbooks_path_missing: |- Cookbook path doesn't exist: %{path} custom_config_path_missing: |- Path specified for "custom_config_path" does not exist. server_url_empty: "Chef Server URL must be populated." validation_key_path: "Validation key path must be valid path to your Chef Server validation key." loader: bad_v1_key: |- Unknown configuration section '%{key}'. If this section was part of a Vagrant 1.0.x plugin, note that 1.0.x plugins are incompatible with 1.1+. root: sensitive_bad_type: |- Invalid type provided for `sensitive`. The sensitive option expects a string or an array of strings. bad_key: |- Unknown configuration section '%{key}'. ssh: private_key_missing: "`private_key_path` file must exist: %{path}" paranoid_deprecated: |- The key `paranoid` is deprecated. Please use `verify_host_key`. Supported values are exactly the same, only the name of the option has changed. vm: bad_version: |- Invalid box version constraints: %{version} box_download_ca_cert_not_found: |- "box_download_ca_cert" file not found: %{path} box_download_ca_path_not_found: |- "box_download_ca_path" directory not found: %{path} box_download_checksum_blank: |- Checksum type specified but "box_download_checksum" is blank box_download_checksum_notblank: |- Checksum specified but must also specify "box_download_checksum_type" box_missing: "A box must be specified." clone_and_box: "Only one of clone or box can be specified." hostname_invalid_characters: |- The hostname set for the VM should only contain letters, numbers, hyphens or dots. It cannot start with a hyphen or dot. name_invalid: |- The sub-VM name '%{name}' is invalid. Please don't use special characters. network_ip_ends_in_one: |- You assigned a static IP ending in ".1" to this machine. This is very often used by the router and can cause the network to not work properly. If the network doesn't work properly, try changing this IP. network_ip_required: |- An IP is required for a private network. network_fp_invalid_port: |- Ports to forward must be 1 to 65535 network_fp_host_not_unique: |- Forwarded port '%{host}' (host port) is declared multiple times with the protocol '%{protocol}'. network_fp_requires_ports: |- Forwarded port definitions require a "host" and "guest" value network_type_invalid: |- Network type '%{type}' is invalid. Please use a valid network type. provisioner_not_found: |- The '%{name}' provisioner could not be found. shared_folder_guestpath_duplicate: |- A shared folder guest path is used multiple times. Shared folders must all map to a unique guest path: %{path} shared_folder_guestpath_relative: |- The shared folder guest path must be absolute: %{path} shared_folder_hostpath_missing: |- The host path of the shared folder is missing: %{path} shared_folder_nfs_owner_group: |- Shared folders that have NFS enabled do not support owner/group attributes. Host path: %{path} shared_folder_mount_options_array: |- Shared folder mount options specified by 'mount_options' must be an array of options. #------------------------------------------------------------------------------- # Translations for commands. e.g. `vagrant x` #------------------------------------------------------------------------------- commands: deprecated: |- [DEPRECATION WARNING]: The Vagrant command 'vagrant %{name}' is scheduled be deprecated in an upcoming Vagrant release. common: vm_already_running: |- VirtualBox VM is already running. vm_not_created: "VM not created. Moving on..." vm_not_running: "VM is not currently running. Please, first bring it up with `vagrant up` then run this command." box: no_installed_boxes: "There are no installed boxes! Use `vagrant box add` to add some." remove_in_use_query: |- Box '%{name}' (v%{version}) with provider '%{provider}' appears to still be in use by at least one Vagrant environment. Removing the box could corrupt the environment. We recommend destroying these environments first: %{users} Are you sure you want to remove this box? [y/N] removing: |- Removing box '%{name}' (v%{version}) with provider '%{provider}'... destroy: confirmation: "Are you sure you want to destroy the '%{name}' VM? [y/N] " will_not_destroy: |- The VM '%{name}' will not be destroyed, since the confirmation was declined. warning: |- Destroying guests with `--parallel` automatically enables `--force`. Press ctrl-c to cancel. init: success: |- A `Vagrantfile` has been placed in this directory. You are now ready to `vagrant up` your first virtual environment! Please read the comments in the Vagrantfile as well as documentation on `vagrantup.com` for more information on using Vagrant. plugin: expunge_confirm: |- This command permanently deletes all currently installed user plugins. It should only be used when a repair command is unable to properly fix the system. Continue? expunge_request_reinstall: |- Would you like Vagrant to attempt to reinstall current plugins? expunge_complete: |- All user installed plugins have been removed from this Vagrant environment! expunge_reinstall: |- Vagrant will now attempt to reinstall user plugins that were removed. expunge_aborted: |- Vagrant expunge has been declined. Skipping removal of plugins. installed_license: |- The license for '%{name}' was successfully installed! installing_license: |- Installing license for '%{name}'... no_plugins: |- No plugins installed. plugin_require: " - Custom entrypoint: %{require}" plugin_version: " - Version Constraint: %{version}" installed: |- Installed the plugin '%{name} (%{version})'! installing: |- Installing the '%{name}' plugin. This can take a few minutes... uninstalling: |- Uninstalling the '%{name}' plugin... up_to_date: |- All plugins are up to date. updated: |- Updated '%{name}' to version '%{version}'! updating: |- Updating installed plugins... updating_specific: |- Updating plugins: %{names}. This may take a few minutes... post_install: |- Post install message from the '%{name}' plugin: %{message} repairing: |- Repairing currently installed plugins. This may take a few minutes... repair_complete: |- Installed plugins successfully repaired! repair_failed: |- Failed to automatically repair installed Vagrant plugins. To fix this problem remove all user installed plugins and reinstall. Vagrant can do this for you automatically by running the following command: vagrant plugin expunge --reinstall Failure message received during repair: %{message} snapshot: not_supported: |- This provider doesn't support snapshots. This may be intentional or this may be a bug. If this provider should support snapshots, then please report this as a bug to the maintainer of the provider. no_push_snapshot: |- No pushed snapshot found! Use `vagrant snapshot push` to push a snapshot to restore to. status: aborted: |- The VM is in an aborted state. This means that it was abruptly stopped without properly closing the session. Run `vagrant up` to resume this virtual machine. If any problems persist, you may have to destroy and restart the virtual machine. gurumeditation: |- The VM is in the "guru meditation" state. This is a rare case which means that an internal error in VitualBox caused the VM to fail. This is always the sign of a bug in VirtualBox. You can try to bring your VM back online with a `vagrant up`. inaccessible: |- The VM is inaccessible! This is a rare case which means that VirtualBox can't find your VM configuration. This usually happens when upgrading VirtualBox, moving to a new computer, etc. Please consult VirtualBox for how to handle this issue. output: |- Current machine states: %{states} %{message} not_created: |- The environment has not yet been created. Run `vagrant up` to create the environment. If a machine is not created, only the default provider will be shown. So if a provider is not listed, then the machine is not created for that environment. paused: |- The VM is paused. This VM may have been paused via the VirtualBox GUI or the VBoxManage command line interface. To unpause, please use the VirtualBox GUI and/or VBoxManage command line interface so that vagrant would be able to control the VM again. poweroff: |- The VM is powered off. To restart the VM, simply run `vagrant up` stopping: |- The VM is stopping. running: |- The VM is running. To stop this VM, you can run `vagrant halt` to shut it down forcefully, or you can run `vagrant suspend` to simply suspend the virtual machine. In either case, to restart it again, simply run `vagrant up`. saving: |- The VM is currently saving its state. In a few moments this state should transition to "saved." Please run `vagrant status` again in a few seconds. saved: |- To resume this VM, simply run `vagrant up`. stuck: |- The VM is "stuck!" This is a very rare state which means that VirtualBox is unable to recover the current state of the VM. The only known solution to this problem is to restart your machine, sorry. listing: |- This environment represents multiple VMs. The VMs are all listed above with their current state. For more information about a specific VM, run `vagrant status NAME`. up: upping: |- Bringing machine '%{name}' up with '%{provider}' provider... validate: success: |- Vagrantfile validated successfully. #------------------------------------------------------------------------------- # Translations for Vagrant middleware actions #------------------------------------------------------------------------------- actions: runner: waiting_cleanup: "Waiting for cleanup before exiting..." exit_immediately: "Exiting immediately, without cleanup!" vm: boot: booting: Booting VM... bridged_networking: available: |- Available bridged network interfaces: bridging: |- Bridging adapter #%{adapter} to '%{bridge}' enabling: |- Enabling bridged network... preparing: |- Preparing bridged networking... choice_help: |- When choosing an interface, it is usually the one that is being used to connect to the internet. specific_not_found: |- Specific bridge '%{bridge}' not found. You may be asked to specify which network to bridge to. check_box: not_found: |- Box '%{name}' was not found. Fetching box from specified URL for the provider '%{provider}'. Note that if the URL does not have a box for this provider, you should interrupt Vagrant now and add the box yourself. Otherwise Vagrant will attempt to download the full box prior to discovering this error. check_guest_additions: not_detected: |- No guest additions were detected on the base box for this VM! Guest additions are required for forwarded ports, shared folders, host only networking, and more. If SSH fails on this machine, please install the guest additions and repackage the box to continue. This is not an error message; everything may continue to work properly, in which case you may ignore this message. version_mismatch: |- The guest additions on this VM do not match the installed version of VirtualBox! In most cases this is fine, but in rare cases it can prevent things such as shared folders from working properly. If you see shared folder errors, please make sure the guest additions within the virtual machine match the version of VirtualBox you have installed on your host and reload your VM. Guest Additions Version: %{guest_version} VirtualBox Version: %{virtualbox_version} clear_forward_ports: deleting: Clearing any previously set forwarded ports... clear_network_interfaces: deleting: Clearing any previously set network interfaces... clear_shared_folders: deleting: Cleaning previously set shared folders... clone: setup_master: Preparing master VM for linked clones... setup_master_detail: |- This is a one time operation. Once the master VM is prepared, it will be used as a base for linked clones, making the creation of new VMs take milliseconds on a modern system. creating: Cloning VM... failure: Creation of the linked clone failed. create_master: failure: |- Failed to create lock-file for master VM creation for box %{box}. customize: failure: |- A customization command failed: %{command} The following error was experienced: %{error} Please fix this customization and try again. running: Running '%{event}' VM customizations... destroy: destroying: Destroying VM and associated drives... destroy_network: destroying: Destroying unused networking interface... disable_networks: disabling: Disabling host only networks... discard_state: discarding: Discarding saved state of VM... export: create_dir: Creating temporary directory for export... exporting: Exporting VM... power_off: "The Vagrant virtual environment you are trying to package must be powered off." forward_ports: auto_empty: |- Vagrant found a port collision for the specified port and virtual machine. While this port was marked to be auto-corrected, the ports in the auto-correction range are all also used. VM: %{vm_name} Forwarded port: %{guest_port} => %{host_port} collision_error: |- Vagrant cannot forward the specified ports on this VM, since they would collide with some other application that is already listening on these ports. The forwarded port to %{host_port} is already in use on the host machine. To fix this, modify your current project's Vagrantfile to use another port. Example, where '1234' would be replaced by a unique host port: config.vm.network :forwarded_port, guest: %{guest_port}, host: 1234 Sometimes, Vagrant will attempt to auto-correct this for you. In this case, Vagrant was unable to. This is usually because the guest machine is in a state which doesn't allow modifying port forwarding. You could try 'vagrant reload' (equivalent of running a halt followed by an up) so vagrant can attempt to auto-correct this upon booting. Be warned that any unsaved work might be lost. fixed_collision: |- Fixed port collision for %{guest_port} => %{host_port}. Now on port %{new_port}. forwarding: Forwarding ports... forwarding_entry: |- %{guest_port} (guest) => %{host_port} (host) (adapter %{adapter}) non_nat: |- VirtualBox adapter #%{adapter} not configured as "NAT". Skipping port forwards on this adapter. privileged_ports: |- You are trying to forward to privileged ports (ports <= 1024). Most operating systems restrict this to only privileged process (typically processes running as an administrative user). This is a warning in case the port forwarding doesn't work. If any problems occur, please try a port higher than 1024. halt: force: |- Forcing shutdown of VM... graceful: |- Attempting graceful shutdown of VM... guest_not_ready: |- Guest communication could not be established! This is usually because SSH is not running, the authentication information was changed, or some other networking issue. Vagrant will force halt, if capable. hostname: setting: "Setting hostname..." import: importing: Importing base box '%{name}'... failure: |- The VM import failed! Try running `VBoxManage import` on the box file manually for more verbose error output. match_mac: matching: Matching MAC address for NAT networking... no_base_mac: |- No base MAC address was specified. This is required for the NAT networking to work properly (and hence port forwarding, SSH, etc.). Specifying this MAC address is typically up to the box and box maintainer. Please contact the relevant person to solve this issue. network: configuring: |- Configuring and enabling network interfaces... dhcp_already_attached: |- A host only network interface you're attempting to configure via DHCP already has a conflicting host only adapter with DHCP enabled. The DHCP on this adapter is incompatible with the DHCP settings. Two host only network interfaces are not allowed to overlap, and each host only network interface can have only one DHCP server. Please reconfigure your host only network or remove the virtual machine using the other host only network. preparing: |- Preparing network interfaces based on configuration... cleanup_vbox_default_dhcp: |- Found default DHCP server from initial VirtualBox install. Cleaning it up... host_only_network: collides: |- The specified host network collides with a non-hostonly network! This will cause your specified IP to be inaccessible. Please change the IP or name of your host only network so that it no longer matches that of a bridged or non-hostonly network. creating: "Creating new host only network for environment..." enabling: "Enabling host only network..." not_found: |- The specified host network could not be found: '%{name}.' If the name specification is removed, Vagrant will create a new host only network for you. Alternatively, please create the specified network manually. preparing: "Preparing host only network..." nfs: exporting: Exporting NFS shared folders... installing: "Installing NFS client..." mounting: Mounting NFS shared folders... v4_with_udp_warning: |- WARNING: Invalid NFS settings detected! Detected UDP enabled with NFSv4. NFSv4 does not support UDP. If folder mount fails disable UDP using the following option: nfs_udp: false For more information see: RFC5661: https://tools.ietf.org/html/rfc5661#section-2.9.1 RFC7530: https://tools.ietf.org/html/rfc7530#section-3.1 provision: beginning: "Running provisioner: %{provisioner}..." disabled_by_config: |- Machine not provisioned because `--no-provision` is specified. disabled_by_sentinel: |- Machine already provisioned. Run `vagrant provision` or use the `--provision` flag to force provisioning. Provisioners marked to run always will still run. resume: resuming: Resuming suspended VM... unpausing: |- Unpausing the VM... share_folders: creating: Creating shared folders metadata... mounting: Mounting shared folders... mounting_entry: "%{guestpath} => %{hostpath}" nomount_entry: "Automounting disabled: %{hostpath}" set_name: setting_name: |- Setting the name of the VM: %{name} snapshot: deleting: |- Deleting the snapshot '%{name}'... deleted: |- Snapshot deleted! list_none: |- No snapshots have been taken yet! list_none_detail: |- You can take a snapshot using `vagrant snapshot save`. Note that not all providers support this yet. Once a snapshot is taken, you can list them using this command, and use commands such as `vagrant snapshot restore` to go back to a certain snapshot. restoring: |- Restoring the snapshot '%{name}'... saving: |- Snapshotting the machine as '%{name}'... saved: |- Snapshot saved! You can restore the snapshot at any time by using `vagrant snapshot restore`. You can delete it using `vagrant snapshot delete`. suspend: suspending: Saving VM state and suspending execution... box: unpackage: untar_failure: |- The box failed to unpackage properly. Please verify that the box file you're trying to add is not corrupted and that enough disk space is available and then try again. The output from attempting to unpackage (if any): %{output} add: adding: |- Extracting box... checksumming: |- Calculating and comparing box checksum... destroy: destroying: "Deleting box '%{name}'..." download: cleaning: "Cleaning up downloaded box..." download_failed: |- Download failed. Will try another box URL if there is one. interrupted: "Box download was interrupted. Exiting." resuming: "Box download is resuming from prior download progress" verify: verifying: "Verifying box..." failed: |- The box file you're attempting to add is invalid. This can be commonly attributed to typos in the path given to the box add command. Another common case of this is invalid packaging of the box itself. general: package: packaging: "Packaging additional file: %{file}" compressing: "Compressing package to: %{fullpath}" box_folder: "Creating new folder: %{folder_path}" output_exists: |- The specified file '%{filename}' to save the package as already exists. Please remove this file or specify a different file name for outputting. output_is_directory: |- The specified output is a directory. Please specify a path including a filename. requires_directory: |- A directory was not specified to package. This should never happen and is a result of an internal inconsistency. Please report a bug on the Vagrant bug tracker. include_file_missing: |- Package include file doesn't exist: %{file} hosts: bsd: nfs_export: |- Preparing to edit /etc/exports. Administrator privileges will be required... nfs_prune: |- Pruning invalid NFS exports. Administrator privileges will be required... darwin: virtualbox_install_download: |- Downloading VirtualBox %{version}... virtualbox_install_detail: |- This may not be the latest version of VirtualBox, but it is a version that is known to work well. Over time, we'll update the version that is installed. virtualbox_install_install: |- Installing VirtualBox. This will take a few minutes... virtualbox_install_install_detail: |- You may be asked for your administrator password during this time. If you're uncomfortable entering your password here, please install VirtualBox manually. virtualbox_install_success: |- VirtualBox has successfully been installed! linux: nfs_export: |- Preparing to edit /etc/exports. Administrator privileges will be required... nfs_prune: |- Pruning invalid NFS exports. Administrator privileges will be required... arch: nfs_export: prepare: "Preparing to edit /etc/exports. Administrator privileges will be required..." windows: virtualbox_install_download: |- Downloading VirtualBox %{version}... virtualbox_install_detail: |- This may not be the latest version of VirtualBox, but it is a version that is known to work well. Over time, we'll update the version that is installed. virtualbox_install_install: |- Installing VirtualBox. This will take a few minutes... virtualbox_install_install_detail: |- A couple pop-ups will occur during this installation process to ask for admin privileges as well as to install Oracle drivers. Please say yes to both. If you're uncomfortable with this, please install VirtualBox manually. virtualbox_install_success: |- VirtualBox has successfully been installed! provisioners: chef: chef_not_detected: |- The chef binary (either `chef-solo` or `chef-client`) was not found on the VM and is required for chef provisioning. Please verify that chef is installed and that the binary is available on the PATH. cookbooks_folder_not_found_warning: "The cookbook path '%{path}' doesn't exist. Ignoring..." nodes_folder_not_found_warning: "The node path '%{path}' doesn't exist. Ignoring..." data_bags_folder_not_found_warning: "The databag path '%{path}' doesn't exist. Ignoring..." roles_folder_not_found_warning: "The role path '%{path}' doesn't exist. Ignoring..." environments_folder_not_found_warning: "The environment path '%{path}' doesn't exist. Ignoring..." json: "Generating chef JSON and uploading..." client_key_folder: "Creating folder to hold client key..." generating_node_name: |- Auto-generating node name for Chef... using_hostname_node_name: |- Using hostname "%{hostname}" as node name for Chef... install_failed: |- Vagrant could not detect Chef on the guest! Even after Vagrant attempted to install Chef, it could still not find Chef on the system. Please make sure you are connected to the Internet and can access Chef's package distribution servers. If you already have Chef installed on this guest, you can disable the automatic Chef detection by setting the 'install' option in the Chef configuration section of your Vagrantfile: chef.install = false log_level_empty: |- The Chef provisioner requires a log level. If you did not set a log level, this is probably a bug and should be reported. upload_validation_key: "Uploading chef client validation key..." upload_encrypted_data_bag_secret_key: "Uploading chef encrypted data bag secret key..." recipe_empty: |- Chef Apply provisioning requires that the `config.chef.recipe` be set to a string containing the recipe contents you want to execute on the guest. running_client: "Running chef-client..." running_client_again: "Running chef-client again (failed to converge)..." running_apply: "Running chef-apply..." running_solo: "Running chef-solo..." running_solo_again: "Running chef-solo again (failed to converge)..." running_zero: "Running chef-client (local-mode)..." running_zero_again: "Running chef-client (local-mode) again (failed to converge)..." missing_shared_folders: |- Shared folders that Chef requires are missing on the virtual machine. This is usually due to configuration changing after already booting the machine. The fix is to run a `vagrant reload` so that the proper shared folders will be prepared and mounted on the VM. no_convergence: |- Chef never successfully completed! Any errors should be visible in the output above. Please fix your recipes so that they properly complete. not_detected: |- The `%{binary}` binary appears not to be in the PATH of the guest. This could be because the PATH is not properly setup or perhaps chef is not installed on this guest. Chef provisioning can not continue without chef properly installed. server_url_required: |- Chef server provisioning requires that the `config.chef.chef_server_url` be set to the URL of your chef server. Examples include "http://12.12.12.12:4000" and "http://myserver.com:4000" (the port of course can be different, but 4000 is the default) server_validation_key_required: |- Chef server provisioning requires that the `config.chef.validation_key_path` configuration be set to a path on your local machine of the validation key used to register the VM with the chef server. server_validation_key_doesnt_exist: |- The validation key set for `config.chef.validation_key_path` does not exist! This file needs to exist so it can be uploaded to the virtual machine. upload_path_empty: |- The Chef Apply provisioner requires that the `config.chef.upload_path` be set to a non-nil, non-empty value. missing_node_name: |- The Chef Client provisioner cannot delete the %{deletable} from the Chef server because Vagrant does not know the `node_name'! You need to manually delete the %{deletable} from the Chef server. You can specify the `node_name' in the Chef configuration to prevent this in the future. deleting_from_server: "Deleting %{deletable} \"%{name}\" from Chef server..." file: no_dest_file: "File destination must be specified." no_source_file: "File source must be specified." path_invalid: "File upload source file %{path} must exist" puppet: not_detected: |- The `%{binary}` binary appears not to be in the PATH of the guest. This could be because the PATH is not properly setup or perhaps Puppet is not installed on this guest. Puppet provisioning can not continue without Puppet properly installed. running_puppet: "Running Puppet with %{manifest}..." running_puppet_env: "Running Puppet with environment %{environment}..." manifest_missing: |- The configured Puppet manifest is missing. Please specify a path to an existing manifest: %{manifest} environment_missing: |- The configured Puppet environment folder %{environment} was not found in the specified environmentpath %{environmentpath}. Please specify a path to an existing Puppet directory environment. environment_path_missing: "The environment path specified for Puppet does not exist: %{path}" manifests_path_missing: "The manifests path specified for Puppet does not exist: %{path}" missing_shared_folders: |- Shared folders that Puppet requires are missing on the virtual machine. This is usually due to configuration changing after already booting the machine. The fix is to run a `vagrant reload` so that the proper shared folders will be prepared and mounted on the VM. module_path_missing: "The configured module path doesn't exist: %{path}" puppet_server: cert_requires_node: |- "puppet_node" is required when a client cert or key is specified client_cert_and_private_key: |- Both a client certificate and private key must be specified, if any client_cert_not_found: |- The specified client cert path could not be found client_private_key_not_found: |- The specified client private key path could not be found not_detected: |- The `%{binary}` binary appears not to be in the PATH of the guest. This could be because the PATH is not properly setup or perhaps Puppet is not installed on this guest. Puppet provisioning can not continue without Puppet properly installed. running_puppetd: "Running Puppet agent..." uploading_client_cert: |- Uploading client certificate and private key... shell: args_bad_type: "Shell provisioner `args` must be a string or array." invalid_encoding: |- Invalid encoding '%{actual}' for script at '%{path}'. Must be '%{default}' or UTF-8. env_must_be_a_hash: |- The shell provisioner's `env' option must be a hash. no_path_or_inline: "One of `path` or `inline` must be set." path_and_inline_set: "Only one of `path` or `inline` may be set." path_invalid: "`path` for shell provisioner does not exist on the host system: %{path}" running: "Running: %{script}" runningas: "Running: %{local} as %{remote}" upload_path_not_set: "`upload_path` must be set for the shell provisioner." interactive_not_elevated: "To be interactive, it must also be privileged." ansible: ansible_host_pattern_detected: |- Vagrant has detected a host range pattern in the `groups` option. Vagrant doesn't fully check the validity of these parameters! Please check https://docs.ansible.com/ansible/intro_inventory.html#hosts-and-groups for more information. cannot_detect: |- Vagrant does not support detecting whether Ansible is installed for the guest OS running in the machine. Vagrant will assume it is installed and attempt to continue. If you'd like this provisioner to be improved, please take a look at the Vagrant source code linked below and try to contribute back support. Thank you! https://github.com/mitchellh/vagrant errors: ansible_command_failed: |- Ansible failed to complete successfully. Any error output should be visible above. Please fix these errors and try again. ansible_compatibility_mode_conflict: |- The requested Ansible compatibility mode (%{compatibility_mode}) is in conflict with the Ansible installation on your Vagrant %{system} system (currently: %{ansible_version}). See https://docs.vagrantup.com/v2/provisioning/ansible_common.html#compatibility_mode for more information. ansible_not_found_on_guest: |- The Ansible software could not be found! Please verify that Ansible is correctly installed on your guest system. If you haven't installed Ansible yet, please install Ansible on your Vagrant basebox, or enable the automated setup with the ansible_local provisioner `install` option. Please check https://docs.vagrantup.com/v2/provisioning/ansible_local.html#install for more information. ansible_not_found_on_host: |- The Ansible software could not be found! Please verify that Ansible is correctly installed on your host system. If you haven't installed Ansible yet, please install Ansible on your host system. Vagrant can't do this for you in a safe and automated way. Please check https://docs.ansible.com for more information. ansible_programming_error: |- Ansible Provisioner Programming Error: %{message} Internal Details: %{details} Sorry, but this Vagrant error should never occur. Please check https://github.com/mitchellh/vagrant/issues for any existing bug report. If needed, please create a new issue. Thank you! cannot_support_pip_install: |- Unfortunately Vagrant does not support yet installing Ansible from pip for the guest OS running in the machine. If you'd like this provisioner to be improved, please take a look at the Vagrant source code linked below and try to contribute back support. Thank you! https://github.com/mitchellh/vagrant ansible_version_mismatch: |- The requested Ansible version (%{required_version}) was not found on the %{system}. Please check the Ansible installation on your Vagrant %{system} system (currently: %{current_version}), or adapt the provisioner `version` option in your Vagrantfile. See https://docs.vagrantup.com/v2/provisioning/ansible_common.html#version for more information. config_file_not_found: |- `%{config_option}` does not exist on the %{system}: %{path} extra_vars_invalid: |- `extra_vars` must be a hash or a path to an existing file. Received: %{value} (as %{type}) no_compatibility_mode: |- `compatibility_mode` must be a valid mode (possible values: %{valid_modes}). no_playbook: |- `playbook` file path must be set. raw_arguments_invalid: |- `raw_arguments` must be an array of strings. Received: %{value} (as %{type}) raw_ssh_args_invalid: |- `raw_ssh_args` must be an array of strings. Received: %{value} (as %{type}) installing: "Installing Ansible..." installing_pip: "Installing pip... (for Ansible installation)" running_galaxy: "Running ansible-galaxy..." running_playbook: "Running ansible-playbook..." windows_not_supported_for_control_machine: |- Windows is not officially supported for the Ansible Control Machine. Please check https://docs.ansible.com/intro_installation.html#control-machine-requirements compatibility_mode_not_detected: |- Vagrant gathered an unknown Ansible version: %{gathered_version} and falls back on the compatibility mode '%{compatibility_mode}'. Alternatively, the compatibility mode can be specified in your Vagrantfile: https://www.vagrantup.com/docs/provisioning/ansible_common.html#compatibility_mode compatibility_mode_warning: |- Vagrant has automatically selected the compatibility mode '%{compatibility_mode}' according to the Ansible version installed (%{ansible_version}). Alternatively, the compatibility mode can be specified in your Vagrantfile: https://www.vagrantup.com/docs/provisioning/ansible_common.html#compatibility_mode docker: wrong_provisioner: |- The Docker post-install provisioner cannot also take a Docker post-install provisioner not_running: "Docker is not running on the guest VM." install_failed: "Docker installation failed." salt: minion_config_nonexist: |- The specified minion_config '%{missing_config_file}' file could not be found. master_config_nonexist: |- The specified master_config '%{missing_config_file}' file could not be found. grains_config_nonexist: |- The specified grains_config file could not be found. missing_key: |- You must include both public and private keys. must_accept_keys: |- You must accept keys when running highstate with master! args_array: |- You must set this value as an array. python_version: |- You must set this as an integer or string that represents an integer. pushes: file: no_destination: "File destination must be specified." vagrant-2.0.2/templates/locales/guest_windows.yml000066400000000000000000000021221323370221500222140ustar00rootroot00000000000000en: vagrant_windows: errors: public_key_directory_failure: |- Vagrant failed to properly discover the correct paths for the temporary directory and user profile directory on the Windows guest. Please ensure the guest is properly configured. network_winrm_required: |- Configuring networks on Windows requires the communicator to be set to WinRM. To do this, add the following to your Vagrantfile: config.vm.communicator = "winrm" Note that the Windows guest must be configured to accept insecure WinRM connections, and the WinRM port must be forwarded properly from the guest machine. This is not always done by default. rename_computer_failed: |- Renaming the Windows guest failed. Most often this is because you've specified a FQDN instead of just a host name. Ensure the new guest name is properly formatted. Standard names may contain letters (a-z, A-Z), numbers (0-9), and hypens (-), but no spaces or periods (.). The name may not consist entirely of digits. vagrant-2.0.2/templates/locales/providers_docker.yml000066400000000000000000000242551323370221500226720ustar00rootroot00000000000000en: docker_provider: already_built: |- Image is already built from the Dockerfile. `vagrant reload` to rebuild. build_image_destroy: |- Removing built image... build_image_destroy_in_use: |- Build image couldn't be destroyed because the image is in use. The image must be destroyed manually in the future if you want to remove it. build_image_invalid: |- Build image no longer exists. Rebuilding... building: |- Building the container from a Dockerfile... building_named_dockerfile: |- Building the container from the named Dockerfile: %{file}... creating: |- Creating the container... created: |- Container created: %{id} host_machine_disabling_folders: |- Removing synced folders... host_machine_forwarded_ports: |- Warning: When using a remote Docker host, forwarded ports will NOT be immediately available on your machine. They will still be forwarded on the remote machine, however, so if you have a way to access the remote machine, then you should be able to access those ports there. This is not an error, it is only an informational message. host_machine_needed: |- Docker host is required. One will be created if necessary... host_machine_ready: |- Docker host VM is already ready. host_machine_starting: |- Vagrant will now create or start a local VM to act as the Docker host. You'll see the output of the `vagrant up` for this VM below. host_machine_syncing_folders: |- Syncing folders to the host VM... logging_in: |- Logging in to Docker server... logs_host_state_unknown: |- 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. not_created_skip: |- Container not created. Skipping. not_docker_provider: |- Not backed by Docker provider. Skipping. pull: |- Pulling image '%{image}'... run_command_required: |- `vagrant docker-run` requires a command to execute. This command must be specified after a `--` in the command line. This is used to separate possible machine names and options from the actual command to execute. An example is shown below: vagrant docker-run web -- rails new . running: |- Container is starting. Output will stream in below... running_detached: |- Container is started detached. 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. 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... messages: destroying: |- Deleting the container... not_created: |- The container hasn't been created yet. not_created_original: |- The original container hasn't been created yet. Run `vagrant up` for this machine first. not_running: |- The container is not currently running. preparing: |- Preparing to start the container... provision_no_ssh: |- Provisioners will not be run since container doesn't support SSH. will_not_destroy: |- The container will not be destroyed, since the confirmation was declined. starting: |- Starting container... stopping: |- Stopping container... container_ready: |- Container started and ready for use! status: host_state_unknown: |- The host VM for the Docker containers appears to not be running or is currently inaccessible. Because of this, we can't determine the state of the containers on that host. Run `vagrant up` to bring up the host VM again. not_created: |- The environment has not yet been created. Run `vagrant up` to create the environment. If a machine is not created, only the default provider will be shown. So if a provider is not listed, then the machine is not created for that environment. preparing: |- Vagrant is preparing to start this Docker container. Run `vagrant up` to continue. running: |- The container is created and running. You can stop it using `vagrant halt`, see logs with `vagrant docker-logs`, and kill/destroy it with `vagrant destroy`. stopped: |- The container is created but not running. You can run it again with `vagrant up`. If the container always goes to "stopped" right away after being started, it is because the command being run exits and doesn't keep running. errors: compose_lock_timeout: |- Vagrant enountered a timeout waiting for the docker compose driver to become available. Please try to run your command again. If you continue to experience this error it may be resolved by disabling parallel execution. docker_compose_not_installed: |- Vagrant has been instructed to use to use the Compose driver for the Docker plugin but was unable to locate the `docker-compose` executable. Ensure that `docker-compose` is installed and available on the PATH. not_created: |- The container hasn't been created yet. not_running: |- The container is not currently running. communicator_non_docker: |- The "docker_hostvm" communicator was specified on a machine that is not provided by the Docker provider. This is a bug with your Vagrantfile. Please contact the creator of your Vagrant environment and notify them to not use this communicator for anything except the "docker" provider. config: both_build_and_image: |- Only one of "build_dir" or "image" can be set build_dir_invalid: |- "build_dir" must exist and contain a Dockerfile build_dir_or_image: |- One of "build_dir" or "image" must be set compose_configuration_hash: |- "compose_configuration" must be a hash compose_force_vm: |- Docker compose is not currently supported from within proxy VM. create_args_array: |- "create_args" must be an array invalid_link: |- Invalid link (should be 'name:alias'): "%{link}" invalid_vagrantfile: |- "vagrant_vagrantfile" must point to a Vagrantfile that exists. docker_provider_nfs_without_privileged: |- You've configured a NFS synced folder but didn't enable privileged mode for the container. Please set the `privileged` option to true on the provider block from your Vagrantfile, recreate the container and try again. docker_provider_image_not_configured: |- The base Docker image has not been set for the '%{name}' VM! execute_error: |- A Docker command executed by Vagrant didn't complete successfully! The command run along with the output from the command is shown below. Command: %{command} Stderr: %{stderr} Stdout: %{stdout} exec_command_required: |- The "docker-exec" command requires a command to execute. This command must be specified after a "--" in the command line. This is used to separate machine name and options from the actual command to execute. An example is show below: $ vagrant docker-exec -t nginx -- bash host_vm_communicator_not_ready: |- The Docker provider was able to bring up the host VM successfully but the host VM is still reporting that SSH is unavailable. This sometimes happens with certain providers due to bugs in the underlying hypervisor, and can be fixed with a `vagrant reload`. The ID for the host VM is shown below for convenience. If this does not fix it, please verify that the host VM provider is functional and properly configured. Host VM ID: %{id} 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 into the host VM (if there is one), and run `docker commit` and so on manually. state_not_running: |- The container never entered the "running" state, or entered it briefly but reverted back to another state. Please verify that the configuration of the container is correct. If you meant for this container to not remain running, please set the Docker provider configuration "remains_running" to "false": config.vm.provider "docker" do |d| d.remains_running = false end state_stopped: |- The container started either never left the "stopped" state or very quickly reverted to the "stopped" state. This is usually because the container didn't execute a command that kept it running, and usually indicates a misconfiguration. If you meant for this container to not remain running, please set the Docker provider configuration "remains_running" to "false": config.vm.provider "docker" do |d| d.remains_running = false end suspend_not_supported: |- The "suspend" command is not supported with the Docker provider. Docker containers don't natively support suspend. If you're using a host machine, you can suspend the host machine by finding it in `vagrant global-status` and using `vagrant suspend `. synced_folder_non_docker: |- The "docker" synced folder type can't be used because the provider in use is not Docker. This synced folder type only works with the Docker provider. The provider this machine is using is: %{provider} vagrantfile_not_found: |- The configured host VM Vagrantfile could not be found. Please fix the Vagrantfile for this Docker environment to point to a valid host VM. vagrant-2.0.2/templates/locales/providers_hyperv.yml000066400000000000000000000064061323370221500227360ustar00rootroot00000000000000en: vagrant_hyperv: choose_switch: |- Please choose a switch to attach to your Hyper-V instance. If none of these are appropriate, please open the Hyper-V manager to create a new virtual switch. message_already_running: |- Hyper-V instance already running. message_not_created: |- VM not created. Moving on... message_not_running: |- Hyper-V machine isn't running. Can't SSH in! errors: admin_required: |- The Hyper-V provider requires that Vagrant be run with administrative privileges. This is a limitation of Hyper-V itself. Hyper-V requires administrative privileges for management commands. Please restart your console with administrative privileges and try again. box_invalid: |- The box you're using with the Hyper-V provider ('%{name}') is invalid. A Hyper-V box should contain both a "Virtual Machines" and a "Virtual Hard Disks" folder that are created as part of exporting a Hyper-V machine. Within these directories, Vagrant expects to find the virtual machine configuration as well as the root hard disk. The box you're attempting to use is missing one or both of these directories or does not contain the files expected. Verify that you added the correct box. If this problem persists, please contact the creator of the box for assistance. ip_addr_timeout: |- Hyper-V failed to determine your machine's IP address within the configured timeout. Please verify the machine properly booted and the network works. To do this, open the Hyper-V manager, find your virtual machine, and connect to it. The most common cause for this error is that the running virtual machine doesn't have the latest Hyper-V integration drivers. Please research for your operating system how to install these in order for the VM to properly communicate its IP address to Hyper-V. no_switches: |- There are no virtual switches created for Hyper-V! Please open the Hyper-V Manager, go to the "Virtual Switch Manager", and create at least one virtual switch. A virtual switch is required for Vagrant to create a Hyper-V machine that is connected to a network so it can access it. For more help, please see the documentation on the Vagrant website for Hyper-V. powershell_features_disabled: |- The Hyper-V cmdlets for PowerShell are not available! Vagrant requires these to control Hyper-V. Please enable them in the "Windows Features" control panel and try again. powershell_error: |- An error occurred while executing a PowerShell script. This error is shown below. Please read the error message and see if this is a configuration error with your system. If it is not, then please report a bug. Script: %{script} Error: %{stderr} powershell_required: |- The Vagrant Hyper-V provider requires PowerShell to be available. Please make sure "powershell.exe" is available on your PATH. windows_required: |- The Hyper-V provider only works on Windows. Please try to use another provider. vagrant-2.0.2/templates/locales/synced_folder_smb.yml000066400000000000000000000072201323370221500230000ustar00rootroot00000000000000en: vagrant_sf_smb: not_supported: |- It appears your machine doesn't support SMB, has not been properly configured for SMB, or there is not an adapter to enable SMB on this machine for Vagrant. Ensure SMB host functionality is available on this machine and try again. mounting: |- Mounting SMB shared folders... mounting_single: |- %{host} => %{guest} preparing: |- Preparing SMB shared folders... warning_password: |- You will be asked for the username and password to use for the SMB folders shortly. Please use the proper username/password of your account. incorrect_credentials: |- Credentials incorrect. Please try again. uac: prune_warning: |- Vagrant requires administator access for pruning SMB shares and may request access to complete removal of stale shares. create_warning: |- Vagrant requires administator access to create SMB shares and may request access to complete setup of configured shares. errors: start_failed: |- Vagrant failed to automatically start the SMB service. Ensure the required services can be started and try again. Command: %{command} Stderr: %{stderr} Stdout: %{stdout} credentials_missing: |- Vagrant SMB synced folders require the account password to be stored in an NT compatible format. Please update your sharing settings to enable a Windows compatible password and try again. credentials_request_error: |- Vagrant failed to receive credential information required for preparing an SMB share. define_share_failed: |- Exporting an SMB share failed! Details about the failure are shown below. Please inspect the error message and correct any problems. Host path: %{host} Stderr: %{stderr} Stdout: %{stdout} prune_share_failed: |- Pruning an SMB share failed! Details about the failure are shown below. Please inspect the error message and correct any problems. Share name: %{name} Stderr: %{stderr} Stdout: %{stdout} name_error: |- Vagrant is unable to setup a requested SMB share. An SMB share already exists with the given name. Share name: %{name} Current path: %{existing_path} Requested path: %{path} list_failed: |- Vagrant failed to generate a list of local SMB shares. Please try running the command again. no_routable_host_addr: |- We couldn't detect an IP address that was routable to this machine from the guest machine! Please verify networking is properly setup in the guest machine and that it is able to access this host. As another option, you can manually specify an IP for the machine to mount from using the `smb_host` option to the synced folder. powershell_version: |- PowerShell version 3 or later is required for SMB synced folders to work on Windows. You have version: '%{version}'. Please update your PowerShell installation. windows_admin_required: |- SMB shared folders require running Vagrant with administrative privileges. This is a limitation of Windows, since creating new network shares requires admin privileges. Please try again in a console with proper permissions or use another synced folder type. windows_host_required: |- SMB shared folders are only available when Vagrant is running on Windows. The guest machine can be running non-Windows. Please use another synced folder type. vagrant-2.0.2/templates/nfs/000077500000000000000000000000001323370221500157375ustar00rootroot00000000000000vagrant-2.0.2/templates/nfs/exports.erb000066400000000000000000000003461323370221500201400ustar00rootroot00000000000000# 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 %> vagrant-2.0.2/templates/nfs/exports_freebsd.erb000066400000000000000000000003421323370221500216260ustar00rootroot00000000000000# 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 %> vagrant-2.0.2/templates/nfs/exports_linux.erb000066400000000000000000000003551323370221500213570ustar00rootroot00000000000000# VAGRANT-BEGIN: <%= user %> <%= uuid %> <% ips.each do |ip| %> <% folders.each do |name, opts| %> "<%= opts[:hostpath] %>" <%= ip %>(<%= opts[:linux__nfs_options].join(",") %>) <% end %> <% end %> # VAGRANT-END: <%= user %> <%= uuid %> vagrant-2.0.2/templates/package_Vagrantfile.erb000066400000000000000000000007741323370221500215700ustar00rootroot00000000000000Vagrant::Config.run do |config| # This Vagrantfile is auto-generated by `vagrant package` to contain # the MAC address of the box. Custom configuration should be placed in # the actual `Vagrantfile` in this box. config.vm.base_mac = "<%= base_mac %>" end # Load include vagrant file if it exists after the auto-generated # so it can override any of the settings include_vagrantfile = File.expand_path("../include/_Vagrantfile", __FILE__) load include_vagrantfile if File.exist?(include_vagrantfile) vagrant-2.0.2/templates/provisioners/000077500000000000000000000000001323370221500177135ustar00rootroot00000000000000vagrant-2.0.2/templates/provisioners/chef_client/000077500000000000000000000000001323370221500221565ustar00rootroot00000000000000vagrant-2.0.2/templates/provisioners/chef_client/client.erb000066400000000000000000000022511323370221500241260ustar00rootroot00000000000000log_level <%= log_level.inspect %> log_location STDOUT verbose_logging <%= verbose_logging.inspect %> <% if node_name %> node_name "<%= node_name %>" <% end %> ssl_verify_mode :verify_none chef_server_url "<%= chef_server_url %>" validation_client_name "<%= validation_client_name %>" validation_key "<%= validation_key %>" client_key "<%= client_key %>" encrypted_data_bag_secret <%= encrypted_data_bag_secret.inspect %> <% if environment %> environment "<%= environment %>" <% end %> file_cache_path "<%= file_cache_path %>" file_backup_path "<%= file_backup_path %>" http_proxy <%= http_proxy.inspect %> http_proxy_user <%= http_proxy_user.inspect %> http_proxy_pass <%= http_proxy_pass.inspect %> https_proxy <%= https_proxy.inspect %> https_proxy_user <%= https_proxy_user.inspect %> https_proxy_pass <%= https_proxy_pass.inspect %> no_proxy <%= no_proxy.inspect %> pid_file "/var/run/chef/chef-client.pid" Mixlib::Log::Formatter.show_time = true <% if formatter %> add_formatter "<%= formatter %>" <% end %> <% if custom_configuration -%> Chef::Config.from_file "<%= custom_configuration %>" <% end -%> vagrant-2.0.2/templates/provisioners/chef_solo/000077500000000000000000000000001323370221500216545ustar00rootroot00000000000000vagrant-2.0.2/templates/provisioners/chef_solo/solo.erb000066400000000000000000000027301323370221500233240ustar00rootroot00000000000000<% if node_name %> node_name "<%= node_name %>" <% end %> file_cache_path "<%= file_cache_path %>" file_backup_path "<%= file_backup_path %>" cookbook_path <%= cookbooks_path.inspect %> <% if roles_path && !roles_path.empty? -%> role_path <%= roles_path.size == 1 ? roles_path.first.inspect : roles_path.inspect %> <% end -%> log_level <%= log_level.inspect %> verbose_logging <%= verbose_logging.inspect %> encrypted_data_bag_secret <%= encrypted_data_bag_secret.inspect %> <% if data_bags_path && !data_bags_path.empty? -%> data_bag_path <%= data_bags_path.size == 1 ? data_bags_path.first.inspect : data_bags_path.inspect %> <% end %> <% if recipe_url -%> recipe_url "<%= recipe_url %>" <% end -%> <% if environments_path && !environments_path.empty? -%> environment_path <%= environments_path.inspect %> <% end -%> <% if environment -%> environment "<%= environment %>" <% end -%> <% if local_mode -%> local_mode true <% end -%> <% if nodes_path && !nodes_path.empty? -%> node_path <%= nodes_path.inspect %> <% end -%> http_proxy <%= http_proxy.inspect %> http_proxy_user <%= http_proxy_user.inspect %> http_proxy_pass <%= http_proxy_pass.inspect %> https_proxy <%= https_proxy.inspect %> https_proxy_user <%= https_proxy_user.inspect %> https_proxy_pass <%= https_proxy_pass.inspect %> no_proxy <%= no_proxy.inspect %> <% if formatter -%> add_formatter "<%= formatter %>" <% end %> <% if custom_configuration -%> Chef::Config.from_file "<%= custom_configuration %>" <% end -%> vagrant-2.0.2/templates/provisioners/chef_zero/000077500000000000000000000000001323370221500216575ustar00rootroot00000000000000vagrant-2.0.2/templates/provisioners/chef_zero/zero.erb000066400000000000000000000021601323370221500233270ustar00rootroot00000000000000<% if node_name %> node_name "<%= node_name %>" <% end %> file_cache_path "<%= file_cache_path %>" file_backup_path "<%= file_backup_path %>" cookbook_path <%= cookbooks_path.inspect %> <% if roles_path %> role_path <%= roles_path.size == 1 ? roles_path.first.inspect : roles_path.inspect %> <% end %> log_level <%= log_level.inspect %> verbose_logging <%= verbose_logging.inspect %> <% if !enable_reporting %> enable_reporting <%= enable_reporting.inspect %> <% end %> encrypted_data_bag_secret <%= encrypted_data_bag_secret.inspect %> <% if data_bags_path -%> data_bag_path <%= data_bags_path.size == 1 ? data_bags_path.first.inspect : data_bags_path.inspect %> <% end %> <% if environments_path %> environment_path <%= environments_path.inspect %> <% end -%> <% if environment %> environment "<%= environment %>" <% end -%> <% if local_mode -%> chef_zero.enabled true local_mode true <% end -%> <% if nodes_path -%> node_path <%= nodes_path.inspect %> <% end -%> <% if formatter %> add_formatter "<%= formatter %>" <% end %> <% if custom_configuration -%> Chef::Config.from_file "<%= custom_configuration %>" <% end -%> vagrant-2.0.2/templates/rgloader.rb000066400000000000000000000005421323370221500172760ustar00rootroot00000000000000# This file loads the proper rgloader/loader.rb file that comes packaged # with Vagrant so that encoded files can properly run with Vagrant. if ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"] require File.expand_path( "rgloader/loader", ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"]) else raise "Encoded files can't be read outside of the Vagrant installer." end vagrant-2.0.2/test/000077500000000000000000000000001323370221500141325ustar00rootroot00000000000000vagrant-2.0.2/test/acceptance/000077500000000000000000000000001323370221500162205ustar00rootroot00000000000000vagrant-2.0.2/test/acceptance/base.rb000066400000000000000000000001171323370221500174560ustar00rootroot00000000000000require "vagrant-spec/acceptance" require_relative "shared/context_virtualbox" vagrant-2.0.2/test/acceptance/provider-virtualbox/000077500000000000000000000000001323370221500222475ustar00rootroot00000000000000vagrant-2.0.2/test/acceptance/provider-virtualbox/linked_clone_spec.rb000066400000000000000000000020021323370221500262260ustar00rootroot00000000000000# This tests that VM is up as a linked clone shared_examples 'provider/linked_clone' do |provider, options| if !options[:box] raise ArgumentError, "box option must be specified for provider: #{provider}" end include_context 'acceptance' before do environment.skeleton('linked_clone') assert_execute('vagrant', 'box', 'add', 'box', options[:box]) end after do assert_execute('vagrant', 'destroy', '--force') end it 'creates machine as linked clone' do status('Test: machine is created successfully') result = execute('vagrant', 'up', "--provider=#{provider}") expect(result).to exit_with(0) status('Test: master VM is created') expect(result.stdout).to match(/master VM/) status('Test: machine is a master VM clone') expect(result.stdout).to match(/Cloning/) status('Test: machine is available by ssh') result = execute('vagrant', 'ssh', '-c', 'echo foo') expect(result).to exit_with(0) expect(result.stdout).to match(/foo\n$/) end end vagrant-2.0.2/test/acceptance/provider-virtualbox/network_intnet_spec.rb000066400000000000000000000010401323370221500266530ustar00rootroot00000000000000shared_examples "provider/network/intnet" do |provider, options| if !options[:box] raise ArgumentError, "box option must be specified for provider: #{provider}" end include_context "acceptance" before do environment.skeleton("network_intnet") assert_execute("vagrant", "box", "add", "box", options[:box]) assert_execute("vagrant", "up", "--provider=#{provider}") end after do assert_execute("vagrant", "destroy", "--force", log: false) end it "properly configures an internal network" do end end vagrant-2.0.2/test/acceptance/shared/000077500000000000000000000000001323370221500174665ustar00rootroot00000000000000vagrant-2.0.2/test/acceptance/shared/context_virtualbox.rb000066400000000000000000000001651323370221500237600ustar00rootroot00000000000000shared_context "provider-context/virtualbox" do let(:extra_env) {{ "VBOX_USER_HOME" => "{{homedir}}", }} end vagrant-2.0.2/test/acceptance/skeletons/000077500000000000000000000000001323370221500202275ustar00rootroot00000000000000vagrant-2.0.2/test/acceptance/skeletons/linked_clone/000077500000000000000000000000001323370221500226555ustar00rootroot00000000000000vagrant-2.0.2/test/acceptance/skeletons/linked_clone/Vagrantfile000066400000000000000000000002131323370221500250360ustar00rootroot00000000000000Vagrant.configure('2') do |config| config.vm.box = 'basic' config.vm.provider 'virtualbox' do |v| v.linked_clone = true end end vagrant-2.0.2/test/acceptance/skeletons/network_intnet/000077500000000000000000000000001323370221500233015ustar00rootroot00000000000000vagrant-2.0.2/test/acceptance/skeletons/network_intnet/Vagrantfile000066400000000000000000000002331323370221500254640ustar00rootroot00000000000000Vagrant.configure("2") do |config| config.vm.box = "box" config.vm.network "private_network", ip: "192.168.50.4", virtualbox__intnet: true end vagrant-2.0.2/test/config/000077500000000000000000000000001323370221500153775ustar00rootroot00000000000000vagrant-2.0.2/test/config/acceptance_boxes.yml000066400000000000000000000005571323370221500214170ustar00rootroot00000000000000# This is the list of required boxes for acceptance tests, and # their locations. The locations are used to download the boxes # on the fly, if necessary. Additionally, a SHA1 checksum is # given to determine if the box needs to be updated. - name: default url: http://files.vagrantup.com/test/boxes/default.box checksum: 1b0a7eb6e152c5b43f45485654ff4965a7f9f604 vagrant-2.0.2/test/support/000077500000000000000000000000001323370221500156465ustar00rootroot00000000000000vagrant-2.0.2/test/support/isolated_environment.rb000066400000000000000000000027551323370221500224340ustar00rootroot00000000000000require "fileutils" require "pathname" require "tmpdir" require "log4r" require "vagrant/util/platform" # This class manages an isolated environment for Vagrant to # run in. It creates a temporary directory to act as the # working directory as well as sets a custom home directory. # # This class also provides various helpers to create Vagrantfiles, # boxes, etc. class IsolatedEnvironment attr_reader :homedir attr_reader :workdir # Initializes an isolated environment. You can pass in some # options here to configure runing custom applications in place # of others as well as specifying environmental variables. # # @param [Hash] apps A mapping of application name (such as "vagrant") # to an alternate full path to the binary to run. # @param [Hash] env Additional environmental variables to inject # into the execution environments. def initialize @logger = Log4r::Logger.new("test::isolated_environment") # Create a temporary directory for our work @tempdir = Vagrant::Util::Platform.fs_real_path(Dir.mktmpdir("vagrant-iso-env")) @logger.info("Initialize isolated environment: #{@tempdir}") # Setup the home and working directories @homedir = Pathname.new(File.join(@tempdir, "home")) @workdir = Pathname.new(File.join(@tempdir, "work")) @homedir.mkdir @workdir.mkdir end # This closes the environment by cleaning it up. def close @logger.info("Removing isolated environment: #{@tempdir}") FileUtils.rm_rf(@tempdir) end end vagrant-2.0.2/test/unit/000077500000000000000000000000001323370221500151115ustar00rootroot00000000000000vagrant-2.0.2/test/unit/base.rb000066400000000000000000000033731323370221500163560ustar00rootroot00000000000000require "tmpdir" require "rubygems" # Gems require "checkpoint" require "webmock/rspec" require "rspec/its" # Require Vagrant itself so we can reference the proper # classes to test. require "vagrant" require "vagrant/util/platform" # Add the test directory to the load path $:.unshift File.expand_path("../../", __FILE__) # Load in helpers require "unit/support/dummy_communicator" require "unit/support/dummy_provider" require "unit/support/shared/base_context" require "unit/support/shared/action_synced_folders_context" require "unit/support/shared/capability_helpers_context" require "unit/support/shared/plugin_command_context" require "unit/support/shared/virtualbox_context" # Do not buffer output $stdout.sync = true $stderr.sync = true # Create a temporary directory where test vagrant will run. The reason we save # this to a constant is so we can clean it up later. VAGRANT_TEST_CWD = Dir.mktmpdir("vagrant-test-cwd") # Configure RSpec RSpec.configure do |c| c.formatter = :documentation if Vagrant::Util::Platform.windows? c.filter_run_excluding :skip_windows else c.filter_run_excluding :windows end c.after(:suite) do FileUtils.rm_rf(VAGRANT_TEST_CWD) end end # Configure VAGRANT_CWD so that the tests never find an actual # Vagrantfile anywhere, or at least this minimizes those chances. ENV["VAGRANT_CWD"] = VAGRANT_TEST_CWD # Set the dummy provider to the default for tests ENV["VAGRANT_DEFAULT_PROVIDER"] = "dummy" # Unset all host plugins so that we aren't executing subprocess things # to detect a host for every test. Vagrant.plugin("2").manager.registered.dup.each do |plugin| if plugin.components.hosts.to_hash.length > 0 Vagrant.plugin("2").manager.unregister(plugin) end end # Disable checkpoint Checkpoint.disable! vagrant-2.0.2/test/unit/plugins/000077500000000000000000000000001323370221500165725ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/000077500000000000000000000000001323370221500203735ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/box/000077500000000000000000000000001323370221500211635ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/box/command/000077500000000000000000000000001323370221500226015ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/box/command/add_test.rb000066400000000000000000000032051323370221500247150ustar00rootroot00000000000000require File.expand_path("../../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/box/command/add") describe VagrantPlugins::CommandBox::Command::Add do include_context "unit" let(:argv) { [] } 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 subject { described_class.new(argv, iso_env) } let(:action_runner) { double("action_runner") } before do allow(iso_env).to receive(:action_runner).and_return(action_runner) end context "with no arguments" do it "shows help" do expect { subject.execute }. to raise_error(Vagrant::Errors::CLIInvalidUsage) end end context "with one argument" do let(:argv) { ["foo"] } it "executes the runner with the proper actions" do expect(action_runner).to receive(:run).with(any_args) { |action, **opts| expect(opts[:box_name]).to be_nil expect(opts[:box_url]).to eq("foo") true } subject.execute end end context "with two arguments" do let(:argv) { ["foo", "bar"] } it "executes the runner with the proper actions" do expect(action_runner).to receive(:run).with(any_args) { |action, **opts| expect(opts[:box_name]).to eq("foo") expect(opts[:box_url]).to eq("bar") true } subject.execute end end context "with more than two arguments" do let(:argv) { ["one", "two", "three"] } it "shows help" do expect { subject.execute }. to raise_error(Vagrant::Errors::CLIInvalidUsage) end end end vagrant-2.0.2/test/unit/plugins/commands/box/command/prune_test.rb000066400000000000000000000143141323370221500253210ustar00rootroot00000000000000require File.expand_path("../../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/box/command/prune") describe VagrantPlugins::CommandBox::Command::Prune do include_context "unit" include_context "command plugin helpers" let(:entry_klass) { Vagrant::MachineIndex::Entry } let(:iso_env) do # We have to create a Vagrantfile so there is a root path isolated_environment.tap do |env| env.vagrantfile("") end end let(:iso_vagrant_env) { iso_env.create_vagrant_env } let(:argv) { [] } # Seems this way of providing a box version triggers box in use. def new_entry(name, box_name, box_provider, version) entry_klass.new.tap do |e| e.name = name e.vagrantfile_path = "/bar" e.extra_data["box"] = { "name" => box_name, "provider" => box_provider, "version" => version, } end end subject { described_class.new(argv, iso_vagrant_env) } describe "execute" do context "with no args" do it "removes the old version and keeps the current one" do # Let's put some things in the index iso_env.box3("foobox", "1.0", :virtualbox); iso_env.box3("foobox", "1.1", :virtualbox); iso_env.box3("barbox", "1.0", :vmware); iso_env.box3("barbox", "1.1", :vmware); iso_vagrant_env.machine_index.set(new_entry("foo", "foobox", "virtualbox", 1)) output = "" allow(iso_vagrant_env.ui).to receive(:info) do |data| output << data end expect(iso_vagrant_env.boxes.all.count).to eq(4) expect(subject.execute).to eq(0) expect(iso_vagrant_env.boxes.all.count).to eq(2) expect(output).to include("barbox (vmware, 1.1)") expect(output).to include("Removing box 'barbox' (v1.0) with provider 'vmware'...") expect(output).to include("foobox (virtualbox, 1.1)") expect(output).to include("Removing box 'foobox' (v1.0) with provider 'virtualbox'...") end it "removes nothing" do # Let's put some things in the index iso_env.box3("foobox", "1.0", :virtualbox); iso_env.box3("barbox", "1.0", :vmware); iso_vagrant_env.machine_index.set(new_entry("foo", "foobox", "virtualbox", 1)) output = "" allow(iso_vagrant_env.ui).to receive(:info) do |data| output << data end expect(iso_vagrant_env.boxes.all.count).to eq(2) expect(subject.execute).to eq(0) expect(iso_vagrant_env.boxes.all.count).to eq(2) expect(output).to include("No old versions of boxes to remove...") end end context "with --provider" do let(:argv) { ["--provider", "virtualbox"] } it "removes the old versions of the specified provider" do # Let's put some things in the index iso_env.box3("foobox", "1.0", :virtualbox); iso_env.box3("foobox", "1.1", :virtualbox); iso_env.box3("barbox", "1.0", :vmware); iso_env.box3("barbox", "1.1", :vmware); iso_vagrant_env.machine_index.set(new_entry("foo", "foobox", "virtualbox", 1)) output = "" allow(iso_vagrant_env.ui).to receive(:info) do |data| output << "\n" + data end expect(iso_vagrant_env.boxes.all.count).to eq(4) expect(subject.execute).to eq(0) expect(iso_vagrant_env.boxes.all.count).to eq(3) expect(output).to include("foobox (virtualbox, 1.1)") expect(output).to include("Removing box 'foobox' (v1.0) with provider 'virtualbox'...") end end context "with --dry-run" do let(:argv) { ["--dry-run"] } it "removes the old versions of the specified provider" do # Let's put some things in the index iso_env.box3("foobox", "1.0", :virtualbox); iso_env.box3("foobox", "1.1", :virtualbox); iso_vagrant_env.machine_index.set(new_entry("foo", "foobox", "virtualbox", 1)) output = "" allow(iso_vagrant_env.ui).to receive(:info) do |data| output << "\n" + data end expect(iso_vagrant_env.boxes.all.count).to eq(2) expect(subject.execute).to eq(0) expect(iso_vagrant_env.boxes.all.count).to eq(2) expect(output).to include("foobox (virtualbox, 1.1)") expect(output).to include("Would remove foobox virtualbox 1.0") end end context "with --name" do let(:argv) { ["--name", "barbox"] } it "removes the old versions of the specified provider" do # Let's put some things in the index iso_env.box3("foobox", "1.0", :virtualbox); iso_env.box3("foobox", "1.1", :virtualbox); iso_env.box3("barbox", "1.0", :vmware); iso_env.box3("barbox", "1.1", :vmware); iso_vagrant_env.machine_index.set(new_entry("foo", "foobox", "virtualbox", 1)) output = "" allow(iso_vagrant_env.ui).to receive(:info) do |data| output << "\n" + data end expect(iso_vagrant_env.boxes.all.count).to eq(4) expect(subject.execute).to eq(0) expect(iso_vagrant_env.boxes.all.count).to eq(3) expect(output).to include("barbox (vmware, 1.1)") expect(output).to include("Removing box 'barbox' (v1.0) with provider 'vmware'...") end end context "with --name and --provider" do let(:argv) { ["--name", "foobox", "--provider", "virtualbox"] } it "removed the old versions of that name and provider only" do # Let's put some things in the index iso_env.box3("foobox", "1.0", :virtualbox); iso_env.box3("foobox", "1.1", :virtualbox); iso_env.box3("foobox", "1.0", :vmware); iso_env.box3("foobox", "1.1", :vmware); iso_env.box3("barbox", "1.0", :vmware); iso_env.box3("barbox", "1.1", :vmware); iso_vagrant_env.machine_index.set(new_entry("foo", "foobox", "virtualbox", 1)) output = "" allow(iso_vagrant_env.ui).to receive(:info) do |data| output << "\n" + data end expect(iso_vagrant_env.boxes.all.count).to eq(6) expect(subject.execute).to eq(0) expect(iso_vagrant_env.boxes.all.count).to eq(5) expect(output).to include("Removing box 'foobox' (v1.0) with provider 'virtualbox'...") end end end end vagrant-2.0.2/test/unit/plugins/commands/box/command/remove_test.rb000066400000000000000000000040061323370221500254620ustar00rootroot00000000000000require File.expand_path("../../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/box/command/remove") describe VagrantPlugins::CommandBox::Command::Remove do include_context "unit" let(:argv) { [] } 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 subject { described_class.new(argv, iso_env) } let(:action_runner) { double("action_runner") } before do allow(iso_env).to receive(:action_runner).and_return(action_runner) end context "with no arguments" do it "shows help" do expect { subject.execute }. to raise_error(Vagrant::Errors::CLIInvalidUsage) end end context "with one argument" do let(:argv) { ["foo"] } it "invokes the action runner" do expect(action_runner).to receive(:run).with(any_args) { |action, opts| expect(opts[:box_name]).to eq("foo") expect(opts[:force_confirm_box_remove]).to be(false) true } subject.execute end context "with --force" do let(:argv) { super() + ["--force"] } it "invokes the action runner with force option" do expect(action_runner).to receive(:run).with(any_args) { |action, opts| expect(opts[:box_name]).to eq("foo") expect(opts[:force_confirm_box_remove]).to be(true) true } subject.execute end end end context "with two arguments" do let(:argv) { ["foo", "bar"] } it "uses the 2nd arg as a provider" do expect(action_runner).to receive(:run).with(any_args) { |action, opts| expect(opts[:box_name]).to eq("foo") expect(opts[:box_provider]).to eq("bar") true } subject.execute end end context "with more than two arguments" do let(:argv) { ["one", "two", "three"] } it "shows help" do expect { subject.execute }. to raise_error(Vagrant::Errors::CLIInvalidUsage) end end end vagrant-2.0.2/test/unit/plugins/commands/box/command/repackage_test.rb000066400000000000000000000027111323370221500261100ustar00rootroot00000000000000require File.expand_path("../../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/box/command/repackage") describe VagrantPlugins::CommandBox::Command::Repackage do include_context "unit" let(:argv) { [] } 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 subject { described_class.new(argv, iso_env) } let(:action_runner) { double("action_runner") } before do allow(iso_env).to receive(:action_runner).and_return(action_runner) end context "with no arguments" do it "shows help" do expect { subject.execute }. to raise_error(Vagrant::Errors::CLIInvalidUsage) end end context "with one argument" do let(:argv) { ["one"] } it "shows help" do expect { subject.execute }. to raise_error(Vagrant::Errors::CLIInvalidUsage) end end context "with two arguments" do let(:argv) { ["one", "two"] } it "shows help" do expect { subject.execute }. to raise_error(Vagrant::Errors::CLIInvalidUsage) end end context "with three arguments" do it "repackages the box with the given provider" end context "with more than three arguments" do let(:argv) { ["one", "two", "three", "four"] } it "shows help" do expect { subject.execute }. to raise_error(Vagrant::Errors::CLIInvalidUsage) end end end vagrant-2.0.2/test/unit/plugins/commands/box/command/update_test.rb000066400000000000000000000262441323370221500254570ustar00rootroot00000000000000require "pathname" require "tmpdir" require File.expand_path("../../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/box/command/update") describe VagrantPlugins::CommandBox::Command::Update do include_context "unit" let(:argv) { [] } let(:iso_env) do # We have to create a Vagrantfile so there is a root path test_iso_env.vagrantfile("") test_iso_env.create_vagrant_env end let(:test_iso_env) { isolated_environment } let(:action_runner) { double("action_runner") } let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } let(:download_options) { ["--insecure", "--cacert", "foo", "--capath", "bar", "--cert", "baz"] } subject { described_class.new(argv, iso_env) } before do allow(iso_env).to receive(:action_runner).and_return(action_runner) machine.config.vm.box = "foo" end describe "execute" do context "updating specific box" do let(:argv) { ["--box", "foo"] } let(:scratch) { Dir.mktmpdir("vagrant-test-command-box-update-execute") } let(:metadata_url) { Pathname.new(scratch).join("metadata.json") } before do metadata_url.open("w") do |f| f.write("") end test_iso_env.box3( "foo", "1.0", :virtualbox, metadata_url: metadata_url.to_s) end after do FileUtils.rm_rf(scratch) end it "doesn't update if they're up to date" do called = false allow(action_runner).to receive(:run) do |callable, opts| if opts[:box_provider] called = true end opts end subject.execute expect(called).to be(false) end it "does update if there is an update" do metadata_url.open("w") do |f| f.write(<<-RAW) { "name": "foo", "versions": [ { "version": "1.0" }, { "version": "1.1", "providers": [ { "name": "virtualbox", "url": "bar" } ] } ] } RAW end action_called = false allow(action_runner).to receive(:run) do |action, opts| if opts[:box_provider] action_called = true 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_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_insecure]).to be_nil end opts end subject.execute expect(action_called).to be(true) end it "raises an error if there are multiple providers" do test_iso_env.box3("foo", "1.0", :vmware) expect(action_runner).to receive(:run).never expect { subject.execute }. to raise_error(Vagrant::Errors::BoxUpdateMultiProvider) end context "with multiple providers and specifying the provider" do let(:argv) { ["--box", "foo", "--provider", "vmware"] } it "updates the proper box" do metadata_url.open("w") do |f| f.write(<<-RAW) { "name": "foo", "versions": [ { "version": "1.0" }, { "version": "1.1", "providers": [ { "name": "vmware", "url": "bar" } ] } ] } RAW end test_iso_env.box3("foo", "1.0", :vmware) action_called = false allow(action_runner).to receive(:run) do |action, opts| if opts[:box_provider] action_called = true expect(opts[:box_url]).to eq(metadata_url.to_s) expect(opts[:box_provider]).to eq("vmware") expect(opts[:box_version]).to eq("1.1") end opts end subject.execute expect(action_called).to be(true) end it "raises an error if that provider doesn't exist" do expect(action_runner).to receive(:run).never expect { subject.execute }. to raise_error(Vagrant::Errors::BoxNotFoundWithProvider) end end context "download options are specified" do let(:argv) { ["--box", "foo" ].concat(download_options) } it "passes down download options" do metadata_url.open("w") do |f| f.write(<<-RAW) { "name": "foo", "versions": [ { "version": "1.0" }, { "version": "1.1", "providers": [ { "name": "virtualbox", "url": "bar" } ] } ] } RAW end action_called = false allow(action_runner).to receive(:run) do |action, opts| if opts[:box_provider] 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_insecure]).to be(true) end opts end subject.execute expect(action_called).to be(true) end end context "with a box that doesn't exist" do let(:argv) { ["--box", "nope"] } it "raises an exception" do expect(action_runner).to receive(:run).never expect { subject.execute }. to raise_error(Vagrant::Errors::BoxNotFound) end end end context "updating environment machines" 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_return(nil) box end it "ignores machines without boxes" do expect(action_runner).to receive(:run).never subject.execute end it "doesn't update boxes if they're up-to-date" do allow(machine).to receive(:box).and_return(box) expect(box).to receive(:has_update?). with(machine.config.vm.box_version, {download_options: {ca_cert: nil, ca_path: nil, client_cert: nil, insecure: false}}). and_return(nil) expect(action_runner).to receive(:run).never subject.execute end context "boxes have an update" do let(:md) { md = Vagrant::BoxMetadata.new(StringIO.new(<<-RAW)) { "name": "foo", "versions": [ { "version": "1.0" }, { "version": "1.1", "providers": [ { "name": "virtualbox", "url": "bar" } ] } ] } RAW } before { allow(machine).to receive(:box).and_return(box) } it "updates boxes" do expect(box).to receive(:has_update?). with(machine.config.vm.box_version, {download_options: {ca_cert: nil, ca_path: nil, client_cert: nil, insecure: false}}). and_return([md, md.version("1.1"), md.version("1.1").provider("virtualbox")]) expect(action_runner).to receive(:run).with(any_args) { |action, opts| expect(opts[:box_url]).to eq(box.metadata_url) expect(opts[:box_provider]).to eq("virtualbox") expect(opts[:box_version]).to eq("1.1") expect(opts[:ui]).to equal(machine.ui) true } subject.execute end context "machine has download options" do before do machine.config.vm.box_download_ca_cert = "oof" machine.config.vm.box_download_ca_path = "rab" machine.config.vm.box_download_client_cert = "zab" machine.config.vm.box_download_insecure = false end it "uses download options from machine" do expect(box).to receive(:has_update?). with(machine.config.vm.box_version, {download_options: {ca_cert: "oof", ca_path: "rab", client_cert: "zab", insecure: false}}). and_return([md, md.version("1.1"), md.version("1.1").provider("virtualbox")]) 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_insecure]).to be(false) true } subject.execute end context "download options are specified on the command line" do let(:argv) { download_options } it "overrides download options from machine with options from CLI" do expect(box).to receive(:has_update?). with(machine.config.vm.box_version, {download_options: {ca_cert: "foo", ca_path: "bar", client_cert: "baz", insecure: true}}). and_return([md, md.version("1.1"), md.version("1.1").provider("virtualbox")]) 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_insecure]).to be(true) true } subject.execute end end context "force flag is specified on the command line" do let(:argv) { ["--force"].concat(download_options) } it "passes force through to action_box_add as true" do expect(box).to receive(:has_update?). with(machine.config.vm.box_version, {download_options: {ca_cert: "foo", ca_path: "bar", client_cert: "baz", insecure: true}}). and_return([md, md.version("1.1"), md.version("1.1").provider("virtualbox")]) expect(action_runner).to receive(:run).with(any_args) { |action, opts| expect(opts[:box_force]).to be(true) true } subject.execute end end end end end end end vagrant-2.0.2/test/unit/plugins/commands/cap/000077500000000000000000000000001323370221500211365ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/cap/command_test.rb000066400000000000000000000023601323370221500241410ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/cap/command") describe VagrantPlugins::CommandCap::Command 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(:host) { double("host") } let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } let(:argv) { [] } subject { described_class.new(argv, iso_env) } before do allow(subject).to receive(:with_target_vms) { |&block| block.call machine } end describe "execute" do context "--check provider foo (exists)" do let(:argv) { ["--check", "provider", "foo"] } let(:cap) { Class.new } before do register_plugin do |p| p.provider_capability(:dummy, :foo) { cap } end end it "exits with 0 if it exists" do expect(subject.execute).to eq(0) end end context "--check provider foo (doesn't exists)" do let(:argv) { ["--check", "provider", "foo"] } it "exits with 1" do expect(subject.execute).to eq(1) end end end end vagrant-2.0.2/test/unit/plugins/commands/destroy/000077500000000000000000000000001323370221500220645ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/destroy/command_test.rb000066400000000000000000000122031323370221500250640ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/destroy/command") describe VagrantPlugins::CommandDestroy::Command do include_context "unit" let(:entry_klass) { Vagrant::MachineIndex::Entry } let(:argv) { [] } let(:vagrantfile_content){ "" } let(:iso_env) do env = isolated_environment env.vagrantfile(vagrantfile_content) env.create_vagrant_env end subject { described_class.new(argv, iso_env) } let(:action_runner) { double("action_runner") } def new_entry(name) entry_klass.new.tap do |e| e.name = name e.vagrantfile_path = "/bar" end end before do allow(iso_env).to receive(:action_runner).and_return(action_runner) end context "with no argument" do before { @state_var = :running } let(:vagrantfile_content){ "Vagrant.configure(2){|config| config.vm.box = 'dummy'}" } let(:state) { double("state", id: :running) } let(:machine) do iso_env.machine(iso_env.machine_names[0], :dummy).tap do |m| allow(m).to receive(:state).and_return(state) end end before do allow(subject).to receive(:with_target_vms) { |&block| block.call machine } end it "should destroy the default box" do allow_any_instance_of(Vagrant::BatchAction).to receive(:action) .with(machine, :destroy, anything) expect(machine.state).to receive(:id).and_return(:running) expect(machine.state).to receive(:id).and_return(:dead) expect(subject.execute).to eq(0) end it "exits 0 if vms are not created" do allow_any_instance_of(Vagrant::BatchAction).to receive(:action) .with(machine, :destroy, anything) allow(machine.state).to receive(:id).and_return(:not_created) expect(machine.state).to receive(:id).and_return(:not_created) expect(subject.execute).to eq(0) end it "exits 1 if a vms destroy was declined" do allow_any_instance_of(Vagrant::BatchAction).to receive(:action) .with(machine, :destroy, anything) expect(machine.state).to receive(:id).and_return(:running) expect(machine.state).to receive(:id).and_return(:running) expect(subject.execute).to eq(1) end context "with VAGRANT_DEFAULT_PROVIDER set" do before do if ENV["VAGRANT_DEFAULT_PROVIDER"] @original_default = ENV["VAGRANT_DEFAULT_PROVIDER"] end ENV["VAGRANT_DEFAULT_PROVIDER"] = "unknown" end after do if @original_default ENV["VAGRANT_DEFAULT_PROVIDER"] = @original_default else ENV.delete("VAGRANT_DEFAULT_PROVIDER") end end it "should attempt to use dummy provider" do expect{ subject.execute }.to raise_error(Vagrant::Errors::UnimplementedProviderAction) end end end context "with --parallel set" do let(:argv){ ["--parallel", "--force"] } it "passes in true to batch" do batch = double("environment_batch") expect(iso_env).to receive(:batch).with(true).and_yield(batch) expect(batch).to receive(:action).with(anything, :destroy, anything) do |machine,action,args| expect(machine).to be_kind_of(Vagrant::Machine) expect(action).to eq(:destroy) end subject.execute end end context "with a global machine" do let(:argv){ ["1234"] } it "destroys a vm with an id" do global_env = isolated_environment global_env.vagrantfile("Vagrant.configure(2){|config| config.vm.box = 'dummy'}") global_venv = global_env.create_vagrant_env global_machine = global_venv.machine(global_venv.machine_names[0], :dummy) global_machine.id = "1234" global = new_entry(global_machine.name) global.provider = "dummy" global.vagrantfile_path = global_env.workdir locked = iso_env.machine_index.set(global) iso_env.machine_index.release(locked) allow(subject).to receive(:with_target_vms) { |&block| block.call global_machine } batch = double("environment_batch") expect(iso_env).to receive(:batch).and_yield(batch) expect(batch).to receive(:action).with(global_machine, :destroy, anything) do |machine,action,args| expect(machine).to be_kind_of(Vagrant::Machine) expect(action).to eq(:destroy) end subject.execute end end context "with an argument" do let(:vagrantfile_content) do <<-VF Vagrant.configure("2") do |config| config.vm.define "app" config.vm.define "db" end VF end let(:argv){ ["app"] } let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } it "destroys a vm" do batch = double("environment_batch") expect(iso_env).to receive(:batch).and_yield(batch) expect(batch).to receive(:action).with(machine, :destroy, anything) do |machine,action,args| expect(machine).to be_kind_of(Vagrant::Machine) expect(action).to eq(:destroy) end subject.execute end context "with an invalid argument" do let(:argv){ ["notweb"] } it "raises an exception" do expect { subject.execute }.to raise_error(Vagrant::Errors::MachineNotFound) end end end end vagrant-2.0.2/test/unit/plugins/commands/global-status/000077500000000000000000000000001323370221500231545ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/global-status/command_test.rb000066400000000000000000000045711323370221500261650ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/global-status/command") describe VagrantPlugins::CommandGlobalStatus::Command do include_context "unit" let(:entry_klass) { Vagrant::MachineIndex::Entry } 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(:argv) { [] } def new_entry(name) entry_klass.new.tap do |e| e.name = name e.vagrantfile_path = "/bar" end end subject { described_class.new(argv, iso_env) } describe "execute with no args" do it "succeeds" do # Let's put some things in the index iso_env.machine_index.set(new_entry("foo")) iso_env.machine_index.set(new_entry("bar")) expect(subject.execute).to eq(0) end end describe "execute with --prune" do let(:argv) { ["--prune"] } it "removes invalid entries" do # Invalid entry because vagrantfile path is gone entryA = new_entry("A") entryA.vagrantfile_path = "/i/dont/exist" locked = iso_env.machine_index.set(entryA) iso_env.machine_index.release(locked) # Invalid entry because that specific machine doesn't exist anymore. entryB_env = isolated_environment entryB_env.vagrantfile("") entryB = new_entry("B") entryB.vagrantfile_path = entryB_env.workdir locked = iso_env.machine_index.set(entryB) iso_env.machine_index.release(locked) # Valid entry because the machine does exist entryC_env = isolated_environment entryC_env.vagrantfile("") entryC_venv = entryC_env.create_vagrant_env entryC_machine = entryC_venv.machine(entryC_venv.machine_names[0], :dummy) entryC_machine.id = "foo" entryC = new_entry(entryC_machine.name) entryC.provider = "dummy" entryC.vagrantfile_path = entryC_env.workdir locked = iso_env.machine_index.set(entryC) iso_env.machine_index.release(locked) expect(subject.execute).to eq(0) # Reload the data and see that we got things correct entries = [] iso_env.machine_index.each(true) { |e| entries << e } expect(entries.length).to eq(1) expect(entries[0].name).to eq(entryC_machine.name.to_s) end end end vagrant-2.0.2/test/unit/plugins/commands/init/000077500000000000000000000000001323370221500213365ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/init/command_test.rb000066400000000000000000000111751323370221500243450ustar00rootroot00000000000000require_relative "../../../base" require_relative "../../../../../plugins/commands/init/command" describe VagrantPlugins::CommandInit::Command do include_context "unit" include_context "command plugin helpers" let(:iso_env) do isolated_environment end let(:env) do iso_env.create_vagrant_env end let(:vagrantfile_path) { File.join(env.cwd, "Vagrantfile") } before do allow(Vagrant.plugin("2").manager).to receive(:commands).and_return({}) end after do iso_env.close end describe "#execute" do it "creates a Vagrantfile with no args" do described_class.new([], env).execute contents = File.read(vagrantfile_path) expect(contents).to match(/config.vm.box = "base"/) end it "creates a minimal Vagrantfile" do described_class.new(["-m"], env).execute contents = File.read(vagrantfile_path) expect(contents).to_not match(/#/) expect(contents).to_not match(/provision/) end it "creates a custom Vagrantfile using a relative template path" do described_class.new(["--template", "test/unit/templates/commands/init/Vagrantfile"], env).execute contents = File.read(vagrantfile_path) expect(contents).to match(/config.vm.hostname = "vagrant.dev"/) end it "creates a custom Vagrantfile using an absolute template path" do described_class.new(["--template", ::Vagrant.source_root.join("test/unit/templates/commands/init/Vagrantfile").to_s], env).execute contents = File.read(vagrantfile_path) expect(contents).to match(/config.vm.hostname = "vagrant.dev"/) end it "creates a custom Vagrantfile using a provided template with the extension included" do described_class.new(["--template", ::Vagrant.source_root.join("test/unit/templates/commands/init/Vagrantfile.erb").to_s], env).execute contents = File.read(vagrantfile_path) expect(contents).to match(/config.vm.hostname = "vagrant.dev"/) end it "ignores the -m option when using a provided template" do described_class.new(["-m", "--template", ::Vagrant.source_root.join("test/unit/templates/commands/init/Vagrantfile").to_s], env).execute contents = File.read(vagrantfile_path) expect(contents).to match(/config.vm.hostname = "vagrant.dev"/) end it "raises an appropriate exception when the template file can't be found" do expect { described_class.new(["--template", "./a/b/c/template"], env).execute }.to raise_error(Vagrant::Errors::VagrantfileTemplateNotFoundError) end it "does not overwrite an existing Vagrantfile" do # Create an existing Vagrantfile File.open(File.join(env.cwd, "Vagrantfile"), "w+") { |f| f.write("") } expect { described_class.new([], env).execute }.to raise_error(Vagrant::Errors::VagrantfileExistsError) end it "overwrites an existing Vagrantfile with force" do # Create an existing Vagrantfile File.open(File.join(env.cwd, "Vagrantfile"), "w+") { |f| f.write("") } expect { described_class.new(["-f"], env).execute }.to_not raise_error contents = File.read(vagrantfile_path) expect(contents).to match(/config.vm.box = "base"/) end it "creates a Vagrantfile with a box" do described_class.new(["hashicorp/precise64"], env).execute contents = File.read(vagrantfile_path) expect(contents).to match(/config.vm.box = "hashicorp\/precise64"/) end it "creates a Vagrantfile with a box and box_url" do described_class.new(["hashicorp/precise64", "http://mybox.com"], env).execute contents = File.read(vagrantfile_path) expect(contents).to match(/config.vm.box = "hashicorp\/precise64"/) expect(contents).to match(/config.vm.box_url = "http:\/\/mybox.com"/) end it "creates a Vagrantfile with a box and box version" do described_class.new(["--box-version", "1.2.3", "hashicorp/precise64"], env).execute contents = File.read(vagrantfile_path) expect(contents).to match(/config.vm.box = "hashicorp\/precise64"/) expect(contents).to match(/config.vm.box_version = "1.2.3"/) end it "creates a minimal Vagrantfile with a box and box version" do described_class.new(["--minimal", "--box-version", "1.2.3", "hashicorp/precise64"], env).execute contents = File.read(vagrantfile_path) expect(contents).to match(/config.vm.box = "hashicorp\/precise64"/) expect(contents).to match(/config.vm.box_version = "1.2.3"/) end it "creates a Vagrantfile at a custom path" do described_class.new(["--output", "vf.rb"], env).execute expect(File.exist?(File.join(env.cwd, "vf.rb"))).to be(true) end end end vagrant-2.0.2/test/unit/plugins/commands/list-commands/000077500000000000000000000000001323370221500231455ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/list-commands/command_test.rb000066400000000000000000000021751323370221500261540ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/list-commands/command") describe VagrantPlugins::CommandListCommands::Command do include_context "unit" include_context "command plugin helpers" 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(:argv) { [] } let(:commands) { {} } subject { described_class.new(argv, iso_env) } before do allow(Vagrant.plugin("2").manager).to receive(:commands).and_return(commands) end describe "execute" do it "includes all subcommands" do commands[:foo] = [command_lambda("foo", 0), { primary: true }] commands[:bar] = [command_lambda("bar", 0), { primary: true }] commands[:baz] = [command_lambda("baz", 0), { primary: false }] expect(iso_env.ui).to receive(:info).with(any_args) { |message, opts| expect(message).to include("foo") expect(message).to include("bar") expect(message).to include("baz") } subject.execute end end end vagrant-2.0.2/test/unit/plugins/commands/login/000077500000000000000000000000001323370221500215035ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/login/client_test.rb000066400000000000000000000153571323370221500243600ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/login/command") describe VagrantPlugins::LoginCommand::Client do include_context "unit" let(:env) { isolated_environment.create_vagrant_env } subject(:client) { described_class.new(env) } before(:all) do I18n.load_path << Vagrant.source_root.join("plugins/commands/login/locales/en.yml") I18n.reload! end before do stub_env("ATLAS_TOKEN" => nil) subject.clear_token end describe "#logged_in?" do let(:url) { "#{Vagrant.server_url}/api/v1/authenticate?access_token=#{token}" } let(:headers) { { "Content-Type" => "application/json" } } before { allow(subject).to receive(:token).and_return(token) } context "when there is no token" do let(:token) { nil } it "returns false" do expect(subject.logged_in?).to be(false) end end context "when there is a token" do let(:token) { "ABCD1234" } it "returns true if the endpoint returns a 200" do stub_request(:get, url) .with(headers: headers) .to_return(body: JSON.pretty_generate("token" => token)) expect(subject.logged_in?).to be(true) end it "raises an error if the endpoint returns a non-200" do stub_request(:get, url) .with(headers: headers) .to_return(body: JSON.pretty_generate("bad" => true), status: 401) expect(subject.logged_in?).to be(false) end it "raises an exception if the server cannot be found" do stub_request(:get, url) .to_raise(SocketError) expect { subject.logged_in? } .to raise_error(VagrantPlugins::LoginCommand::Errors::ServerUnreachable) end end end describe "#login" do let(:request) { { user: { login: login, password: password, }, token: { description: description, }, two_factor: { code: nil } } } let(:login) { "foo" } let(:password) { "bar" } let(:description) { "Token description" } let(:headers) { { "Accept" => "application/json", "Content-Type" => "application/json", } } let(:response) { { token: "baz" } } it "returns the access token after successful login" do stub_request(:post, "#{Vagrant.server_url}/api/v1/authenticate"). with(body: JSON.dump(request), headers: headers). to_return(status: 200, body: JSON.dump(response)) client.username_or_email = login client.password = password expect(client.login(description: "Token description")).to eq("baz") end context "when 2fa is required" do let(:response) { { two_factor: { default_delivery_method: default_delivery_method, delivery_methods: delivery_methods } } } let(:default_delivery_method) { "app" } let(:delivery_methods) { ["app"] } before do stub_request(:post, "#{Vagrant.server_url}/api/v1/authenticate"). to_return(status: 406, body: JSON.dump(response)) end it "raises a two-factor required error" do expect { client.login }.to raise_error(VagrantPlugins::LoginCommand::Errors::TwoFactorRequired) end context "when the default delivery method is not app" do let(:default_delivery_method) { "sms" } let(:delivery_methods) { ["app", "sms"] } it "requests a code and then raises a two-factor required error" do expect(client) .to receive(:request_code) .with(default_delivery_method) expect { client.login }.to raise_error(VagrantPlugins::LoginCommand::Errors::TwoFactorRequired) end end end context "on bad login" do before do stub_request(:post, "#{Vagrant.server_url}/api/v1/authenticate"). to_return(status: 401, body: "") end it "raises an error" do expect { client.login }.to raise_error(VagrantPlugins::LoginCommand::Errors::Unauthorized) end end context "if it can't reach the server" do before do stub_request(:post, "#{Vagrant.server_url}/api/v1/authenticate"). to_raise(SocketError) end it "raises an exception" do expect { subject.login }.to raise_error(VagrantPlugins::LoginCommand::Errors::ServerUnreachable) end end end describe "#request_code" do let(:request) { { user: { login: login, password: password, }, two_factor: { delivery_method: delivery_method } } } let(:login) { "foo" } let(:password) { "bar" } let(:delivery_method) { "sms" } let(:headers) { { "Accept" => "application/json", "Content-Type" => "application/json" } } let(:response) { { two_factor: { obfuscated_destination: "SMS number ending in 1234" } } } it "displays that the code was sent" do expect(env.ui) .to receive(:success) .with("2FA code sent to SMS number ending in 1234.") stub_request(:post, "#{Vagrant.server_url}/api/v1/two-factor/request-code"). with(body: JSON.dump(request), headers: headers). to_return(status: 201, body: JSON.dump(response)) client.username_or_email = login client.password = password client.request_code delivery_method end end describe "#token" do it "reads ATLAS_TOKEN" do stub_env("ATLAS_TOKEN" => "ABCD1234") expect(subject.token).to eq("ABCD1234") end it "reads the stored file" do subject.store_token("EFGH5678") expect(subject.token).to eq("EFGH5678") end it "prefers the environment variable" do stub_env("VAGRANT_CLOUD_TOKEN" => "ABCD1234") subject.store_token("EFGH5678") expect(subject.token).to eq("ABCD1234") end it "prints a warning if the envvar and stored file are both present" do stub_env("VAGRANT_CLOUD_TOKEN" => "ABCD1234") subject.store_token("EFGH5678") expect(env.ui).to receive(:warn).with(/detected both/) subject.token end it "returns nil if there's no token set" do expect(subject.token).to be(nil) end end describe "#store_token, #clear_token" do it "stores the token and can re-access it" do subject.store_token("foo") expect(subject.token).to eq("foo") expect(described_class.new(env).token).to eq("foo") end it "deletes the token" do subject.store_token("foo") subject.clear_token expect(subject.token).to be_nil end end end vagrant-2.0.2/test/unit/plugins/commands/login/command_test.rb000066400000000000000000000042721323370221500245120ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/login/command") describe VagrantPlugins::LoginCommand::Command do include_context "unit" let(:env) { isolated_environment.create_vagrant_env } let(:token_path) { env.data_dir.join("vagrant_login_token") } let(:stdout) { StringIO.new } let(:stderr) { StringIO.new } subject { described_class.new(argv, env) } before do stub_env("ATLAS_TOKEN" => "") end describe "#execute" do context "with no args" do let(:argv) { [] } end context "with --check" do let(:argv) { ["--check"] } context "when there is a token" do before do stub_request(:get, %r{^#{Vagrant.server_url}/api/v1/authenticate}) .to_return(status: 200) end before do File.open(token_path, "w+") { |f| f.write("abcd1234") } end it "returns 0" do expect(subject.execute).to eq(0) end end context "when there is no token" do it "returns 1" do expect(subject.execute).to eq(1) end end end context "with --logout" do let(:argv) { ["--logout"] } it "returns 0" do expect(subject.execute).to eq(0) end it "clears the token" do subject.execute expect(File.exist?(token_path)).to be(false) end end context "with --token" do let(:argv) { ["--token", "efgh5678"] } context "when the token is valid" do before do stub_request(:get, %r{^#{Vagrant.server_url}/api/v1/authenticate}) .to_return(status: 200) end it "sets the token" do subject.execute token = File.read(token_path).strip expect(token).to eq("efgh5678") end it "returns 0" do expect(subject.execute).to eq(0) end end context "when the token is invalid" do before do stub_request(:get, %r{^#{Vagrant.server_url}/api/v1/authenticate}) .to_return(status: 401) end it "returns 1" do expect(subject.execute).to eq(1) end end end end end vagrant-2.0.2/test/unit/plugins/commands/login/middleware/000077500000000000000000000000001323370221500236205ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/login/middleware/add_authentication_test.rb000066400000000000000000000060101323370221500310300ustar00rootroot00000000000000require 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(:env) { { env: iso_env, } } let(:iso_env) { isolated_environment.create_vagrant_env } let(:server_url) { "http://foo.com" } subject { described_class.new(app, env) } before do allow(Vagrant).to receive(:server_url).and_return(server_url) 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 "appends 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) token = "foobarbaz" VagrantPlugins::LoginCommand::Client.new(iso_env).store_token(token) original = [ "http://google.com/box.box", "http://app.vagrantup.com/foo.box", "http://vagrantcloud.com/foo.box", "http://vagrantcloud.com/bar.box?arg=true", ] expected = original.dup expected[1] = "#{original[1]}?access_token=#{token}" expected[2] = "#{original[2]}?access_token=#{token}" expected[3] = "#{original[3]}&access_token=#{token}" env[:box_urls] = original.dup subject.call(env) expect(env[:box_urls]).to eq(expected) 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 vagrant-2.0.2/test/unit/plugins/commands/package/000077500000000000000000000000001323370221500217665ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/package/command_test.rb000066400000000000000000000062041323370221500247720ustar00rootroot00000000000000require_relative "../../../base" require_relative "../../../../../plugins/commands/package/command" RSpec::Matchers.define :a_machine_named do |name| match{ |actual| actual.name.to_s == name.to_s } end RSpec::Matchers.define :an_existing_directory do match{ |actual| File.directory?(actual) } end describe VagrantPlugins::CommandPackage::Command do include_context "unit" let(:argv) { [] } 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(:package_command) { described_class.new(argv, iso_env) } let(:action_runner) { double("action_runner") } before do allow(iso_env).to receive(:action_runner).and_return(action_runner) end describe "#execute" do context "with no arguments" do it "packages default machine" do expect(package_command).to receive(:package_vm).with(a_machine_named('default'), {}) package_command.execute end end context "with single argument" do context "set to default" do let(:argv){ ['default'] } it "packages default machine" do expect(package_command).to receive(:package_vm).with(a_machine_named('default'), {}) package_command.execute end end context "set to undefined vm" do let(:argv){ ['undefined'] } it "raises machine not found error" do expect{ package_command.execute }.to raise_error(Vagrant::Errors::MachineNotFound) end end context "with --output option" do let(:argv){ ['--output', 'package-output-folder/default'] } it "packages default machine inside specified folder" do expect(package_command).to receive(:package_vm).with( a_machine_named('default'), :output => "package-output-folder/default" ) package_command.execute end end end context "with multiple arguments" do let(:argv){ ['default', 'undefined'] } it "ignores the extra arguments" do expect(package_command).to receive(:package_vm).with(a_machine_named('default'), {}) package_command.execute end end context "with --base option" do context "and no option value" do let(:argv){ ['--base'] } it "shows help" do expect{ package_command.execute }.to raise_error(Vagrant::Errors::CLIInvalidOptions) end end context "and option value" do let(:argv){ ['--base', 'machine-id'] } it "packages vm defined within virtualbox" do expect(package_command).to receive(:package_base).with(:base => 'machine-id') package_command.execute end it "provides a machine data directory" do expect(Vagrant::Machine).to receive(:new).with( 'machine-id', :virtualbox, anything, nil, anything, anything, an_existing_directory, anything, anything, anything, anything).and_return(double("vm", name: "machine-id")) allow(package_command).to receive(:package_vm) package_command.execute end end end end end vagrant-2.0.2/test/unit/plugins/commands/plugin/000077500000000000000000000000001323370221500216715ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/plugin/action/000077500000000000000000000000001323370221500231465ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/plugin/action/expunge_plugins_test.rb000066400000000000000000000033011323370221500277430ustar00rootroot00000000000000require File.expand_path("../../../../../base", __FILE__) describe VagrantPlugins::CommandPlugin::Action::ExpungePlugins do let(:app) { lambda { |env| } } let(:home_path){ '/fake/file/path/.vagrant.d' } let(:gems_path){ "#{home_path}/gems" } let(:force){ true } let(:env) {{ ui: Vagrant::UI::Silent.new, home_path: home_path, gems_path: gems_path, force: force }} let(:manager) { double("manager") } let(:expect_to_receive) do lambda do allow(File).to receive(:exist?).with(File.join(home_path, 'plugins.json')).and_return(true) allow(File).to receive(:directory?).with(gems_path).and_return(true) expect(FileUtils).to receive(:rm).with(File.join(home_path, 'plugins.json')) expect(FileUtils).to receive(:rm_rf).with(gems_path) expect(app).to receive(:call).with(env).once end end subject { described_class.new(app, env) } before do allow(Vagrant::Plugin::Manager).to receive(:instance).and_return(manager) end describe "#call" do before do instance_exec(&expect_to_receive) end it "should delete all plugins" do subject.call(env) end describe "when force is false" do let(:force){ false } it "should prompt user before deleting all plugins" do expect(env[:ui]).to receive(:ask).and_return("Y\n") subject.call(env) end describe "when user declines prompt" do let(:expect_to_receive) do lambda do expect(app).not_to receive(:call) end end it "should not delete all plugins" do expect(env[:ui]).to receive(:ask).and_return("N\n") subject.call(env) end end end end end vagrant-2.0.2/test/unit/plugins/commands/plugin/action/install_gem_test.rb000066400000000000000000000052051323370221500270320ustar00rootroot00000000000000require File.expand_path("../../../../../base", __FILE__) describe VagrantPlugins::CommandPlugin::Action::InstallGem do let(:app) { lambda { |env| } } let(:env) {{ ui: Vagrant::UI::Silent.new }} let(:manager) { double("manager") } subject { described_class.new(app, env) } before do allow(Vagrant::Plugin::Manager).to receive(:instance).and_return(manager) end describe "#call" do it "should install the plugin" do spec = Gem::Specification.new expect(manager).to receive(:install_plugin).with( "foo", version: nil, require: nil, sources: nil, verbose: false).once.and_return(spec) expect(app).to receive(:call).with(env).once env[:plugin_name] = "foo" subject.call(env) end it "should specify the version if given" do spec = Gem::Specification.new expect(manager).to receive(:install_plugin).with( "foo", version: "bar", require: nil, sources: nil, verbose: false).once.and_return(spec) expect(app).to receive(:call).with(env).once env[:plugin_name] = "foo" env[:plugin_version] = "bar" subject.call(env) end it "should specify the entrypoint if given" do spec = Gem::Specification.new expect(manager).to receive(:install_plugin).with( "foo", version: "bar", require: "baz", sources: nil, verbose: false).once.and_return(spec) expect(app).to receive(:call).with(env).once env[:plugin_entry_point] = "baz" env[:plugin_name] = "foo" env[:plugin_version] = "bar" subject.call(env) end it "should specify the sources if given" do spec = Gem::Specification.new expect(manager).to receive(:install_plugin).with( "foo", version: nil, require: nil, sources: ["foo"], verbose: false).once.and_return(spec) expect(app).to receive(:call).with(env).once env[:plugin_name] = "foo" env[:plugin_sources] = ["foo"] subject.call(env) end end describe "#recover" do it "should do nothing by default" do subject.recover(env) end context "with a successful plugin install" do let(:action_runner) { double("action_runner") } before do spec = Gem::Specification.new spec.name = "foo" allow(manager).to receive(:install_plugin).and_return(spec) env[:plugin_name] = "foo" subject.call(env) env[:action_runner] = action_runner end it "should uninstall the plugin" do expect(action_runner).to receive(:run).with(any_args) { |action, newenv| expect(newenv[:plugin_name]).to eql("foo") } subject.recover(env) end end end end vagrant-2.0.2/test/unit/plugins/commands/plugin/action/plugin_exists_check_test.rb000066400000000000000000000016201323370221500305630ustar00rootroot00000000000000require File.expand_path("../../../../../base", __FILE__) describe VagrantPlugins::CommandPlugin::Action::PluginExistsCheck do let(:app) { lambda {} } let(:env) { {} } let(:manager) { double("manager") } subject { described_class.new(app, env) } before do allow(Vagrant::Plugin::Manager).to receive(:instance).and_return(manager) end it "should raise an exception if the plugin doesn't exist" do allow(manager).to receive(:installed_plugins).and_return({ "foo" => {} }) expect(app).not_to receive(:call) env[:plugin_name] = "bar" expect { subject.call(env) }. to raise_error(Vagrant::Errors::PluginNotInstalled) end it "should call the app if the plugin is installed" do allow(manager).to receive(:installed_plugins).and_return({ "bar" => {} }) expect(app).to receive(:call).once.with(env) env[:plugin_name] = "bar" subject.call(env) end end vagrant-2.0.2/test/unit/plugins/commands/plugin/action/uninstall_plugin_test.rb000066400000000000000000000011431323370221500301200ustar00rootroot00000000000000require File.expand_path("../../../../../base", __FILE__) describe VagrantPlugins::CommandPlugin::Action::UninstallPlugin do let(:app) { lambda { |env| } } let(:env) {{ ui: Vagrant::UI::Silent.new, }} let(:manager) { double("manager") } subject { described_class.new(app, env) } before do allow(Vagrant::Plugin::Manager).to receive(:instance).and_return(manager) end it "uninstalls the specified plugin" do expect(manager).to receive(:uninstall_plugin).with("bar").ordered expect(app).to receive(:call).ordered env[:plugin_name] = "bar" subject.call(env) end end vagrant-2.0.2/test/unit/plugins/commands/plugin/action/update_gems_test.rb000066400000000000000000000021141323370221500270250ustar00rootroot00000000000000require File.expand_path("../../../../../base", __FILE__) describe VagrantPlugins::CommandPlugin::Action::UpdateGems do let(:app) { lambda { |env| } } let(:env) {{ ui: Vagrant::UI::Silent.new }} let(:manager) { double("manager") } subject { described_class.new(app, env) } before do allow(Vagrant::Plugin::Manager).to receive(:instance).and_return(manager) allow(manager).to receive(:installed_specs).and_return([]) end describe "#call" do it "should update all plugins if none are specified" do expect(manager).to receive(:update_plugins).with([]).once.and_return([]) expect(manager).to receive(:installed_plugins).twice.and_return({}) expect(app).to receive(:call).with(env).once subject.call(env) end it "should update specified plugins" do expect(manager).to receive(:update_plugins).with(["foo"]).once.and_return([]) expect(manager).to receive(:installed_plugins).twice.and_return({}) expect(app).to receive(:call).with(env).once env[:plugin_name] = ["foo"] subject.call(env) end end end vagrant-2.0.2/test/unit/plugins/commands/port/000077500000000000000000000000001323370221500213575ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/port/command_test.rb000066400000000000000000000101571323370221500243650ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/port/command") describe VagrantPlugins::CommandPort::Command do include_context "unit" include_context "command plugin helpers" let(:iso_env) { isolated_environment } let(:env) do iso_env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "hashicorp/precise64" end VF iso_env.create_vagrant_env end let(:state) { double(:state, id: :running) } let(:machine) { env.machine(env.machine_names[0], :dummy) } before(:all) do I18n.load_path << Vagrant.source_root.join("plugins/commands/port/locales/en.yml") I18n.reload! end subject { described_class.new([], env) } before do allow(machine).to receive(:state).and_return(state) allow(subject).to receive(:with_target_vms) { |&block| block.call(machine) } end describe "#execute" do it "validates the configuration" do iso_env.vagrantfile <<-EOH Vagrant.configure("2") do |config| config.vm.box = "hashicorp/precise64" config.push.define "noop" do |push| push.bad = "ham" end end EOH subject = described_class.new([], iso_env.create_vagrant_env) expect { subject.execute }.to raise_error(Vagrant::Errors::ConfigInvalid) { |err| expect(err.message).to include("The following settings shouldn't exist: bad") } end it "ensures the vm is running" do allow(state).to receive(:id).and_return(:stopped) expect(env.ui).to receive(:error).with(any_args) { |message, _| expect(message).to include("does not support listing forwarded ports") } expect(subject.execute).to eq(1) end it "shows a friendly error when the capability is not supported" do allow(machine.provider).to receive(:capability?).and_return(false) expect(env.ui).to receive(:error).with(any_args) { |message, _| expect(message).to include("does not support listing forwarded ports") } expect(subject.execute).to eq(1) end it "returns a friendly message when there are no forwarded ports" do allow(machine.provider).to receive(:capability?).and_return(true) allow(machine.provider).to receive(:capability).with(:forwarded_ports) .and_return([]) expect(env.ui).to receive(:info).with(any_args) { |message, _| expect(message).to include("there are no forwarded ports") } expect(subject.execute).to eq(0) end it "returns the list of ports" do allow(machine.provider).to receive(:capability?).and_return(true) allow(machine.provider).to receive(:capability).with(:forwarded_ports) .and_return([[2222,22], [1111,11]]) output = "" allow(env.ui).to receive(:info) do |data| output << data end expect(subject.execute).to eq(0) expect(output).to include("forwarded ports for the machine") expect(output).to include("22 (guest) => 2222 (host)") expect(output).to include("11 (guest) => 1111 (host)") end it "prints the matching host port when --guest is given" do argv = ["--guest", "22"] subject = described_class.new(argv, env) allow(machine.provider).to receive(:capability?).and_return(true) allow(machine.provider).to receive(:capability).with(:forwarded_ports) .and_return([[2222,22]]) output = "" allow(env.ui).to receive(:info) do |data| output << data end expect(subject.execute).to eq(0) expect(output).to eq("2222") end it "returns an error with no port is mapped to the --guest option" do argv = ["--guest", "80"] subject = described_class.new(argv, env) allow(machine.provider).to receive(:capability?).and_return(true) allow(machine.provider).to receive(:capability).with(:forwarded_ports) .and_return([[2222,22]]) output = "" allow(env.ui).to receive(:error) do |data| output << data end expect(subject.execute).to_not eq(0) expect(output).to include("not currently mapping port 80") end end end vagrant-2.0.2/test/unit/plugins/commands/provider/000077500000000000000000000000001323370221500222255ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/provider/command_test.rb000066400000000000000000000022451323370221500252320ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/provider/command") describe VagrantPlugins::CommandProvider::Command 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(:host) { double("host") } let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } let(:argv) { [] } subject { described_class.new(argv, iso_env) } before do allow(subject).to receive(:with_target_vms) { |&block| block.call machine } end describe "execute" do context "no arguments" do it "exits with the provider name" do expect(subject.execute).to eq(0) end end context "--usable" do let(:argv) { ["--usable"] } it "exits 0 if it is usable" do expect(subject.execute).to eq(0) end it "exits 1 if it is not usable" do expect(machine.provider.class).to receive(:usable?).and_return(false) expect(subject.execute).to eq(1) end end end end vagrant-2.0.2/test/unit/plugins/commands/push/000077500000000000000000000000001323370221500213525ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/push/command_test.rb000066400000000000000000000077221323370221500243640ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/push/command") describe VagrantPlugins::CommandPush::Command do include_context "unit" include_context "command plugin helpers" let(:iso_env) { isolated_environment } let(:env) do iso_env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "hashicorp/precise64" end VF iso_env.create_vagrant_env end let(:argv) { [] } let(:pushes) { {} } subject { described_class.new(argv, env) } before do allow(Vagrant.plugin("2").manager).to receive(:pushes).and_return(pushes) end describe "#execute" do before do allow(subject).to receive(:validate_pushes!) .and_return(:noop) allow(env).to receive(:pushes) allow(env).to receive(:push) end it "validates the pushes" do expect(subject).to receive(:validate_pushes!).once subject.execute end it "delegates to Environment#push" do expect(env).to receive(:push).once subject.execute end it "validates the configuration" do iso_env.vagrantfile <<-EOH Vagrant.configure("2") do |config| config.vm.box = "hashicorp/precise64" config.push.define "noop" do |push| push.bad = "ham" end end EOH subject = described_class.new(argv, iso_env.create_vagrant_env) allow(subject).to receive(:validate_pushes!) .and_return(:noop) expect { subject.execute }.to raise_error(Vagrant::Errors::ConfigInvalid) { |err| expect(err.message).to include("The following settings shouldn't exist: bad") } end end describe "#validate_pushes!" do context "when there are no pushes defined" do let(:pushes) { [] } context "when a strategy is given" do it "raises an exception" do expect { subject.validate_pushes!(pushes, :noop) } .to raise_error(Vagrant::Errors::PushesNotDefined) end end context "when no strategy is given" do it "raises an exception" do expect { subject.validate_pushes!(pushes) } .to raise_error(Vagrant::Errors::PushesNotDefined) end end end context "when there is one push defined" do let(:noop) { double("noop") } let(:pushes) { [:noop] } context "when a strategy is given" do context "when that strategy is not defined" do it "raises an exception" do expect { subject.validate_pushes!(pushes, :bacon) } .to raise_error(Vagrant::Errors::PushStrategyNotDefined) end end context "when that strategy is defined" do it "returns that push" do expect(subject.validate_pushes!(pushes, :noop)).to eq(:noop) end end end context "when no strategy is given" do it "returns the strategy" do expect(subject.validate_pushes!(pushes)).to eq(:noop) end end end context "when there are multiple pushes defined" do let(:noop) { double("noop") } let(:ftp) { double("ftp") } let(:pushes) { [:noop, :ftp] } context "when a strategy is given" do context "when that strategy is not defined" do it "raises an exception" do expect { subject.validate_pushes!(pushes, :bacon) } .to raise_error(Vagrant::Errors::PushStrategyNotDefined) end end context "when that strategy is defined" do it "returns the strategy" do expect(subject.validate_pushes!(pushes, :noop)).to eq(:noop) expect(subject.validate_pushes!(pushes, :ftp)).to eq(:ftp) end end end context "when no strategy is given" do it "raises an exception" do expect { subject.validate_pushes!(pushes) } .to raise_error(Vagrant::Errors::PushStrategyNotProvided) end end end end end vagrant-2.0.2/test/unit/plugins/commands/snapshot/000077500000000000000000000000001323370221500222325ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/snapshot/command/000077500000000000000000000000001323370221500236505ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/snapshot/command/delete_test.rb000066400000000000000000000052461323370221500265050ustar00rootroot00000000000000require File.expand_path("../../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/snapshot/command/delete") describe VagrantPlugins::CommandSnapshot::Command::Delete 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(:host) { double("host") } let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } let(:argv) { [] } subject { described_class.new(argv, iso_env) } before do allow(machine.provider).to receive(:capability).with(:snapshot_list). and_return([]) allow(machine.provider).to receive(:capability?).with(:snapshot_list). and_return(true) allow(subject).to receive(:with_target_vms) { |&block| block.call machine } end describe "execute" do context "with no arguments" do it "shows help" do expect { subject.execute }. to raise_error(Vagrant::Errors::CLIInvalidUsage) end end context "with an unsupported provider" do let(:argv) { ["test"] } before do allow(machine.provider).to receive(:capability?).with(:snapshot_list). and_return(false) end it "raises an exception" do machine.id = "foo" expect { subject.execute }. to raise_error(Vagrant::Errors::SnapshotNotSupported) end end context "with a snapshot name given" do let(:argv) { ["test"] } it "calls snapshot_delete with a snapshot name" do machine.id = "foo" allow(machine.provider).to receive(:capability).with(:snapshot_list). and_return(["test"]) expect(machine).to receive(:action) do |name, opts| expect(name).to eq(:snapshot_delete) expect(opts[:snapshot_name]).to eq("test") end expect(subject.execute).to eq(0) end it "doesn't delete a snapshot on 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 snapshot name that doesn't exist" do let(:argv) { ["nopetest"] } it "fails to take a snapshot and prints a warning to the user" do machine.id = "foo" allow(machine.provider).to receive(:capability).with(:snapshot_list). and_return(["test"]) expect(machine).to_not receive(:action) expect { subject.execute }. to raise_error(Vagrant::Errors::SnapshotNotFound) end end end end vagrant-2.0.2/test/unit/plugins/commands/snapshot/command/list_test.rb000066400000000000000000000045601323370221500262140ustar00rootroot00000000000000require File.expand_path("../../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/snapshot/command/list") describe VagrantPlugins::CommandSnapshot::Command::List 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(:host) { double("host") } let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } let(:argv) { [] } subject { described_class.new(argv, iso_env) } before do allow(machine.provider).to receive(:capability?).with(:snapshot_list). and_return(true) allow(machine.provider).to receive(:capability).with(:snapshot_list). and_return([]) allow(subject).to receive(:with_target_vms) { |&block| block.call machine } end describe "execute" do context "with an unsupported provider" do let(:argv) { ["foo"] } before do allow(machine.provider).to receive(:capability?).with(:snapshot_list). and_return(false) end it "raises an exception" do machine.id = "foo" expect { subject.execute }. to raise_error(Vagrant::Errors::SnapshotNotSupported) end end context "with a vm given" do let(:argv) { ["foo"] } it "prints a message if the vm does not exist" do machine.id = nil expect(iso_env.ui).to receive(:info).with("==> default: VM not created. Moving on...", anything) .and_return({}) expect(machine).to_not receive(:action) expect(subject.execute).to eq(0) end it "prints a message if no snapshots have been taken" do machine.id = "foo" expect(iso_env.ui).to receive(:output) .with(/No snapshots have been taken yet!/, anything) expect(subject.execute).to eq(0) end it "prints a list of snapshots" do machine.id = "foo" 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(subject.execute).to eq(0) end end end end vagrant-2.0.2/test/unit/plugins/commands/snapshot/command/pop_test.rb000066400000000000000000000026441323370221500260400ustar00rootroot00000000000000require File.expand_path("../../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/snapshot/command/pop") describe VagrantPlugins::CommandSnapshot::Command::Pop 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(:host) { double("host") } let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } let(:argv) { [] } subject { described_class.new(argv, iso_env) } before do allow(subject).to receive(:with_target_vms) { |&block| block.call machine } end describe "execute" do it "calls snapshot_restore with the last pushed snapshot" do machine.id = "foo" allow(machine.provider).to receive(:capability). with(:snapshot_list).and_return(["push_2_0", "push_1_0"]) expect(machine).to receive(:action) do |name, opts| expect(name).to eq(:snapshot_restore) expect(opts[:snapshot_name]).to eq("push_2_0") end expect(subject.execute).to eq(0) end it "isn't an error if no matching snapshot" do machine.id = "foo" allow(machine.provider).to receive(:capability). with(:snapshot_list).and_return(["foo"]) expect(machine).to_not receive(:action) expect(subject.execute).to eq(0) end end end vagrant-2.0.2/test/unit/plugins/commands/snapshot/command/push_test.rb000066400000000000000000000022731323370221500262170ustar00rootroot00000000000000require File.expand_path("../../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/snapshot/command/push") describe VagrantPlugins::CommandSnapshot::Command::Push 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(:host) { double("host") } let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } let(:argv) { [] } subject { described_class.new(argv, iso_env) } before do allow(subject).to receive(:with_target_vms) { |&block| block.call machine } end describe "execute" do it "calls snapshot_save with a random 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 match(/^push_/) end expect(subject.execute).to eq(0) end it "doesn't snapshot a non-existent machine" do machine.id = nil expect(machine).to_not receive(:action) expect(subject.execute).to eq(0) end end end vagrant-2.0.2/test/unit/plugins/commands/snapshot/command/restore_test.rb000066400000000000000000000052511323370221500267220ustar00rootroot00000000000000require File.expand_path("../../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/snapshot/command/restore") describe VagrantPlugins::CommandSnapshot::Command::Restore 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(:host) { double("host") } let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } let(:argv) { [] } subject { described_class.new(argv, iso_env) } before do allow(machine.provider).to receive(:capability).with(:snapshot_list). and_return([]) allow(machine.provider).to receive(:capability?).with(:snapshot_list). and_return(true) allow(subject).to receive(:with_target_vms) { |&block| block.call machine } end describe "execute" do context "with no arguments" do it "shows help" do expect { subject.execute }. to raise_error(Vagrant::Errors::CLIInvalidUsage) end end context "with an unsupported provider" do let(:argv) { ["test"] } before do allow(machine.provider).to receive(:capability?).with(:snapshot_list). and_return(false) end it "raises an exception" do machine.id = "foo" expect { subject.execute }. to raise_error(Vagrant::Errors::SnapshotNotSupported) end end context "with a snapshot name given" do let(:argv) { ["test"] } it "calls snapshot_delete with a snapshot name" do machine.id = "foo" allow(machine.provider).to receive(:capability).with(:snapshot_list). and_return(["test"]) expect(machine).to receive(:action) do |name, opts| expect(name).to eq(:snapshot_restore) expect(opts[:snapshot_name]).to eq("test") end expect(subject.execute).to eq(0) end it "doesn't delete a snapshot on 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 snapshot name that doesn't exist" do let(:argv) { ["nopetest"] } it "fails to take a snapshot and prints a warning to the user" do machine.id = "foo" allow(machine.provider).to receive(:capability).with(:snapshot_list). and_return(["test"]) expect(machine).to_not receive(:action) expect { subject.execute }. to raise_error(Vagrant::Errors::SnapshotNotFound) end end end end vagrant-2.0.2/test/unit/plugins/commands/snapshot/command/save_test.rb000066400000000000000000000061441323370221500261770ustar00rootroot00000000000000require File.expand_path("../../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/snapshot/command/save") describe VagrantPlugins::CommandSnapshot::Command::Save 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(:host) { double("host") } let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } let(:argv) { [] } subject { described_class.new(argv, iso_env) } before do allow(machine.provider).to receive(:capability).with(:snapshot_list). and_return([]) allow(machine.provider).to receive(:capability?).with(:snapshot_list). and_return(true) allow(subject).to receive(:with_target_vms) { |&block| block.call machine } end describe "execute" do context "with no arguments" do it "shows help" do expect { subject.execute }. to raise_error(Vagrant::Errors::CLIInvalidUsage) end end context "with an unsupported provider" do let(:argv) { ["test"] } before do allow(machine.provider).to receive(:capability?).with(:snapshot_list). and_return(false) end it "raises an exception" do machine.id = "foo" expect { subject.execute }. to raise_error(Vagrant::Errors::SnapshotNotSupported) end end context "with a snapshot name given" do let(:argv) { ["test"] } 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("test") 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"] } it "fails to take a snapshot and prints a warning to the user" do machine.id = "fool" allow(machine.provider).to receive(:capability).with(:snapshot_list). and_return(["test"]) expect(machine).to_not receive(:action) expect { subject.execute }. to raise_error(Vagrant::Errors::SnapshotConflictFailed) end end context "with a duplicate snapshot name given and a force flag" do let(:argv) { ["test", "--force"] } it "deletes the existing snapshot and takes a new one" do machine.id = "foo" allow(machine.provider).to receive(:capability).with(:snapshot_list). and_return(["test"]) expect(machine).to receive(:action).with(:snapshot_delete, snapshot_name: "test") expect(machine).to receive(:action).with(:snapshot_save, snapshot_name: "test") expect(subject.execute).to eq(0) end end end end vagrant-2.0.2/test/unit/plugins/commands/ssh_config/000077500000000000000000000000001323370221500225155ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/ssh_config/command_test.rb000066400000000000000000000105331323370221500255210ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/ssh_config/command") describe VagrantPlugins::CommandSSHConfig::Command do include_context "unit" include_context "virtualbox" 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(:host) { double("host") } let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } let(:argv) { [] } let(:ssh_info) {{ host: "testhost.vagrant.dev", port: 1234, username: "testuser", keys_only: true, verify_host_key: false, private_key_path: ["/home/vagrant/.private/keys.key"], forward_agent: false, forward_x11: false }} subject { described_class.new(argv, iso_env) } before do allow(machine).to receive(:ssh_info).and_return(ssh_info) allow(subject).to receive(:with_target_vms) { |&block| block.call machine } end describe "execute" do it "prints out the ssh config for the given machine" do output = "" allow(subject).to receive(:safe_puts) do |data| output += data if data end subject.execute expect(output).to eq(<<-SSHCONFIG) Host #{machine.name} HostName testhost.vagrant.dev User testuser Port 1234 UserKnownHostsFile /dev/null StrictHostKeyChecking no PasswordAuthentication no IdentityFile /home/vagrant/.private/keys.key IdentitiesOnly yes LogLevel FATAL SSHCONFIG end it "turns on agent forwarding when it is configured" do allow(machine).to receive(:ssh_info) { ssh_info.merge(forward_agent: true) } output = "" allow(subject).to receive(:safe_puts) do |data| output += data if data end subject.execute expect(output).to include("ForwardAgent yes") end it "turns on x11 forwarding when it is configured" do allow(machine).to receive(:ssh_info) { ssh_info.merge(forward_x11: true) } output = "" allow(subject).to receive(:safe_puts) do |data| output += data if data end subject.execute expect(output).to include("ForwardX11 yes") end it "handles multiple private key paths" do allow(machine).to receive(:ssh_info) { ssh_info.merge(private_key_path: ["foo", "bar"]) } output = "" allow(subject).to receive(:safe_puts) do |data| output += data if data end subject.execute expect(output).to include("IdentityFile foo") expect(output).to include("IdentityFile bar") end it "puts quotes around an identityfile path if it has a space" do allow(machine).to receive(:ssh_info) { ssh_info.merge(private_key_path: ["with a space"]) } output = "" allow(subject).to receive(:safe_puts) do |data| output += data if data end subject.execute expect(output).to include('IdentityFile "with a space"') end it "omits IdentitiesOnly when keys_only is false" do allow(machine).to receive(:ssh_info) { ssh_info.merge(keys_only: false) } output = "" allow(subject).to receive(:safe_puts) do |data| output += data if data end subject.execute expect(output).not_to include('IdentitiesOnly') end it "omits StrictHostKeyChecking and UserKnownHostsFile when verify_host_key is true" do allow(machine).to receive(:ssh_info) { ssh_info.merge(verify_host_key: true) } output = "" allow(subject).to receive(:safe_puts) do |data| output += data if data end subject.execute expect(output).not_to include('StrictHostKeyChecking ') expect(output).not_to include('UserKnownHostsFile ') end it "formats windows paths if windows" do allow(machine).to receive(:ssh_info) { ssh_info.merge(private_key_path: ["C:\\path\\to\\vagrant\\home.key"]) } allow(Vagrant::Util::Platform).to receive(:format_windows_path).and_return("/home/vagrant/home.key") allow(Vagrant::Util::Platform).to receive(:windows?).and_return(true) output = "" allow(subject).to receive(:safe_puts) do |data| output += data if data end subject.execute expect(output).to include('IdentityFile /home/vagrant/home.key') end end end vagrant-2.0.2/test/unit/plugins/commands/up/000077500000000000000000000000001323370221500210175ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/up/command_test.rb000066400000000000000000000076541323370221500240350ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/commands/up/command") describe VagrantPlugins::CommandUp::Command do include_context "unit" let(:entry_klass) { Vagrant::MachineIndex::Entry } let(:argv) { [] } let(:vagrantfile_content){ "" } let(:iso_env) do env = isolated_environment env.vagrantfile(vagrantfile_content) env.create_vagrant_env end subject { described_class.new(argv, iso_env) } let(:action_runner) { double("action_runner") } def new_entry(name) entry_klass.new.tap do |e| e.name = name e.vagrantfile_path = "/bar" end end before do allow(iso_env).to receive(:action_runner).and_return(action_runner) end context "with no argument" do let(:vagrantfile_content){ "Vagrant.configure(2){|config| config.vm.box = 'dummy'}" } it "should bring up the default box" do batch = double("environment_batch") expect(iso_env).to receive(:batch).and_yield(batch) expect(batch).to receive(:action).with(anything, :up, anything) subject.execute end context "with VAGRANT_DEFAULT_PROVIDER set" do before do if ENV["VAGRANT_DEFAULT_PROVIDER"] @original_default = ENV["VAGRANT_DEFAULT_PROVIDER"] end ENV["VAGRANT_DEFAULT_PROVIDER"] = "unknown" end after do if @original_default ENV["VAGRANT_DEFAULT_PROVIDER"] = @original_default else ENV.delete("VAGRANT_DEFAULT_PROVIDER") end end it "should attempt to use dummy provider" do expect{ subject.execute }.to raise_error(Vagrant::Errors::ProviderNotFound) end context "with --provider set" do let(:argv){ ["--provider", "dummy"] } it "should only use provider explicitly set" do batch = double("environment_batch") expect(iso_env).to receive(:batch).and_yield(batch) expect(batch).to receive(:action).with(anything, :up, anything) subject.execute end end end end context "with a global machine" do let(:argv){ ["1234"] } it "brings up a vm with an id" do global_env = isolated_environment global_env.vagrantfile("Vagrant.configure(2){|config| config.vm.box = 'dummy'}") global_venv = global_env.create_vagrant_env global_machine = global_venv.machine(global_venv.machine_names[0], :dummy) global_machine.id = "1234" global = new_entry(global_machine.name) global.provider = "dummy" global.vagrantfile_path = global_env.workdir locked = iso_env.machine_index.set(global) iso_env.machine_index.release(locked) allow(subject).to receive(:with_target_vms) { |&block| block.call global_machine } batch = double("environment_batch") expect(iso_env).to receive(:batch).and_yield(batch) expect(batch).to receive(:action).with(global_machine, :up, anything) do |machine,action,args| expect(machine).to be_kind_of(Vagrant::Machine) expect(action).to eq(:up) end subject.execute end end context "with an argument" do let(:vagrantfile_content) do <<-VF Vagrant.configure("2") do |config| config.vm.define "app" config.vm.define "db" end VF end let(:argv){ ["app"] } let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } it "brings up a vm" do batch = double("environment_batch") expect(iso_env).to receive(:batch).and_yield(batch) expect(batch).to receive(:action).with(machine, :up, anything) do |machine,action,args| expect(machine).to be_kind_of(Vagrant::Machine) expect(action).to eq(:up) end subject.execute end context "with an invalid argument" do let(:argv){ ["notweb"] } it "brings up a vm" do expect { subject.execute }.to raise_error(Vagrant::Errors::MachineNotFound) end end end end vagrant-2.0.2/test/unit/plugins/commands/validate/000077500000000000000000000000001323370221500221645ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/commands/validate/command_test.rb000066400000000000000000000051601323370221500251700ustar00rootroot00000000000000require_relative "../../../base" require_relative "../../../../../plugins/commands/validate/command" describe VagrantPlugins::CommandValidate::Command do include_context "unit" include_context "command plugin helpers" let(:iso_env) do isolated_environment end let(:env) do iso_env.create_vagrant_env end let(:argv) { [] } before(:all) do I18n.load_path << Vagrant.source_root.join("plugins/commands/port/locales/en.yml") I18n.reload! end subject { described_class.new(argv, env) } describe "#execute" do it "validates correct Vagrantfile" do iso_env.vagrantfile(<<-EOH) Vagrant.configure("2") do |config| config.vm.box = "hashicorp/precise64" end EOH expect(env.ui).to receive(:info).with(any_args) { |message, _| expect(message).to include("Vagrantfile validated successfully.") } expect(subject.execute).to eq(0) end it "validates the configuration" do iso_env.vagrantfile <<-EOH Vagrant.configure("2") do |config| config.vm.bix = "hashicorp/precise64" end EOH expect { subject.execute }.to raise_error(Vagrant::Errors::ConfigInvalid) { |err| expect(err.message).to include("The following settings shouldn't exist: bix") } end it "validates correct Vagrantfile of all vms" do iso_env.vagrantfile <<-EOH Vagrant.configure("2") do |config| config.vm.box = "hashicorp/precise64" config.vm.define "test" do |vm| vm.vm.provider :virtualbox end config.vm.define "machine" do |vm| vm.vm.provider :virtualbox end end EOH expect(env.ui).to receive(:info).with(any_args) { |message, _| expect(message).to include("Vagrantfile validated successfully.") } expect(subject.execute).to eq(0) end it "validates the configuration of all vms" do iso_env.vagrantfile <<-EOH Vagrant.configure("2") do |config| config.vm.box = "hashicorp/precise64" config.vm.define "test" do |vm| vm.vm.provider :virtualbox end config.vm.define "machine" do |vm| vm.vm.not_provider :virtualbox end end EOH expect { subject.execute }.to raise_error(Vagrant::Errors::ConfigInvalid) { |err| expect(err.message).to include("The following settings shouldn't exist: not_provider") } end it "throws an exception if there's no Vagrantfile" do expect { subject.execute }.to raise_error(Vagrant::Errors::NoEnvironmentError) end end end vagrant-2.0.2/test/unit/plugins/communicators/000077500000000000000000000000001323370221500214555ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/communicators/ssh/000077500000000000000000000000001323370221500222525ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/communicators/ssh/communicator_test.rb000066400000000000000000000434371323370221500263510ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/communicators/ssh/communicator") describe VagrantPlugins::CommunicatorSSH::Communicator do include_context "unit" let(:export_command_template){ 'export %ENV_KEY%="%ENV_VALUE%"' } # SSH configuration information mock let(:ssh) do double("ssh", timeout: 1, host: nil, port: 5986, guest_port: 5986, pty: false, keep_alive: false, insert_key: false, export_command_template: export_command_template, shell: 'bash -l' ) end # Configuration mock let(:config) { double("config", ssh: ssh) } # Provider mock let(:provider) { double("provider") } # UI mock let(:ui) { double("ui") } # Machine mock built with previously defined let(:machine) do double("machine", config: config, provider: provider, ui: ui ) end # Subject instance to test let(:communicator){ @communicator ||= described_class.new(machine) } # Underlying net-ssh connection mock let(:connection) { double("connection") } # Base net-ssh connection channel mock let(:channel) { double("channel") } # net-ssh connection channel mock for running commands let(:command_channel) { double("command_channel") } # Default exit data for commands run let(:exit_data) { double("exit_data", read_long: 0) } # Core shell command used when starting command connection let(:core_shell_cmd) { "bash -l" } # Marker used for flagging start of output let(:command_garbage_marker) { communicator.class.const_get(:CMD_GARBAGE_MARKER) } # Start marker output when PTY is enabled let(:pty_delim_start) { communicator.class.const_get(:PTY_DELIM_START) } # End marker output when PTY is enabled let(:pty_delim_end) { communicator.class.const_get(:PTY_DELIM_END) } # Command output returned on stdout let(:command_stdout_data) { '' } # Command output returned on stderr let(:command_stderr_data) { '' } # Mock for net-ssh scp let(:scp) { double("scp") } # Setup for commands using the net-ssh connection. This can be reused where needed # by providing to `before` connection_setup = proc do allow(connection).to receive(:closed?).and_return false allow(connection).to receive(:open_channel). and_yield(channel).and_return(channel) allow(channel).to receive(:wait).and_return true allow(channel).to receive(:close) allow(command_channel).to receive(:send_data) allow(command_channel).to receive(:eof!) allow(command_channel).to receive(:on_data). and_yield(nil, command_stdout_data) allow(command_channel).to receive(:on_extended_data). and_yield(nil, nil, command_stderr_data) allow(machine).to receive(:ssh_info).and_return(host: '10.1.2.3', port: 22) expect(channel).to receive(:exec).with(core_shell_cmd). and_yield(command_channel, '').and_return channel expect(command_channel).to receive(:on_request).with('exit-status'). and_yield(nil, exit_data) # Return mocked net-ssh connection during setup allow(communicator).to receive(:retryable).and_return(connection) end describe ".wait_for_ready" do before(&connection_setup) context "with no static config (default scenario)" do before do allow(ui).to receive(:detail) end context "when ssh_info requires a multiple tries before it is ready" do before do expect(machine).to receive(:ssh_info). and_return(nil).ordered expect(machine).to receive(:ssh_info). and_return(host: '10.1.2.3', port: 22).ordered end it "retries ssh_info until ready" do # retries are every 0.5 so buffer the timeout just a hair over expect(communicator.wait_for_ready(0.6)).to eq(true) end end end end describe ".ready?" do before(&connection_setup) it "returns true if shell test is successful" do expect(communicator.ready?).to be(true) end context "with an invalid shell test" do before do expect(exit_data).to receive(:read_long).and_return 1 end it "returns raises SSHInvalidShell error" do expect{ communicator.ready? }.to raise_error Vagrant::Errors::SSHInvalidShell end end end describe ".execute" do before(&connection_setup) it "runs valid command and returns successful status code" do expect(command_channel).to receive(:send_data).with(/ls \/\n/) expect(communicator.execute("ls /")).to eq(0) end it "prepends UUID output to command for garbage removal" do expect(command_channel).to receive(:send_data). with("printf '#{command_garbage_marker}'\n(>&2 printf '#{command_garbage_marker}')\nls /\n") expect(communicator.execute("ls /")).to eq(0) end context "with command returning an error" do let(:exit_data) { double("exit_data", read_long: 1) } it "raises error when exit-code is non-zero" do expect(command_channel).to receive(:send_data).with(/ls \/\n/) expect{ communicator.execute("ls /") }.to raise_error(Vagrant::Errors::VagrantError) end it "returns exit-code when exit-code is non-zero and error check is disabled" do expect(command_channel).to receive(:send_data).with(/ls \/\n/) expect(communicator.execute("ls /", error_check: false)).to eq(1) end end context "with garbage content prepended to command output" do let(:command_stdout_data) do "Line of garbage\nMore garbage\n#{command_garbage_marker}bin\ntmp\n" end it "removes any garbage output prepended to command output" do stdout = '' expect( communicator.execute("ls /") do |type, data| if type == :stdout stdout << data end end ).to eq(0) expect(stdout).to eq("bin\ntmp\n") end end context "with no command output" do let(:command_stdout_data) do "#{command_garbage_marker}" end it "does not send empty stdout data string" do empty = true expect( communicator.execute("ls /") do |type, data| if type == :stdout && data.empty? empty = false end end ).to eq(0) expect(empty).to be(true) end end context "with garbage content prepended to command stderr output" do let(:command_stderr_data) do "Line of garbage\nMore garbage\n#{command_garbage_marker}bin\ntmp\n" end it "removes any garbage output prepended to command stderr output" do stderr = '' expect( communicator.execute("ls /") do |type, data| if type == :stderr stderr << data end end ).to eq(0) expect(stderr).to eq("bin\ntmp\n") end end context "with no command output on stderr" do let(:command_stderr_data) do "#{command_garbage_marker}" end it "does not send empty stderr data string" do empty = true expect( communicator.execute("ls /") do |type, data| if type == :stderr && data.empty? empty = false end end ).to eq(0) expect(empty).to be(true) end end context "with pty enabled" do before do expect(ssh).to receive(:pty).and_return true expect(channel).to receive(:request_pty).and_yield(command_channel, true) expect(command_channel).to receive(:send_data). with(/#{Regexp.escape(pty_delim_start)}/) end let(:command_stdout_data) do "#{pty_delim_start}bin\ntmp\n#{pty_delim_end}" end it "requests pty for connection" do expect(communicator.execute("ls")).to eq(0) end context "with sudo enabled" do let(:core_shell_cmd){ 'sudo bash -l' } before do expect(ssh).to receive(:sudo_command).and_return 'sudo %c' end it "wraps command in elevated shell when sudo is true" do expect(communicator.execute("ls", sudo: true)).to eq(0) end end end context "with sudo enabled" do let(:core_shell_cmd){ 'sudo bash -l' } before do expect(ssh).to receive(:sudo_command).and_return 'sudo %c' end it "wraps command in elevated shell when sudo is true" do expect(communicator.execute("ls", sudo: true)).to eq(0) end end end describe ".test" do before(&connection_setup) context "with exit code as zero" do it "returns true" do expect(communicator.test("ls")).to be(true) end end context "with exit code as non-zero" do before do expect(exit_data).to receive(:read_long).and_return 1 end it "returns false" do expect(communicator.test("/bin/false")).to be(false) end end end describe ".upload" do before do 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, '/destination', recursive: true) communicator.upload(dir, '/destination') end end it "uploads a file if local path is a file" do file = Tempfile.new('vagrant-test') begin expect(scp).to receive(:upload!).with(instance_of(File), '/destination/file') communicator.upload(file.path, '/destination/file') ensure file.delete end end it "raises custom error on permission errors" do file = Tempfile.new('vagrant-test') begin expect(scp).to receive(:upload!).with(instance_of(File), '/destination/file'). and_raise("Permission denied") expect{ communicator.upload(file.path, '/destination/file') }.to( raise_error(Vagrant::Errors::SCPPermissionDenied) ) ensure file.delete end end it "does not raise custom error on non-permission errors" do file = Tempfile.new('vagrant-test') begin expect(scp).to receive(:upload!).with(instance_of(File), '/destination/file'). and_raise("Some other error") expect{ communicator.upload(file.path, '/destination/file') }.to raise_error(RuntimeError) ensure file.delete end end end describe ".download" do before do expect(communicator).to receive(:scp_connect).and_yield(scp) end it "calls scp to download file" do expect(scp).to receive(:download!).with('/path/from', '/path/to') communicator.download('/path/from', '/path/to') end end describe ".connect" do it "cannot be called directly" do expect{ communicator.connect }.to raise_error(NoMethodError) end context "with default configuration" do before do expect(machine).to receive(:ssh_info).and_return( host: nil, port: nil, private_key_path: nil, username: nil, password: nil, keys_only: true, verify_host_key: false ) end it "has keys_only enabled" do expect(Net::SSH).to receive(:start).with( nil, nil, hash_including( keys_only: true ) ).and_return(true) communicator.send(:connect) end it "has verify_host_key disabled" do expect(Net::SSH).to receive(:start).with( nil, nil, hash_including( verify_host_key: false ) ).and_return(true) communicator.send(:connect) end it "does not include any private key paths" do expect(Net::SSH).to receive(:start).with( nil, nil, hash_excluding( keys: anything ) ).and_return(true) communicator.send(:connect) end it "includes `none` and `hostbased` auth methods" do expect(Net::SSH).to receive(:start).with( nil, nil, hash_including( auth_methods: ["none", "hostbased"] ) ).and_return(true) communicator.send(:connect) end it "includes the default cipher array for encryption" do cipher_array = %w(aes128-cbc 3des-cbc blowfish-cbc cast128-cbc aes192-cbc aes256-cbc rijndael-cbc@lysator.liu.se idea-cbc arcfour128 arcfour256 arcfour aes128-ctr aes192-ctr aes256-ctr cast128-ctr blowfish-ctr 3des-ctr none) expect(Net::SSH).to receive(:start).with( nil, nil, hash_including( encryption: cipher_array ) ).and_return(true) communicator.send(:connect) end end context "with keys_only disabled and verify_host_key enabled" do before do expect(machine).to receive(:ssh_info).and_return( host: nil, port: nil, private_key_path: nil, username: nil, password: nil, keys_only: false, verify_host_key: true ) end it "has keys_only enabled" do expect(Net::SSH).to receive(:start).with( nil, nil, hash_including( keys_only: false ) ).and_return(true) communicator.send(:connect) end it "has verify_host_key disabled" do expect(Net::SSH).to receive(:start).with( nil, nil, hash_including( verify_host_key: true ) ).and_return(true) communicator.send(:connect) end end context "with host and port configured" do before do expect(machine).to receive(:ssh_info).and_return( host: '127.0.0.1', port: 2222, private_key_path: nil, username: nil, password: nil, keys_only: true, verify_host_key: false ) end it "specifies configured host" do expect(Net::SSH).to receive(:start).with("127.0.0.1", anything, anything) communicator.send(:connect) end it "has port defined" do expect(Net::SSH).to receive(:start).with("127.0.0.1", anything, hash_including(port: 2222)) communicator.send(:connect) end end context "with private_key_path configured" do before do expect(machine).to receive(:ssh_info).and_return( host: '127.0.0.1', port: 2222, private_key_path: ['/priv/key/path'], username: nil, password: nil, keys_only: true, verify_host_key: false ) end it "includes private key paths" do expect(Net::SSH).to receive(:start).with( anything, anything, hash_including( keys: ["/priv/key/path"] ) ).and_return(true) communicator.send(:connect) end it "includes `publickey` auth method" do expect(Net::SSH).to receive(:start).with( anything, anything, hash_including( auth_methods: ["none", "hostbased", "publickey"] ) ).and_return(true) communicator.send(:connect) end end context "with username and password configured" do before do expect(machine).to receive(:ssh_info).and_return( host: '127.0.0.1', port: 2222, private_key_path: nil, username: 'vagrant', password: 'vagrant', keys_only: true, verify_host_key: false ) end it "has username defined" do expect(Net::SSH).to receive(:start).with(anything, 'vagrant', anything).and_return(true) communicator.send(:connect) end it "has password defined" do expect(Net::SSH).to receive(:start).with( anything, anything, hash_including( password: 'vagrant' ) ).and_return(true) communicator.send(:connect) end it "includes `password` auth method" do expect(Net::SSH).to receive(:start).with( anything, anything, hash_including( auth_methods: ["none", "hostbased", "password"] ) ).and_return(true) communicator.send(:connect) end end context "with password and private_key_path configured" do before do expect(machine).to receive(:ssh_info).and_return( host: '127.0.0.1', port: 2222, private_key_path: ['/priv/key/path'], username: 'vagrant', password: 'vagrant', keys_only: true, verify_host_key: false ) end it "has password defined" do expect(Net::SSH).to receive(:start).with( anything, anything, hash_including( password: 'vagrant' ) ).and_return(true) communicator.send(:connect) end it "includes private key paths" do expect(Net::SSH).to receive(:start).with( anything, anything, hash_including( keys: ["/priv/key/path"] ) ).and_return(true) communicator.send(:connect) end it "includes `publickey` and `password` auth methods" do expect(Net::SSH).to receive(:start).with( anything, anything, hash_including( auth_methods: ["none", "hostbased", "publickey", "password"] ) ).and_return(true) communicator.send(:connect) end end end describe ".generate_environment_export" do it "should generate bourne shell compatible export" do expect(communicator.send(:generate_environment_export, "TEST", "value")).to eq("export TEST=\"value\"\n") end context "with custom template defined" do let(:export_command_template){ "setenv %ENV_KEY% %ENV_VALUE%" } it "should generate custom export based on template" do expect(communicator.send(:generate_environment_export, "TEST", "value")).to eq("setenv TEST value\n") end end end end vagrant-2.0.2/test/unit/plugins/communicators/winrm/000077500000000000000000000000001323370221500226115ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/communicators/winrm/command_filter_test.rb000066400000000000000000000062211323370221500271610ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/communicators/winrm/command_filter") describe VagrantPlugins::CommunicatorWinRM::CommandFilter, unit: true do describe '.command_filters' do it 'initializes all command filters in command filters directory' do expect(subject.command_filters()).not_to be_empty end end describe '.filter' do it 'filters out uname commands' do expect(subject.filter('uname -s stuff')).to eq('') end it 'filters out grep commands' do expect(subject.filter("grep 'Fedora release [12][67890]' /etc/redhat-release")).to eq("") end it 'filters out which commands' do expect(subject.filter('which ruby')).to include( '[Array](Get-Command ruby -errorAction SilentlyContinue)') end it 'filters out test -d commands' do expect(subject.filter('test -d /tmp/dir')).to include( "$p = \"/tmp/dir\"") expect(subject.filter('test -d /tmp/dir')).to include( "if ((Test-Path $p) -and (get-item $p).PSIsContainer) {") end it 'filters out test -f commands' do expect(subject.filter('test -f /tmp/file.txt')).to include( "$p = \"/tmp/file.txt\"") expect(subject.filter('test -f /tmp/file.txt')).to include( "if ((Test-Path $p) -and (!(get-item $p).PSIsContainer)) {") end it 'filters out test -x commands' do expect(subject.filter('test -x /tmp/file.txt')).to include( "$p = \"/tmp/file.txt\"") expect(subject.filter('test -x /tmp/file.txt')).to include( "if ((Test-Path $p) -and (!(get-item $p).PSIsContainer)) {") end it 'filters out other test commands' do expect(subject.filter('test -L /tmp/file.txt')).to include( "$p = \"/tmp/file.txt\"") expect(subject.filter('test -L /tmp/file.txt')).to include( "if (Test-Path $p) {") end it 'filters out rm recurse commands' do expect(subject.filter('rm -Rf /some/dir')).to eq( "rm /some/dir -recurse -force") expect(subject.filter('rm -fr /some/dir')).to eq( "rm /some/dir -recurse -force") expect(subject.filter('rm -r /some/dir')).to eq( "rm /some/dir -recurse -force") end it 'filters out rm commands' do expect(subject.filter('rm /some/dir')).to eq( "rm /some/dir -force") expect(subject.filter('rm -f /some/dir')).to eq( "rm /some/dir -force") end it 'filters out mkdir commands' do expect(subject.filter('mkdir /some/dir')).to eq( "mkdir /some/dir -force") expect(subject.filter('mkdir -p /some/dir')).to eq( "mkdir /some/dir -force") end it 'filters out chown commands' do expect(subject.filter("chown -R root '/tmp/dir'")).to eq('') end it 'filters out chmod commands' do expect(subject.filter("chmod 0600 ~/.ssh/authorized_keys")).to eq('') end it 'filters out certain cat commands' do expect(subject.filter("cat /etc/release | grep -i OmniOS")).to eq('') end it 'should not filter out other cat commands' do expect(subject.filter("cat /tmp/somefile")).to eq('cat /tmp/somefile') end end end vagrant-2.0.2/test/unit/plugins/communicators/winrm/communicator_test.rb000066400000000000000000000127071323370221500267040ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/communicators/winrm/communicator") describe VagrantPlugins::CommunicatorWinRM::Communicator do include_context "unit" let(:winrm) { double("winrm", timeout: 1, host: nil, port: 5986, guest_port: 5986) } let(:config) { double("config", winrm: winrm) } let(:provider) { double("provider") } let(:ui) { double("ui") } let(:machine) { double("machine", config: config, provider: provider, ui: ui) } let(:shell) { double("shell") } let(:good_output) { WinRM::Output.new.tap { |out| out.exitcode = 0 } } let(:bad_output) { WinRM::Output.new.tap { |out| out.exitcode = 1 } } subject do described_class.new(machine).tap do |comm| allow(comm).to receive(:create_shell).and_return(shell) end end before do allow(shell).to receive(:username).and_return('vagrant') allow(shell).to receive(:password).and_return('password') allow(shell).to receive(:execution_time_limit).and_return('PT2H') end describe ".wait_for_ready" do context "with no winrm_info capability and no static config (default scenario)" do before do # No default providers support this capability allow(provider).to receive(:capability?).with(:winrm_info).and_return(false) # Get us through the detail prints allow(ui).to receive(:detail) allow(shell).to receive(:host) allow(shell).to receive(:port) allow(shell).to receive(:username) allow(shell).to receive(:config) { double("config", transport: nil)} end context "when ssh_info requires a multiple tries before it is ready" do before do allow(machine).to receive(:ssh_info).and_return(nil, { host: '10.1.2.3', port: '22', }) # Makes ready? return true allow(shell).to receive(:cmd).with("hostname").and_return({ exitcode: 0 }) end it "retries ssh_info until ready" do expect(subject.wait_for_ready(2)).to eq(true) end end end end describe ".ready?" do it "returns true if hostname command executes without error" do expect(shell).to receive(:cmd).with("hostname").and_return({ exitcode: 0 }) expect(subject.ready?).to be(true) end it "returns false if hostname command fails with a transient error" do expect(shell).to receive(:cmd).with("hostname").and_raise(VagrantPlugins::CommunicatorWinRM::Errors::TransientError) expect(subject.ready?).to be(false) end it "raises an error if hostname command fails with an unknown error" do expect(shell).to receive(:cmd).with("hostname").and_raise(Vagrant::Errors::VagrantError) expect { subject.ready? }.to raise_error(Vagrant::Errors::VagrantError) end it "raises timeout error when hostname command takes longer then winrm timeout" do expect(shell).to receive(:cmd).with("hostname") do sleep 2 # winrm.timeout = 1 end expect { subject.ready? }.to raise_error(Timeout::Error) end end describe ".execute" do it "defaults to running in powershell" do expect(shell).to receive(:powershell).with(kind_of(String), kind_of(Hash)).and_return(good_output) expect(subject.execute("dir")).to eq(0) end it "use elevated shell script when elevated is true" do expect(shell).to receive(:elevated).and_return(good_output) expect(subject.execute("dir", { elevated: true })).to eq(0) end it "can use cmd shell" do expect(shell).to receive(:cmd).with(kind_of(String), kind_of(Hash)).and_return(good_output) expect(subject.execute("dir", { shell: :cmd })).to eq(0) end it "raises error when error_check is true and exit code is non-zero" do expect(shell).to receive(:powershell).with(kind_of(String), kind_of(Hash)).and_return(bad_output) expect { subject.execute("dir") }.to raise_error( VagrantPlugins::CommunicatorWinRM::Errors::WinRMBadExitStatus) end it "does not raise error when error_check is false and exit code is non-zero" do expect(shell).to receive(:powershell).with(kind_of(String), kind_of(Hash)).and_return(bad_output) expect(subject.execute("dir", { error_check: false })).to eq(1) end end describe ".test" do it "returns true when exit code is zero" do expect(shell).to receive(:powershell).with(kind_of(String)).and_return(good_output) expect(subject.test("test -d c:/windows")).to be(true) end it "returns false when exit code is non-zero" do expect(shell).to receive(:powershell).with(kind_of(String)).and_return(bad_output) expect(subject.test("test -d /tmp/foobar")).to be(false) end it "returns false when stderr contains output" do output = WinRM::Output.new output.exitcode = 1 output << { stderr: 'this is an error' } expect(shell).to receive(:powershell).with(kind_of(String)).and_return(output) expect(subject.test("[-x stuff] && foo")).to be(false) end it "returns false when command is testing for linux OS" do expect(subject.test("uname -s | grep Debian")).to be(false) end end describe ".upload" do it "calls upload on shell" do expect(shell).to receive(:upload).with("from", "to") subject.upload("from", "to") end end describe ".download" do it "calls download on shell" do expect(shell).to receive(:download).with("from", "to") subject.download("from", "to") end end end vagrant-2.0.2/test/unit/plugins/communicators/winrm/config_test.rb000066400000000000000000000006151323370221500254440ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/communicators/winrm/config") describe VagrantPlugins::CommunicatorWinRM::Config do let(:machine) { double("machine") } subject { described_class.new } it "is valid by default" do subject.finalize! result = subject.validate(machine) expect(result["WinRM"]).to be_empty end end vagrant-2.0.2/test/unit/plugins/communicators/winrm/helper_test.rb000066400000000000000000000142331323370221500254570ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/communicators/winrm/helper") describe VagrantPlugins::CommunicatorWinRM::Helper do include_context "unit" let(:iso_env) do # We have to create a Vagrantfile so there is a root path test_iso_env.vagrantfile("") test_iso_env.create_vagrant_env end let(:test_iso_env) { isolated_environment } let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } subject { described_class } describe ".winrm_address" do before do machine.config.winrm.host = nil end it "returns the configured host if set" do machine.config.winrm.host = "foo" expect(subject.winrm_address(machine)).to eq("foo") end it "returns the SSH info host if available" do allow(machine).to receive(:ssh_info).and_return({ host: "bar" }) expect(subject.winrm_address(machine)).to eq("bar") end it "raise an exception if it can't detect a host" do allow(machine).to receive(:ssh_info).and_return(nil) expect { subject.winrm_address(machine) }. to raise_error(VagrantPlugins::CommunicatorWinRM::Errors::WinRMNotReady) end it "raise an exception if it detects an empty host ip" do allow(machine).to receive(:ssh_info).and_return({ host: "" }) expect { subject.winrm_address(machine) }. to raise_error(VagrantPlugins::CommunicatorWinRM::Errors::WinRMNotReady) end it "raise a WinRMNotReady exception if it detects an unset host ip" do allow(machine).to receive(:ssh_info).and_return({ host: nil }) expect { subject.winrm_address(machine) }. to raise_error(VagrantPlugins::CommunicatorWinRM::Errors::WinRMNotReady) end it "raise an exception if it detects an APIPA" do allow(machine).to receive(:ssh_info).and_return({ host: "169.254.123.123" }) expect { subject.winrm_address(machine) }. to raise_error(VagrantPlugins::CommunicatorWinRM::Errors::WinRMNotReady) end end describe ".winrm_info" do before do allow(machine.provider).to receive(:capability?) .with(:winrm_info).and_return(false) allow(subject).to receive(:winrm_address).and_return(nil) allow(subject).to receive(:winrm_port).and_return(nil) end it "returns default info if no capability" do allow(subject).to receive(:winrm_address).and_return("bar") allow(subject).to receive(:winrm_port).and_return(45) result = subject.winrm_info(machine) expect(result[:host]).to eq("bar") expect(result[:port]).to eq(45) end it "raises an exception if capability returns nil" do allow(machine.provider).to receive(:capability?) .with(:winrm_info).and_return(true) allow(machine.provider).to receive(:capability) .with(:winrm_info).and_return(nil) expect { subject.winrm_info(machine) }. to raise_error(VagrantPlugins::CommunicatorWinRM::Errors::WinRMNotReady) end it "returns the proper information if set" do allow(machine.provider).to receive(:capability?) .with(:winrm_info).and_return(true) allow(machine.provider).to receive(:capability).with(:winrm_info).and_return({ host: "foo", port: 12, }) result = subject.winrm_info(machine) expect(result[:host]).to eq("foo") expect(result[:port]).to eq(12) end it "defaults information if capability doesn't set it" do allow(machine.provider).to receive(:capability?) .with(:winrm_info).and_return(true) allow(machine.provider).to receive(:capability).with(:winrm_info).and_return({}) allow(subject).to receive(:winrm_address).and_return("bar") allow(subject).to receive(:winrm_port).and_return(45) result = subject.winrm_info(machine) expect(result[:host]).to eq("bar") expect(result[:port]).to eq(45) end end describe ".winrm_port" do it "returns the configured port if no guest port set" do machine.config.winrm.port = 22 machine.config.winrm.guest_port = nil expect(subject.winrm_port(machine)).to eq(22) end it "returns the configured guest port if non local" do machine.config.winrm.port = 22 machine.config.winrm.guest_port = 2222 machine.config.vm.network "forwarded_port", host: 1234, guest: 2222 expect(subject.winrm_port(machine, false)).to eq(2222) end it "returns a forwarded port that matches the guest port" do machine.config.winrm.port = 22 machine.config.winrm.guest_port = 2222 machine.config.vm.network "forwarded_port", host: 1234, guest: 2222 expect(subject.winrm_port(machine)).to eq(1234) end it "uses the provider capability if it exists" do machine.config.winrm.port = 22 machine.config.winrm.guest_port = 2222 machine.config.vm.network "forwarded_port", host: 1234, guest: 2222 allow(machine.provider).to receive(:capability?).with(:forwarded_ports).and_return(true) allow(machine.provider).to receive(:capability).with(:forwarded_ports).and_return({ 1234 => 4567, 2456 => 2222, }) expect(subject.winrm_port(machine)).to eq(2456) end end describe ".winrm_info_invalid?" do it "returns true if it can't detect a host" do allow(machine).to receive(:ssh_info).and_return(nil) expect(subject).to be_winrm_info_invalid(machine.ssh_info) end it "returns true if it detects an empty host ip" do allow(machine).to receive(:ssh_info).and_return({ host: "" }) expect(subject).to be_winrm_info_invalid(machine.ssh_info) end it "returns true if it detects an unset host ip" do allow(machine).to receive(:ssh_info).and_return({ host: nil }) expect(subject).to be_winrm_info_invalid(machine.ssh_info) end it "returns true if it detects an APIPA" do allow(machine).to receive(:ssh_info).and_return({ host: "169.254.123.123" }) expect(subject).to be_winrm_info_invalid(machine.ssh_info) end it "returns false if the IP is valid" do allow(machine).to receive(:ssh_info).and_return({ host: "192.168.123.123" }) expect(subject).not_to be_winrm_info_invalid(machine.ssh_info) end end end vagrant-2.0.2/test/unit/plugins/communicators/winrm/shell_test.rb000066400000000000000000000142641323370221500253130ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/communicators/winrm/shell") require Vagrant.source_root.join("plugins/communicators/winrm/config") describe VagrantPlugins::CommunicatorWinRM::WinRMShell do include_context "unit" let(:connection) { double("winrm_connection") } let(:shell) { double("winrm_shell") } let(:port) { config.transport == :ssl ? 5986 : 5985 } let(:config) { VagrantPlugins::CommunicatorWinRM::Config.new.tap do |c| c.username = 'username' c.password = 'password' c.max_tries = 3 c.retry_delay = 0 c.basic_auth_only = false c.retry_delay = 1 c.max_tries = 2 c.finalize! end } let(:output) { WinRM::Output.new.tap { |out| out.exitcode = 0 } } before { allow(connection).to receive(:shell).and_yield(shell) } subject do described_class.new('localhost', port, config).tap do |comm| allow(comm).to receive(:new_connection).and_return(connection) end end describe "#upload" do let(:fm) { double("file_manager") } 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 expect(subject.upload(from, to)).to eq(size*from.size) end it "should call file_manager.upload once for a single path" do from = "/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(1).times expect(subject.upload(from, to)).to eq(size) end end describe ".powershell" do it "should call winrm powershell" do expect(shell).to receive(:run).with("dir").and_return(output) expect(subject.powershell("dir").exitcode).to eq(0) end it "should raise an execution error when an exception occurs" do expect(shell).to receive(:run).with("dir").and_raise( StandardError.new("Oh no! a 500 SOAP error!")) expect { subject.powershell("dir") }.to raise_error( VagrantPlugins::CommunicatorWinRM::Errors::ExecutionError) end end describe ".elevated" do it "should call winrm elevated" do expect(shell).to receive(:run).with("dir").and_return(output) expect(shell).to receive(:interactive_logon=).with(false) expect(subject.elevated("dir").exitcode).to eq(0) end it "should set interactive_logon when interactive is true" do expect(shell).to receive(:run).with("dir").and_return(output) expect(shell).to receive(:interactive_logon=).with(true) expect(subject.elevated("dir", { interactive: true }).exitcode).to eq(0) end it "should raise an execution error when an exception occurs" do expect(shell).to receive(:run).with("dir").and_raise( StandardError.new("Oh no! a 500 SOAP error!")) expect { subject.powershell("dir") }.to raise_error( VagrantPlugins::CommunicatorWinRM::Errors::ExecutionError) end end describe ".cmd" do it "should call winrm cmd" do expect(connection).to receive(:shell).with(:cmd, { }) expect(shell).to receive(:run).with("dir").and_return(output) expect(subject.cmd("dir").exitcode).to eq(0) end context "when codepage is given" do let(:config) { VagrantPlugins::CommunicatorWinRM::Config.new.tap do |c| c.codepage = 800 c.finalize! end } it "creates shell with the given codepage when set" do expect(connection).to receive(:shell).with(:cmd, { codepage: 800 }) expect(shell).to receive(:run).with("dir").and_return(output) expect(subject.cmd("dir").exitcode).to eq(0) end end end describe ".wql" do it "should call winrm wql" do expect(connection).to receive(:run_wql).with("select * from Win32_OperatingSystem") subject.wql("select * from Win32_OperatingSystem") end it "should retry when a WinRMAuthorizationError is received" do expect(connection).to receive(:run_wql).with("select * from Win32_OperatingSystem").exactly(2).times.and_raise( # Note: The initialize for WinRMAuthorizationError may require a status_code as # the second argument in a future WinRM release. Currently it doesn't track the # status code. WinRM::WinRMAuthorizationError.new("Oh no!! Unauthrorized") ) expect { subject.wql("select * from Win32_OperatingSystem") }.to raise_error( VagrantPlugins::CommunicatorWinRM::Errors::AuthenticationFailed) end end describe ".endpoint" do context 'when transport is :ssl' do let(:config) { VagrantPlugins::CommunicatorWinRM::Config.new.tap do |c| c.transport = :ssl c.finalize! end } it "should create winrm endpoint address using https" do expect(subject.send(:endpoint)).to eq("https://localhost:5986/wsman") end end context "when transport is :negotiate" do it "should create winrm endpoint address using http" do expect(subject.send(:endpoint)).to eq("http://localhost:5985/wsman") end end context "when transport is :plaintext" do let(:config) { VagrantPlugins::CommunicatorWinRM::Config.new.tap do |c| c.transport = :plaintext c.finalize! end } it "should create winrm endpoint address using http" do expect(subject.send(:endpoint)).to eq("http://localhost:5985/wsman") end end end describe ".endpoint_options" do it "should create endpoint options" do expect(subject.send(:endpoint_options)).to eq( { endpoint: "http://localhost:5985/wsman", operation_timeout: 1800, user: "username", password: "password", host: "localhost", port: 5985, basic_auth_only: false, no_ssl_peer_verification: false, retry_delay: 1, retry_limit: 2, transport: :negotiate }) end end end vagrant-2.0.2/test/unit/plugins/communicators/winssh/000077500000000000000000000000001323370221500227705ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/communicators/winssh/communicator_test.rb000066400000000000000000000375451323370221500270720ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/communicators/winssh/communicator") describe VagrantPlugins::CommunicatorWinSSH::Communicator do include_context "unit" let(:export_command_template){ 'export %ENV_KEY%="%ENV_VALUE%"' } let(:ssh) do double("ssh", timeout: 1, host: nil, port: 5986, guest_port: 5986, keep_alive: false ) end # SSH configuration information mock let(:winssh) do double("winssh", insert_key: false, export_command_template: export_command_template, shell: 'cmd', upload_directory: "C:\\Windows\\Temp" ) end # Configuration mock let(:config) { double("config", winssh: winssh, ssh: ssh) } # Provider mock let(:provider) { double("provider") } # UI mock let(:ui) { double("ui") } # Machine mock built with previously defined let(:machine) do double("machine", config: config, provider: provider, ui: ui ) end # Subject instance to test let(:communicator){ @communicator ||= described_class.new(machine) } # Underlying net-ssh connection mock let(:connection) { double("connection") } # Base net-ssh connection channel mock let(:channel) { double("channel") } # net-ssh connection channel mock for running commands let(:command_channel) { double("command_channel") } # Default exit data for commands run let(:exit_data) { double("exit_data", read_long: 0) } # Marker used for flagging start of output let(:command_garbage_marker) { communicator.class.const_get(:CMD_GARBAGE_MARKER) } # Start marker output when PTY is enabled let(:pty_delim_start) { communicator.class.const_get(:PTY_DELIM_START) } # End marker output when PTY is enabled let(:pty_delim_end) { communicator.class.const_get(:PTY_DELIM_END) } # Command output returned on stdout let(:command_stdout_data) { '' } # Command output returned on stderr let(:command_stderr_data) { '' } # Mock for net-ssh scp let(:scp) { double("scp") } # Stub file to match commands let(:ssh_cmd_file){ double("ssh_cmd_file", path: "/dev/null/path") } # Setup for commands using the net-ssh connection. This can be reused where needed # by providing to `before` connection_setup = proc do allow(connection).to receive(:logger) allow(connection).to receive(:closed?).and_return false allow(connection).to receive(:open_channel). and_yield(channel).and_return(channel) allow(channel).to receive(:wait).and_return true allow(channel).to receive(:close) allow(command_channel).to receive(:send_data) allow(command_channel).to receive(:eof!) allow(command_channel).to receive(:on_data). and_yield(nil, command_stdout_data) allow(command_channel).to receive(:on_extended_data). and_yield(nil, nil, command_stderr_data) allow(machine).to receive(:ssh_info).and_return(host: '10.1.2.3', port: 22) allow(channel).to receive(:[]=).with(any_args).and_return(true) allow(channel).to receive(:on_close) allow(channel).to receive(:on_data) allow(channel).to receive(:on_extended_data) allow(channel).to receive(:on_request) allow(channel).to receive(:on_process) allow(channel).to receive(:exec).with(anything). and_yield(command_channel, '').and_return channel expect(command_channel).to receive(:on_request).with('exit-status'). and_yield(nil, exit_data) # Return mocked net-ssh connection during setup allow(communicator).to receive(:retryable).and_return(connection) allow(Tempfile).to receive(:new).with(/vagrant-ssh/).and_return(ssh_cmd_file) allow(ssh_cmd_file).to receive(:puts) allow(ssh_cmd_file).to receive(:close) allow(ssh_cmd_file).to receive(:delete) allow(scp).to receive(:upload!) allow(communicator).to receive(:scp_connect).and_return(true) end describe ".wait_for_ready" do before(&connection_setup) context "with no static config (default scenario)" do before do allow(ui).to receive(:detail) end context "when ssh_info requires a multiple tries before it is ready" do before do expect(machine).to receive(:ssh_info). and_return(nil).ordered expect(machine).to receive(:ssh_info). and_return(host: '10.1.2.3', port: 22).ordered end it "retries ssh_info until ready" do # retries are every 0.5 so buffer the timeout just a hair over expect(communicator.wait_for_ready(0.6)).to eq(true) end end end end describe ".ready?" do before(&connection_setup) it "returns true if shell test is successful" do expect(communicator.ready?).to be_truthy end context "with an invalid shell test" do before do expect(exit_data).to receive(:read_long).and_return 1 end it "returns raises SSHInvalidShell error" do expect{ communicator.ready? }.to raise_error Vagrant::Errors::SSHInvalidShell end end end describe ".execute" do before(&connection_setup) it "runs valid command and returns successful status code" do expect(ssh_cmd_file).to receive(:puts).with(/dir/) expect(communicator.execute("dir")).to eq(0) end it "prepends UUID output to command for garbage removal" do expect(ssh_cmd_file).to receive(:puts). with(/ECHO OFF\nECHO #{command_garbage_marker}\nECHO #{command_garbage_marker}.*/) expect(communicator.execute("dir")).to eq(0) end context "with command returning an error" do let(:exit_data) { double("exit_data", read_long: 1) } it "raises error when exit-code is non-zero" do expect(ssh_cmd_file).to receive(:puts).with(/dir/) expect{ communicator.execute("dir") }.to raise_error(Vagrant::Errors::VagrantError) end it "returns exit-code when exit-code is non-zero and error check is disabled" do expect(ssh_cmd_file).to receive(:puts).with(/dir/) expect(communicator.execute("dir", error_check: false)).to eq(1) end end context "with garbage content prepended to command output" do let(:command_stdout_data) do "Line of garbage\nMore garbage\n#{command_garbage_marker}Dir1\nDir2\n" end it "removes any garbage output prepended to command output" do stdout = '' expect( communicator.execute("dir") do |type, data| if type == :stdout stdout << data end end ).to eq(0) expect(stdout).to eq("Dir1\nDir2\n") end end context "with garbage content prepended to command stderr output" do let(:command_stderr_data) do "Line of garbage\nMore garbage\n#{command_garbage_marker}Dir1\nDir2\n" end it "removes any garbage output prepended to command stderr output" do stderr = '' expect( communicator.execute("dir") do |type, data| if type == :stderr stderr << data end end ).to eq(0) expect(stderr).to eq("Dir1\nDir2\n") end end end describe ".test" do before(&connection_setup) context "with exit code as zero" do it "returns true" do expect(communicator.test("dir")).to be_truthy end end context "with exit code as non-zero" do before do expect(exit_data).to receive(:read_long).and_return 1 end it "returns false" do expect(communicator.test("false.exe")).to be_falsey end end end describe ".upload" do before do 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) communicator.upload(dir, 'C:\destination') end end it "uploads a file if local path is a file" do file = Tempfile.new('vagrant-test') begin expect(scp).to receive(:upload!).with(instance_of(File), 'C:\destination\file') communicator.upload(file.path, 'C:\destination\file') ensure file.delete end end it "raises custom error on permission errors" do file = Tempfile.new('vagrant-test') begin expect(scp).to receive(:upload!).with(instance_of(File), 'C:\destination\file'). and_raise("Permission denied") expect{ communicator.upload(file.path, 'C:\destination\file') }.to( raise_error(Vagrant::Errors::SCPPermissionDenied) ) ensure file.delete end end it "does not raise custom error on non-permission errors" do file = Tempfile.new('vagrant-test') begin expect(scp).to receive(:upload!).with(instance_of(File), 'C:\destination\file'). and_raise("Some other error") expect{ communicator.upload(file.path, 'C:\destination\file') }.to raise_error(RuntimeError) ensure file.delete end end end describe ".download" do before do expect(communicator).to receive(:scp_connect).and_yield(scp) end it "calls scp to download file" do expect(scp).to receive(:download!).with('/path/from', 'C:\path\to') communicator.download('/path/from', 'C:\path\to') end end describe ".connect" do it "cannot be called directly" do expect{ communicator.connect }.to raise_error(NoMethodError) end context "with default configuration" do before do expect(machine).to receive(:ssh_info).and_return( host: nil, port: nil, private_key_path: nil, username: nil, password: nil, keys_only: true, verify_host_key: false ) end it "has keys_only enabled" do expect(Net::SSH).to receive(:start).with( nil, nil, hash_including( keys_only: true ) ).and_return(true) communicator.send(:connect) end it "has verify_host_key disabled" do expect(Net::SSH).to receive(:start).with( nil, nil, hash_including( verify_host_key: false ) ).and_return(true) communicator.send(:connect) end it "does not include any private key paths" do expect(Net::SSH).to receive(:start).with( nil, nil, hash_excluding( keys: anything ) ).and_return(true) communicator.send(:connect) end it "includes `none` and `hostbased` auth methods" do expect(Net::SSH).to receive(:start).with( nil, nil, hash_including( auth_methods: ["none", "hostbased"] ) ).and_return(true) communicator.send(:connect) end end context "with keys_only disabled and verify_host_key enabled" do before do expect(machine).to receive(:ssh_info).and_return( host: nil, port: nil, private_key_path: nil, username: nil, password: nil, keys_only: false, verify_host_key: true ) end it "has keys_only enabled" do expect(Net::SSH).to receive(:start).with( nil, nil, hash_including( keys_only: false ) ).and_return(true) communicator.send(:connect) end it "has verify_host_key disabled" do expect(Net::SSH).to receive(:start).with( nil, nil, hash_including( verify_host_key: true ) ).and_return(true) communicator.send(:connect) end end context "with host and port configured" do before do expect(machine).to receive(:ssh_info).and_return( host: '127.0.0.1', port: 2222, private_key_path: nil, username: nil, password: nil, keys_only: true, verify_host_key: false ) end it "specifies configured host" do expect(Net::SSH).to receive(:start).with("127.0.0.1", anything, anything) communicator.send(:connect) end it "has port defined" do expect(Net::SSH).to receive(:start).with("127.0.0.1", anything, hash_including(port: 2222)) communicator.send(:connect) end end context "with private_key_path configured" do before do expect(machine).to receive(:ssh_info).and_return( host: '127.0.0.1', port: 2222, private_key_path: ['/priv/key/path'], username: nil, password: nil, keys_only: true, verify_host_key: false ) end it "includes private key paths" do expect(Net::SSH).to receive(:start).with( anything, anything, hash_including( keys: ["/priv/key/path"] ) ).and_return(true) communicator.send(:connect) end it "includes `publickey` auth method" do expect(Net::SSH).to receive(:start).with( anything, anything, hash_including( auth_methods: ["none", "hostbased", "publickey"] ) ).and_return(true) communicator.send(:connect) end end context "with username and password configured" do before do expect(machine).to receive(:ssh_info).and_return( host: '127.0.0.1', port: 2222, private_key_path: nil, username: 'vagrant', password: 'vagrant', keys_only: true, verify_host_key: false ) end it "has username defined" do expect(Net::SSH).to receive(:start).with(anything, 'vagrant', anything).and_return(true) communicator.send(:connect) end it "has password defined" do expect(Net::SSH).to receive(:start).with( anything, anything, hash_including( password: 'vagrant' ) ).and_return(true) communicator.send(:connect) end it "includes `password` auth method" do expect(Net::SSH).to receive(:start).with( anything, anything, hash_including( auth_methods: ["none", "hostbased", "password"] ) ).and_return(true) communicator.send(:connect) end end context "with password and private_key_path configured" do before do expect(machine).to receive(:ssh_info).and_return( host: '127.0.0.1', port: 2222, private_key_path: ['/priv/key/path'], username: 'vagrant', password: 'vagrant', keys_only: true, verify_host_key: false ) end it "has password defined" do expect(Net::SSH).to receive(:start).with( anything, anything, hash_including( password: 'vagrant' ) ).and_return(true) communicator.send(:connect) end it "includes private key paths" do expect(Net::SSH).to receive(:start).with( anything, anything, hash_including( keys: ["/priv/key/path"] ) ).and_return(true) communicator.send(:connect) end it "includes `publickey` and `password` auth methods" do expect(Net::SSH).to receive(:start).with( anything, anything, hash_including( auth_methods: ["none", "hostbased", "publickey", "password"] ) ).and_return(true) communicator.send(:connect) end end end describe ".generate_environment_export" do it "should generate bourne shell compatible export" do expect(communicator.send(:generate_environment_export, "TEST", "value")).to eq("export TEST=\"value\"\n") end context "with custom template defined" do let(:export_command_template){ "setenv %ENV_KEY% %ENV_VALUE%" } it "should generate custom export based on template" do expect(communicator.send(:generate_environment_export, "TEST", "value")).to eq("setenv TEST value\n") end end end end vagrant-2.0.2/test/unit/plugins/guests/000077500000000000000000000000001323370221500201045ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/alt/000077500000000000000000000000001323370221500206645ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/alt/cap/000077500000000000000000000000001323370221500214275ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/alt/cap/change_host_name_test.rb000066400000000000000000000024471323370221500263040ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestALT::Cap::ChangeHostName" do let(:caps) do VagrantPlugins::GuestALT::Plugin .components .guest_capabilities[:alt] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".change_host_name" do let(:cap) { caps.get(:change_host_name) } let(:name) { "banana-rama.example.com" } it "sets the hostname" 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/) end it "does not change the hostname if already set" do comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 0) cap.change_host_name(machine, name) expect(comm.received_commands.size).to eq(1) end end end vagrant-2.0.2/test/unit/plugins/guests/alt/cap/configure_networks_test.rb000066400000000000000000000206311323370221500267320ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestALT::Cap::ConfigureNetworks" do let(:caps) do VagrantPlugins::GuestALT::Plugin .components .guest_capabilities[:alt] end let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } let(:config) { double("config", vm: vm) } let(:guest) { double("guest") } let(:machine) { double("machine", guest: guest, config: config) } let(:networks){ [[{}], [{}]] } let(:vm){ double("vm", networks: networks) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".configure_networks" do let(:cap) { caps.get(:configure_networks) } before do allow(guest).to receive(:capability) .with(:flavor) .and_return(:alt) allow(guest).to receive(:capability) .with(:network_scripts_dir) .and_return("/etc/net") allow(guest).to receive(:capability) .with(:network_interfaces) .and_return(["eth1", "eth2"]) end let(:network_1) do { interface: 0, type: "dhcp", } end let(:network_2) do { interface: 1, type: "static", ip: "33.33.33.10", netmask: "255.255.0.0", gateway: "33.33.0.1", } end context "with NetworkManager installed" do before do allow(cap).to receive(:nmcli?).and_return true end context "with devices managed by NetworkManager" do before do allow(cap).to receive(:nm_controlled?).and_return true end context "with nm_controlled option omitted" do it "downs networks via nmcli, creates ifaces and restart NetworksManager" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/nmcli.*disconnect/) expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/) expect(comm.received_commands[0]).to match(/NetworkManager/) expect(comm.received_commands[0]).to_not match(/ifdown|ifup/) end end context "with nm_controlled option set to true" do let(:networks){ [[{nm_controlled: true}], [{nm_controlled: true}]] } it "downs networks via nmcli, creates ifaces and restart NetworksManager" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/nmcli.*disconnect/) expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/) expect(comm.received_commands[0]).to match(/NetworkManager/) expect(comm.received_commands[0]).to_not match(/(ifdown|ifup)/) end end context "with nm_controlled option set to false" do let(:networks){ [[{nm_controlled: false}], [{nm_controlled: false}]] } it "downs networks manually, creates ifaces, starts networks manually and restart NetworksManager" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/ifdown/) expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/) expect(comm.received_commands[0]).to match(/ifup/) expect(comm.received_commands[0]).to match(/NetworkManager/) expect(comm.received_commands[0]).to_not match(/nmcli/) end end context "with nm_controlled option set to false on first device" do let(:networks){ [[{nm_controlled: false}], [{nm_controlled: true}]] } it "downs networks, creates ifaces and starts the networks with one managed manually and one NetworkManager controlled" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/ifdown/) expect(comm.received_commands[0]).to match(/nmcli.*disconnect/) expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/) expect(comm.received_commands[0]).to match(/ifup/) expect(comm.received_commands[0]).to match(/NetworkManager/) end end end context "with devices not managed by NetworkManager" do before do allow(cap).to receive(:nm_controlled?).and_return false end context "with nm_controlled option omitted" do it "creates and starts the networks manually" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/ifdown/) expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/) expect(comm.received_commands[0]).to match(/ifup/) expect(comm.received_commands[0]).to match(/NetworkManager/) expect(comm.received_commands[0]).to_not match(/nmcli/) end end context "with nm_controlled option set to true" do let(:networks){ [[{nm_controlled: true}], [{nm_controlled: true}]] } it "creates and starts the networks via nmcli" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/ifdown/) expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/) expect(comm.received_commands[0]).to match(/NetworkManager/) expect(comm.received_commands[0]).to_not match(/ifup/) end end context "with nm_controlled option set to false" do let(:networks){ [[{nm_controlled: false}], [{nm_controlled: false}]] } it "creates and starts the networks via ifup " do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/ifdown/) expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/) expect(comm.received_commands[0]).to match(/ifup/) expect(comm.received_commands[0]).to match(/NetworkManager/) expect(comm.received_commands[0]).to_not match(/nmcli/) end end context "with nm_controlled option set to false on first device" do let(:networks){ [[{nm_controlled: false}], [{nm_controlled: true}]] } it "creates and starts the networks with one managed manually and one NetworkManager controlled" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/ifdown/) expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/) expect(comm.received_commands[0]).to match(/ifup/) expect(comm.received_commands[0]).to match(/NetworkManager/) expect(comm.received_commands[0]).to_not match(/nmcli.*disconnect/) end end end end context "without NetworkManager installed" do before do allow(cap).to receive(:nmcli?).and_return false end context "with nm_controlled option omitted" do it "creates and starts the networks manually" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/ifdown/) expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/) expect(comm.received_commands[0]).to match(/ifup/) expect(comm.received_commands[0]).to_not match(/nmcli/) expect(comm.received_commands[0]).to_not match(/NetworkManager/) end end context "with nm_controlled option omitted" do let(:networks){ [[{nm_controlled: false}], [{nm_controlled: false}]] } it "creates and starts the networks manually" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/ifdown/) expect(comm.received_commands[0]).to match(/mkdir.*\/etc\/net\/ifaces/) expect(comm.received_commands[0]).to match(/ifup/) expect(comm.received_commands[0]).to_not match(/nmcli/) expect(comm.received_commands[0]).to_not match(/NetworkManager/) end end context "with nm_controlled option set" do let(:networks){ [[{nm_controlled: false}], [{nm_controlled: true}]] } it "raises an error" do expect{ cap.configure_networks(machine, [network_1, network_2]) }.to raise_error(Vagrant::Errors::NetworkManagerNotInstalled) end end end end end vagrant-2.0.2/test/unit/plugins/guests/alt/cap/flavor_test.rb000066400000000000000000000046701323370221500243130ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestALT::Cap::Flavor" do let(:caps) do VagrantPlugins::GuestALT::Plugin .components .guest_capabilities[:alt] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".flavor" do let(:cap) { caps.get(:flavor) } context "without /etc/os-release file" do { "ALT 8.1 Server" => :alt_8, "ALT Education 8.1" => :alt_8, "ALT Workstation 8.1" => :alt_8, "ALT Workstation K 8.1 (Centaurea Ruthenica)" => :alt_8, "ALT Linux p8 (Hypericum)" => :alt_8, "ALT Sisyphus (unstable) (sisyphus)" => :alt, "ALT Linux Sisyphus (unstable)" => :alt, "ALT Linux 6.0.1 Spt (separator)" => :alt, "ALT Linux 7.0.5 School Master" => :alt, "ALT starter kit (Hypericum)" => :alt, "ALT" => :alt, "Simply" => :alt, }.each do |str, expected| it "returns #{expected} for #{str} in /etc/altlinux-release" do comm.stub_command("test -f /etc/os-release", exit_code: 1) comm.stub_command("cat /etc/altlinux-release", stdout: str) expect(cap.flavor(machine)).to be(expected) end end end context "with /etc/os-release file" do { [ "NAME=\"Sisyphus\"", "VERSION_ID=20161130" ] => :alt, [ "NAME=\"ALT Education\"", "VERSION_ID=8.1" ] => :alt_8, [ "NAME=\"ALT Server\"", "VERSION_ID=8.1" ] => :alt_8, [ "NAME=\"ALT SPServer\"", "VERSION_ID=8.0" ] => :alt_8, [ "NAME=\"starter kit\"", "VERSION_ID=p8" ] => :alt_8, [ "NAME=\"ALT Linux\"", "VERSION_ID=8.0.0" ] => :alt_8, [ "NAME=\"Simply Linux\"", "VERSION_ID=7.95.0" ] => :alt_8, [ "NAME=\"ALT Linux\"", "VERSION_ID=7.0.5" ] => :alt_7, [ "NAME=\"School Junior\"", "VERSION_ID=7.0.5" ] => :alt_7, }.each do |strs, expected| it "returns #{expected} for #{strs[0]} and #{strs[1]} in /etc/os-release" do comm.stub_command("test -f /etc/os-release", exit_code: 0) comm.stub_command("grep NAME /etc/os-release", stdout: strs[0]) comm.stub_command("grep VERSION_ID /etc/os-release", stdout: strs[1]) expect(cap.flavor(machine)).to be(expected) end end end end end vagrant-2.0.2/test/unit/plugins/guests/alt/cap/network_scripts_dir_test.rb000066400000000000000000000007511323370221500271140ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestALT::Cap::NetworkScriptsDir" do let(:caps) do VagrantPlugins::GuestALT::Plugin .components .guest_capabilities[:alt] end let(:machine) { double("machine") } describe ".network_scripts_dir" do let(:cap) { caps.get(:network_scripts_dir) } let(:name) { "banana-rama.example.com" } it "is /etc/net" do expect(cap.network_scripts_dir(machine)).to eq("/etc/net") end end end vagrant-2.0.2/test/unit/plugins/guests/alt/cap/rsync_test.rb000066400000000000000000000012231323370221500241470ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestALT::Cap:RSync" do let(:caps) do VagrantPlugins::GuestALT::Plugin .components .guest_capabilities[:alt] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".rsync_install" do let(:cap) { caps.get(:rsync_install) } it "installs rsync" do cap.rsync_install(machine) expect(comm.received_commands[0]).to match(/install rsync/) end end end vagrant-2.0.2/test/unit/plugins/guests/amazon/000077500000000000000000000000001323370221500213715ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/amazon/cap/000077500000000000000000000000001323370221500221345ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/amazon/cap/flavor_test.rb000066400000000000000000000006161323370221500250140ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestAmazon::Cap::Flavor" do let(:caps) do VagrantPlugins::GuestAmazon::Plugin .components .guest_capabilities[:amazon] end let(:machine) { double("machine") } describe ".flavor" do let(:cap) { caps.get(:flavor) } it "returns rhel" do expect(cap.flavor(machine)).to be(:rhel) end end end vagrant-2.0.2/test/unit/plugins/guests/arch/000077500000000000000000000000001323370221500210215ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/arch/cap/000077500000000000000000000000001323370221500215645ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/arch/cap/change_host_name_test.rb000066400000000000000000000020721323370221500264330ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestArch::Cap::ChangeHostName" do let(:described_class) do VagrantPlugins::GuestArch::Plugin .components .guest_capabilities[:arch] .get(:change_host_name) end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".change_host_name" do let(:name) { "banana-rama.example.com" } it "sets the hostname" do comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 1) described_class.change_host_name(machine, name) expect(comm.received_commands[1]).to match(/hostnamectl set-hostname 'banana-rama'/) 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 vagrant-2.0.2/test/unit/plugins/guests/arch/cap/configure_networks_test.rb000066400000000000000000000035701323370221500270720ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestArch::Cap::ConfigureNetworks" do let(:caps) do VagrantPlugins::GuestArch::Plugin .components .guest_capabilities[:arch] end let(:guest) { double("guest") } let(:machine) { double("machine", guest: guest) } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".configure_networks" do let(:cap) { caps.get(:configure_networks) } before do allow(guest).to receive(:capability).with(:network_interfaces) .and_return(["eth1", "eth2"]) end let(:network_1) do { interface: 0, type: "dhcp", } end let(:network_2) do { interface: 1, type: "static", ip: "33.33.33.10", netmask: "255.255.0.0", gateway: "33.33.0.1", } end it "creates and starts the networks" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/mv (.+) '\/etc\/netctl\/eth1'/) expect(comm.received_commands[0]).to match(/ip link set 'eth1' down/) expect(comm.received_commands[0]).to match(/netctl restart 'eth1'/) expect(comm.received_commands[0]).to match(/netctl enable 'eth1'/) expect(comm.received_commands[0]).to match(/mv (.+) '\/etc\/netctl\/eth2'/) expect(comm.received_commands[0]).to match(/ip link set 'eth2' down/) expect(comm.received_commands[0]).to match(/netctl restart 'eth2'/) expect(comm.received_commands[0]).to match(/netctl enable 'eth2'/) end it "should not extraneous && joiners" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).not_to match(/^\s*&&\s*$/) end end end vagrant-2.0.2/test/unit/plugins/guests/arch/cap/rsync_test.rb000066400000000000000000000013671323370221500243150ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestArch::Cap:RSync" do let(:described_class) do VagrantPlugins::GuestArch::Plugin .components .guest_capabilities[:arch] .get(:rsync_install) end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".rsync_install" do it "installs rsync=" do described_class.rsync_install(machine) expect(comm.received_commands[0]).to match(/pacman -Sy --noconfirm/) expect(comm.received_commands[0]).to match(/pacman -S --noconfirm rsync/) end end end vagrant-2.0.2/test/unit/plugins/guests/arch/cap/smb_test.rb000066400000000000000000000021331323370221500237300ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestArch::Cap::SMB" do let(:described_class) do VagrantPlugins::GuestArch::Plugin .components .guest_capabilities[:arch] .get(:smb_install) end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".smb_install" do it "installs smb when /usr/bin/mount.cifs does not exist" do comm.stub_command("test -f /usr/bin/mount.cifs", exit_code: 1) described_class.smb_install(machine) expect(comm.received_commands[1]).to match(/pacman -Sy --noconfirm/) expect(comm.received_commands[1]).to match(/pacman -S --noconfirm cifs-utils/) end it "does not install smb when /usr/bin/mount.cifs exists" do comm.stub_command("test -f /usr/bin/mount.cifs", exit_code: 0) described_class.smb_install(machine) expect(comm.received_commands.join("")).to_not match(/-S/) end end end vagrant-2.0.2/test/unit/plugins/guests/atomic/000077500000000000000000000000001323370221500213605ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/atomic/cap/000077500000000000000000000000001323370221500221235ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/atomic/cap/change_host_name_test.rb000066400000000000000000000020561323370221500267740ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestAtomic::Cap::ChangeHostName" do let(:caps) do VagrantPlugins::GuestAtomic::Plugin .components .guest_capabilities[:atomic] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".change_host_name" do let(:cap) { caps.get(:change_host_name) } let(:name) { "banana-rama.example.com" } it "sets the hostname" do comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 1) cap.change_host_name(machine, name) expect(comm.received_commands[1]).to match(/hostnamectl set-hostname 'banana-rama'/) end it "does not change the hostname if already set" do comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 0) cap.change_host_name(machine, name) expect(comm.received_commands.size).to eq(1) end end end vagrant-2.0.2/test/unit/plugins/guests/atomic/cap/docker_test.rb000066400000000000000000000013251323370221500247570ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestAtomic::Cap::ChangeHostName" do let(:described_class) do VagrantPlugins::GuestAtomic::Plugin .components .guest_capabilities[:atomic] .get(:docker_daemon_running) end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".docker_daemon_running" do it "checks /run/docker/sock" do described_class.docker_daemon_running(machine) expect(comm.received_commands[0]).to eq("test -S /run/docker.sock") end end end vagrant-2.0.2/test/unit/plugins/guests/bsd/000077500000000000000000000000001323370221500206545ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/bsd/cap/000077500000000000000000000000001323370221500214175ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/bsd/cap/halt_test.rb000066400000000000000000000020371323370221500237350ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestBSD::Cap::Halt" do let(:caps) do VagrantPlugins::GuestBSD::Plugin .components .guest_capabilities[:bsd] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } let(:shutdown_command) { "/sbin/shutdown -p now" } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".halt" do let(:cap) { caps.get(:halt) } it "runs the shutdown command" do comm.expect_command(shutdown_command) cap.halt(machine) end it "ignores an IOError" do comm.stub_command(shutdown_command, raise: IOError) expect { cap.halt(machine) }.to_not raise_error end it "ignores a Vagrant::Errors::SSHDisconnected" do comm.stub_command(shutdown_command, raise: Vagrant::Errors::SSHDisconnected) expect { cap.halt(machine) }.to_not raise_error end end end vagrant-2.0.2/test/unit/plugins/guests/bsd/cap/insert_public_key_test.rb000066400000000000000000000017151323370221500265210ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestBSD::Cap::InsertPublicKey" do let(:caps) do VagrantPlugins::GuestBSD::Plugin .components .guest_capabilities[:bsd] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".insert_public_key" do let(:cap) { caps.get(:insert_public_key) } it "inserts the public key" do cap.insert_public_key(machine, "ssh-rsa ...") expect(comm.received_commands[0]).to match(/mkdir -p ~\/.ssh/) expect(comm.received_commands[0]).to match(/chmod 0700 ~\/.ssh/) expect(comm.received_commands[0]).to match(/cat '\/tmp\/vagrant-(.+)' >> ~\/.ssh\/authorized_keys/) expect(comm.received_commands[0]).to match(/chmod 0600 ~\/.ssh\/authorized_keys/) end end end vagrant-2.0.2/test/unit/plugins/guests/bsd/cap/nfs_test.rb000066400000000000000000000033221323370221500235710ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestBSD::Cap::NFS" do let(:caps) do VagrantPlugins::GuestBSD::Plugin .components .guest_capabilities[:bsd] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".mount_nfs_folder" do let(:cap) { caps.get(:mount_nfs_folder) } let(:ip) { "1.2.3.4" } it "mounts the folder" do folders = { "/vagrant-nfs" => { guestpath: "/guest", hostpath: "/host", } } cap.mount_nfs_folder(machine, ip, folders) expect(comm.received_commands[0]).to match(/mkdir -p \/guest/) expect(comm.received_commands[1]).to match(/mount -t nfs/) expect(comm.received_commands[1]).to match(/1.2.3.4:\/host \/guest/) end it "mounts with options" do folders = { "/vagrant-nfs" => { guestpath: "/guest", hostpath: "/host", nfs_version: 2, nfs_udp: true, mount_options: ["banana"] } } cap.mount_nfs_folder(machine, ip, folders) expect(comm.received_commands[1]).to match(/mount -t nfs -o 'nfsv2,mntudp,banana'/) end it "escapes host and guest paths" do folders = { "/vagrant-nfs" => { guestpath: "/guest with spaces", hostpath: "/host's", } } cap.mount_nfs_folder(machine, ip, folders) expect(comm.received_commands[1]).to match(/host\\\'s/) expect(comm.received_commands[1]).to match(/guest\\\ with\\\ spaces/) end end end vagrant-2.0.2/test/unit/plugins/guests/bsd/cap/remove_public_key_test.rb000066400000000000000000000020471323370221500265110ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestBSD::Cap::RemovePublicKey" do let(:caps) do VagrantPlugins::GuestBSD::Plugin .components .guest_capabilities[:bsd] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".remove_public_key" do let(:cap) { caps.get(:remove_public_key) } it "removes the public key" do cap.remove_public_key(machine, "ssh-rsa ...") expect(comm.received_commands[0]).to match(/grep -v -x -f '\/tmp\/vagrant-(.+)' ~\/\.ssh\/authorized_keys > ~\/.ssh\/authorized_keys\.tmp/) expect(comm.received_commands[0]).to match(/mv ~\/.ssh\/authorized_keys\.tmp ~\/.ssh\/authorized_keys/) expect(comm.received_commands[0]).to match(/chmod 0600 ~\/.ssh\/authorized_keys/) expect(comm.received_commands[0]).to match(/rm -f '\/tmp\/vagrant-(.+)'/) end end end vagrant-2.0.2/test/unit/plugins/guests/coreos/000077500000000000000000000000001323370221500213765ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/coreos/cap/000077500000000000000000000000001323370221500221415ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/coreos/cap/change_host_name_test.rb000066400000000000000000000020301323370221500270020ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestCoreOS::Cap::ChangeHostName" do let(:described_class) do VagrantPlugins::GuestCoreOS::Plugin .components .guest_capabilities[:coreos] .get(:change_host_name) end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".change_host_name" do let(:name) { "banana-rama.example.com" } 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 vagrant-2.0.2/test/unit/plugins/guests/coreos/cap/configure_networks_test.rb000066400000000000000000000032431323370221500274440ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestCoreOS::Cap::ConfigureNetworks" do let(:described_class) do VagrantPlugins::GuestCoreOS::Plugin .components .guest_capabilities[:coreos] .get(:configure_networks) end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } let(:env) do double("env", machine: machine, active_machines: [machine]) end 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[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 let(:network_1) do { interface: 0, type: "dhcp", } end let(:network_2) do { interface: 1, type: "static", ip: "33.33.33.10", netmask: "255.255.0.0", gateway: "33.33.0.1", } 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/) end end end vagrant-2.0.2/test/unit/plugins/guests/coreos/cap/docker_test.rb000066400000000000000000000013251323370221500247750ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestCoreOS::Cap::ChangeHostName" do let(:described_class) do VagrantPlugins::GuestCoreOS::Plugin .components .guest_capabilities[:coreos] .get(:docker_daemon_running) end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".docker_daemon_running" do it "checks /run/docker/sock" do described_class.docker_daemon_running(machine) expect(comm.received_commands[0]).to eq("test -S /run/docker.sock") end end end vagrant-2.0.2/test/unit/plugins/guests/darwin/000077500000000000000000000000001323370221500213705ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/darwin/cap/000077500000000000000000000000001323370221500221335ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/darwin/cap/change_host_name_test.rb000066400000000000000000000025441323370221500270060ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestDarwin::Cap::ChangeHostName" do let(:described_class) do VagrantPlugins::GuestDarwin::Plugin .components .guest_capabilities[:darwin] .get(:change_host_name) end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".change_host_name" do let(:name) { "banana-rama.example.com" } it "sets the hostname" do comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 1) described_class.change_host_name(machine, name) expect(comm.received_commands[1]).to match(/scutil --set ComputerName 'banana-rama.example.com'/) expect(comm.received_commands[1]).to match(/scutil --set HostName 'banana-rama.example.com'/) expect(comm.received_commands[1]).to match(/scutil --set LocalHostName 'banana-rama'/) expect(comm.received_commands[1]).to match(/hostname 'banana-rama.example.com'/) 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 vagrant-2.0.2/test/unit/plugins/guests/darwin/cap/choose_addressable_ip_addr_test.rb000066400000000000000000000017301323370221500310130ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestDarwin::Cap::ChangeHostName" do let(:described_class) do VagrantPlugins::GuestDarwin::Plugin .components .guest_capabilities[:darwin] .get(:choose_addressable_ip_addr) end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".choose_addressable_ip_addr" do let(:possible) { ["1.2.3.4", "5.6.7.8"] } it "retrieves the value" do comm.stub_command("ping -c1 -t1 5.6.7.8", exit_code: 0) result = described_class.choose_addressable_ip_addr(machine, possible) expect(result).to eq("5.6.7.8") end it "returns nil if no ips are found" do result = described_class.choose_addressable_ip_addr(machine, []) expect(result).to be(nil) end end end vagrant-2.0.2/test/unit/plugins/guests/darwin/cap/halt_test.rb000066400000000000000000000020101323370221500244400ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestDarwin::Cap::Halt" do let(:caps) do VagrantPlugins::GuestDarwin::Plugin .components .guest_capabilities[:darwin] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".halt" do let(:cap) { caps.get(:halt) } it "runs the shutdown command" do comm.expect_command("/sbin/shutdown -h now") cap.halt(machine) end it "ignores an IOError" do comm.stub_command("/sbin/shutdown -h now", raise: IOError) expect { cap.halt(machine) }.to_not raise_error end it "ignores a Vagrant::Errors::SSHDisconnected" do comm.stub_command("/sbin/shutdown -h now", raise: Vagrant::Errors::SSHDisconnected) expect { cap.halt(machine) }.to_not raise_error end end end vagrant-2.0.2/test/unit/plugins/guests/darwin/cap/shell_expand_guest_path_test.rb000066400000000000000000000025651323370221500304200ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestDarwin::Cap::ShellExpandGuestPath" do let(:caps) do VagrantPlugins::GuestDarwin::Plugin .components .guest_capabilities[:darwin] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end describe "#shell_expand_guest_path" do let(:cap) { caps.get(:shell_expand_guest_path) } it "expands the path" do path = "/home/vagrant/folder" allow(machine.communicate).to receive(:execute). with(any_args).and_yield(:stdout, "/home/vagrant/folder") cap.shell_expand_guest_path(machine, path) end it "raises an exception if no path was detected" do path = "/home/vagrant/folder" expect { cap.shell_expand_guest_path(machine, path) }. to raise_error(Vagrant::Errors::ShellExpandFailed) end it "returns a path with a space in it" do path = "/home/vagrant folder/folder" path_with_spaces = "/home/vagrant\\ folder/folder" allow(machine.communicate).to receive(:execute). with(any_args).and_yield(:stdout, path_with_spaces) expect(machine.communicate).to receive(:execute).with("printf #{path_with_spaces}") cap.shell_expand_guest_path(machine, path) end end end vagrant-2.0.2/test/unit/plugins/guests/debian/000077500000000000000000000000001323370221500213265ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/debian/cap/000077500000000000000000000000001323370221500220715ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/debian/cap/change_host_name_test.rb000066400000000000000000000021521323370221500267370ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestDebian::Cap::ChangeHostName" do let(:caps) do VagrantPlugins::GuestDebian::Plugin .components .guest_capabilities[:debian] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".change_host_name" do let(:cap) { caps.get(:change_host_name) } let(:name) { 'banana-rama.example.com' } 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(/hostname -F \/etc\/hostname/) expect(comm.received_commands[1]).to match(/hostname.sh start/) end it "does not set the hostname if unset" do comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 0) cap.change_host_name(machine, name) expect(comm.received_commands.size).to eq(1) end end end vagrant-2.0.2/test/unit/plugins/guests/debian/cap/configure_networks_test.rb000066400000000000000000000104041323370221500273710ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestDebian::Cap::ConfigureNetworks" do let(:caps) do VagrantPlugins::GuestDebian::Plugin .components .guest_capabilities[:debian] end let(:guest) { double("guest") } let(:machine) { double("machine", guest: guest) } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe "#build_interface_entries" do let(:network_0) do { interface: 0, type: "dhcp", } end let(:network_1) do { interface: 1, type: "static", ip: "33.33.33.10", netmask: "255.255.0.0", gateway: "33.33.0.1", } end end describe ".configure_networks" do let(:cap) { caps.get(:configure_networks) } before do allow(guest).to receive(:capability).with(:network_interfaces) .and_return(["eth1", "eth2"]) end let(:network_0) do { interface: 0, type: "dhcp", } end let(:network_1) do { interface: 1, type: "static", ip: "33.33.33.10", netmask: "255.255.0.0", gateway: "33.33.0.1", } end before do allow(comm).to receive(:test).with("systemctl | grep '^-.mount'").and_return(false) allow(comm).to receive(:test).with("sudo systemctl status systemd-networkd.service").and_return(false) allow(comm).to receive(:test).with("netplan -h").and_return(false) end it "creates and starts the networks using net-tools" do cap.configure_networks(machine, [network_0, network_1]) expect(comm.received_commands[0]).to match("/sbin/ifdown 'eth1' || true") expect(comm.received_commands[0]).to match("/sbin/ip addr flush dev 'eth1'") expect(comm.received_commands[0]).to match("/sbin/ifdown 'eth2' || true") expect(comm.received_commands[0]).to match("/sbin/ip addr flush dev 'eth2'") expect(comm.received_commands[0]).to match("/sbin/ifup 'eth1'") expect(comm.received_commands[0]).to match("/sbin/ifup 'eth2'") end context "with systemd" do before do expect(comm).to receive(:test).with("systemctl | grep '^-.mount'").and_return(true) allow(comm).to receive(:test).with("netplan -h").and_return(false) end it "creates and starts the networks using net-tools" do cap.configure_networks(machine, [network_0, network_1]) expect(comm.received_commands[0]).to match("/sbin/ifdown 'eth1' || true") expect(comm.received_commands[0]).to match("/sbin/ip addr flush dev 'eth1'") expect(comm.received_commands[0]).to match("/sbin/ifdown 'eth2' || true") expect(comm.received_commands[0]).to match("/sbin/ip addr flush dev 'eth2'") expect(comm.received_commands[0]).to match("/sbin/ifup 'eth1'") expect(comm.received_commands[0]).to match("/sbin/ifup 'eth2'") end context "with systemd-networkd" do before do expect(comm).to receive(:test).with("sudo systemctl status systemd-networkd.service").and_return(true) end it "creates and starts the networks using systemd-networkd" do cap.configure_networks(machine, [network_0, network_1]) expect(comm.received_commands[0]).to match("mv -f '/tmp/vagrant-network-entry.*' '/etc/systemd/network/.*network'") expect(comm.received_commands[0]).to match("chown") expect(comm.received_commands[0]).to match("chmod") expect(comm.received_commands[0]).to match("systemctl restart") end end context "with netplan" do before do expect(comm).to receive(:test).with("netplan -h").and_return(true) end it "creates and starts the networks for systemd with netplan" do cap.configure_networks(machine, [network_0, network_1]) expect(comm.received_commands[0]).to match("mv -f '/tmp/vagrant-network-entry.*' '/etc/netplan/.*.yaml'") expect(comm.received_commands[0]).to match("chown") expect(comm.received_commands[0]).to match("chmod") expect(comm.received_commands[0]).to match("netplan apply") end end end end end vagrant-2.0.2/test/unit/plugins/guests/debian/cap/nfs_client_test.rb000066400000000000000000000014501323370221500256010ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestDebian::Cap::NFSClient" do let(:described_class) do VagrantPlugins::GuestDebian::Plugin .components .guest_capabilities[:debian] .get(:nfs_client_install) end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".nfs_client_install" do it "installs nfs client utilities" do described_class.nfs_client_install(machine) expect(comm.received_commands[0]).to match(/apt-get -yqq update/) expect(comm.received_commands[0]).to match(/apt-get -yqq install nfs-common portmap/) end end end vagrant-2.0.2/test/unit/plugins/guests/debian/cap/rsync_test.rb000066400000000000000000000013701323370221500246140ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestDebian::Cap:RSync" do let(:described_class) do VagrantPlugins::GuestDebian::Plugin .components .guest_capabilities[:debian] .get(:rsync_install) end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".rsync_install" do it "installs rsync" do described_class.rsync_install(machine) expect(comm.received_commands[0]).to match(/apt-get -yqq update/) expect(comm.received_commands[0]).to match(/apt-get -yqq install rsync/) end end end vagrant-2.0.2/test/unit/plugins/guests/debian/cap/smb_test.rb000066400000000000000000000021251323370221500242360ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestDebian::Cap::SMB" do let(:described_class) do VagrantPlugins::GuestDebian::Plugin .components .guest_capabilities[:debian] .get(:smb_install) end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".smb_install" do it "installs smb when /sbin/mount.cifs does not exist" do comm.stub_command("test -f /sbin/mount.cifs", exit_code: 1) described_class.smb_install(machine) expect(comm.received_commands[1]).to match(/apt-get -yqq update/) expect(comm.received_commands[1]).to match(/apt-get -yqq install cifs-utils/) end it "does not install smb when /sbin/mount.cifs exists" do comm.stub_command("test -f /sbin/mount.cifs", exit_code: 0) described_class.smb_install(machine) expect(comm.received_commands.join("")).to_not match(/update/) end end end vagrant-2.0.2/test/unit/plugins/guests/esxi/000077500000000000000000000000001323370221500210545ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/esxi/cap/000077500000000000000000000000001323370221500216175ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/esxi/cap/halt_test.rb000066400000000000000000000020321323370221500241300ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestEsxi::Cap::Halt" do let(:caps) do VagrantPlugins::GuestEsxi::Plugin .components .guest_capabilities[:esxi] end let(:shutdown_command){ "/bin/halt -d 0" } let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".halt" do let(:cap) { caps.get(:halt) } it "runs the shutdown command" do comm.expect_command(shutdown_command) cap.halt(machine) end it "ignores an IOError" do comm.stub_command(shutdown_command, raise: IOError) expect { cap.halt(machine) }.to_not raise_error end it "ignores a Vagrant::Errors::SSHDisconnected" do comm.stub_command(shutdown_command, raise: Vagrant::Errors::SSHDisconnected) expect { cap.halt(machine) }.to_not raise_error end end end vagrant-2.0.2/test/unit/plugins/guests/esxi/cap/public_key_test.rb000066400000000000000000000034631323370221500253370ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestEsxi::Cap::PublicKey" do let(:caps) do VagrantPlugins::GuestEsxi::Plugin .components .guest_capabilities[:esxi] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".insert_public_key" do let(:cap) { caps.get(:insert_public_key) } it "inserts the public key" do cap.insert_public_key(machine, "ssh-rsa ...") expect(comm.received_commands[0]).to match(/SSH_DIR=".*"/) expect(comm.received_commands[0]).to match(/mkdir -p "\${SSH_DIR}"/) expect(comm.received_commands[0]).to match(/chmod 0700 "\${SSH_DIR}"/) expect(comm.received_commands[0]).to match(/cat '\/tmp\/vagrant-(.+)' >> "\${SSH_DIR}\/authorized_keys"/) expect(comm.received_commands[0]).to match(/chmod 0600 "\${SSH_DIR}\/authorized_keys"/) expect(comm.received_commands[0]).to match(/rm -f '\/tmp\/vagrant-(.+)'/) end end describe ".remove_public_key" do let(:cap) { caps.get(:remove_public_key) } it "removes the public key" do cap.remove_public_key(machine, "ssh-rsa ...") expect(comm.received_commands[0]).to match(/SSH_DIR=".*"/) expect(comm.received_commands[0]).to match(/grep -v -x -f '\/tmp\/vagrant-(.+)' "\${SSH_DIR}\/authorized_keys" > "\${SSH_DIR}\/authorized_keys\.tmp"/) expect(comm.received_commands[0]).to match(/mv "\${SSH_DIR}\/authorized_keys\.tmp" "\${SSH_DIR}\/authorized_keys"/) expect(comm.received_commands[0]).to match(/chmod 0600 "\${SSH_DIR}\/authorized_keys"/) expect(comm.received_commands[0]).to match(/rm -f '\/tmp\/vagrant-(.+)'/) end end end vagrant-2.0.2/test/unit/plugins/guests/freebsd/000077500000000000000000000000001323370221500215165ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/freebsd/cap/000077500000000000000000000000001323370221500222615ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/freebsd/cap/change_host_name_test.rb000066400000000000000000000023611323370221500271310ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestFreeBSD::Cap::ChangeHostName" do let(:described_class) do VagrantPlugins::GuestFreeBSD::Plugin .components .guest_capabilities[:freebsd] .get(:change_host_name) end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".change_host_name" do let(:name) { "banana-rama.example.com" } it "sets the hostname and /etc/hosts" do comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 1) described_class.change_host_name(machine, name) expect(comm.received_commands[1]).to match(/hostname '#{name}'/) expect(comm.received_commands[1]).to match(/grep -w '#{name}' \/etc\/hosts/) expect(comm.received_commands[1]).to match(/echo -e '127.0.0.1\\t#{name}\\tbanana-rama'/) end it "does nothing if the hostname is 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 vagrant-2.0.2/test/unit/plugins/guests/freebsd/cap/configure_networks_test.rb000066400000000000000000000025261323370221500275670ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestFreeBSD::Cap::ConfigureNetworks" do let(:described_class) do VagrantPlugins::GuestFreeBSD::Plugin .components .guest_capabilities[:freebsd] .get(:configure_networks) end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } 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") end after do comm.verify_expectations! end describe ".configure_networks" do let(:network_1) do { interface: 0, type: "dhcp", } end let(:network_2) do { interface: 1, type: "static", ip: "33.33.33.10", netmask: "255.255.0.0", gateway: "33.33.0.1", } end it "creates and starts the networks" do described_class.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[1]).to match(/dhclient 'em1'/) expect(comm.received_commands[1]).to match(/\/etc\/rc.d\/netif restart 'em1'/) expect(comm.received_commands[1]).to_not match(/dhclient 'em2'/) expect(comm.received_commands[1]).to match(/\/etc\/rc.d\/netif restart 'em2'/) end end end vagrant-2.0.2/test/unit/plugins/guests/freebsd/cap/rsync_test.rb000066400000000000000000000020371323370221500250050ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestFreeBSD::Cap::RSync" do let(:caps) do VagrantPlugins::GuestFreeBSD::Plugin .components .guest_capabilities[:freebsd] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".rsync_install" do let(:cap) { caps.get(:rsync_install) } it "installs rsync" do comm.expect_command("pkg install -y rsync") cap.rsync_install(machine) end end describe ".rsync_installed" do let(:cap) { caps.get(:rsync_installed) } it "checks if rsync is installed" do comm.expect_command("which rsync") cap.rsync_installed(machine) end end describe ".rsync_command" do let(:cap) { caps.get(:rsync_command) } it "defaults to 'sudo rsync'" do expect(cap.rsync_command(machine)).to eq("sudo rsync") end end end vagrant-2.0.2/test/unit/plugins/guests/freebsd/cap/shell_expand_guest_path_test.rb000066400000000000000000000026211323370221500305370ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestFreeBSD::Cap::ShellExpandGuestPath" do let(:caps) do VagrantPlugins::GuestFreeBSD::Plugin .components .guest_capabilities[:freebsd] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end describe "#shell_expand_guest_path" do let(:cap) { caps.get(:shell_expand_guest_path) } it "expands the path" do path = "/home/vagrant/folder" allow(machine.communicate).to receive(:execute). with(any_args).and_yield(:stdout, "/home/vagrant/folder") cap.shell_expand_guest_path(machine, path) end it "raises an exception if no path was detected" do path = "/home/vagrant/folder" expect { cap.shell_expand_guest_path(machine, path) }. to raise_error(Vagrant::Errors::ShellExpandFailed) end it "returns a path with a space in it" do path = "/home/vagrant folder/folder" path_with_spaces = "/home/vagrant\\ folder/folder" allow(machine.communicate).to receive(:execute). with(any_args).and_yield(:stdout, path_with_spaces) expect(machine.communicate).to receive(:execute) .with("printf #{path_with_spaces}", {:shell=>"sh"}) cap.shell_expand_guest_path(machine, path) end end end vagrant-2.0.2/test/unit/plugins/guests/linux/000077500000000000000000000000001323370221500212435ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/linux/cap/000077500000000000000000000000001323370221500220065ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/linux/cap/choose_addressable_ip_addr_test.rb000066400000000000000000000020071323370221500306640ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestLinux::Cap::ChooseAddressableIPAddr" do let(:caps) do VagrantPlugins::GuestLinux::Plugin .components .guest_capabilities[:linux] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".choose_addressable_ip_addr" do let(:cap) { caps.get(:choose_addressable_ip_addr) } it "returns the first matching IP address" do possible = ["1.2.3.4", "5.6.7.8"] possible.each do |ip| comm.stub_command("ping -c1 -w1 -W1 #{ip}", exit_code: 0) end result = cap.choose_addressable_ip_addr(machine, possible) expect(result).to eq("1.2.3.4") end it "returns nil when there are no matches" do result = cap.choose_addressable_ip_addr(machine, []) expect(result).to be(nil) end end end vagrant-2.0.2/test/unit/plugins/guests/linux/cap/halt_test.rb000066400000000000000000000017601323370221500243260ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestLinux::Cap::Halt" do let(:caps) do VagrantPlugins::GuestLinux::Plugin .components .guest_capabilities[:linux] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".halt" do let(:cap) { caps.get(:halt) } it "runs the shutdown command" do comm.expect_command("shutdown -h now") cap.halt(machine) end it "does not raise an IOError" do comm.stub_command("shutdown -h now", raise: IOError) expect { cap.halt(machine) }.to_not raise_error end it "does not raise a SSHDisconnected" do comm.stub_command("shutdown -h now", raise: Vagrant::Errors::SSHDisconnected) expect { cap.halt(machine) }.to_not raise_error end end end vagrant-2.0.2/test/unit/plugins/guests/linux/cap/insert_public_key_test.rb000066400000000000000000000017231323370221500271070ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestLinux::Cap::InsertPublicKey" do let(:caps) do VagrantPlugins::GuestLinux::Plugin .components .guest_capabilities[:linux] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".insert_public_key" do let(:cap) { caps.get(:insert_public_key) } it "inserts the public key" do cap.insert_public_key(machine, "ssh-rsa ...") expect(comm.received_commands[0]).to match(/mkdir -p ~\/.ssh/) expect(comm.received_commands[0]).to match(/chmod 0700 ~\/.ssh/) expect(comm.received_commands[0]).to match(/cat '\/tmp\/vagrant-(.+)' >> ~\/.ssh\/authorized_keys/) expect(comm.received_commands[0]).to match(/chmod 0600 ~\/.ssh\/authorized_keys/) end end end vagrant-2.0.2/test/unit/plugins/guests/linux/cap/mount_nfs_test.rb000066400000000000000000000043261323370221500254070ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestLinux::Cap::MountNFS" do let(:caps) do VagrantPlugins::GuestLinux::Plugin .components .guest_capabilities[:linux] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".mount_nfs_folder" do let(:cap) { caps.get(:mount_nfs_folder) } let(:ip) { "1.2.3.4" } let(:hostpath) { "/host" } let(:guestpath) { "/guest" } before do allow(machine).to receive(:guest).and_return( double("capability", capability: guestpath) ) end it "mounts the folder" do folders = { "/vagrant-nfs" => { type: :nfs, guestpath: "/guest", hostpath: "/host", } } cap.mount_nfs_folder(machine, ip, folders) expect(comm.received_commands[0]).to match(/mkdir -p #{guestpath}/) expect(comm.received_commands[1]).to match(/1.2.3.4:#{hostpath} #{guestpath}/) end it "mounts with options" do folders = { "/vagrant-nfs" => { type: :nfs, guestpath: "/guest", hostpath: "/host", nfs_version: 2, nfs_udp: true, } } cap.mount_nfs_folder(machine, ip, folders) expect(comm.received_commands[1]).to match(/mount -o vers=2,udp/) end it "emits an event" do folders = { "/vagrant-nfs" => { type: :nfs, guestpath: "/guest", hostpath: "/host", } } cap.mount_nfs_folder(machine, ip, folders) expect(comm.received_commands[2]).to include( "/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{guestpath}") end it "escapes host and guest paths" do folders = { "/vagrant-nfs" => { guestpath: "/guest with spaces", hostpath: "/host's", } } cap.mount_nfs_folder(machine, ip, folders) expect(comm.received_commands[1]).to match(/host\\\'s/) expect(comm.received_commands[1]).to match(/guest\\\ with\\\ spaces/) end end end vagrant-2.0.2/test/unit/plugins/guests/linux/cap/mount_shared_folder_test.rb000066400000000000000000000007171323370221500274220ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestLinux::Cap::MountSharedFolder" do let(:machine) { double("machine") } let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } let(:guest) { double("guest") } before do allow(machine).to receive(:guest).and_return(guest) allow(machine).to receive(:communicate).and_return(communicator) allow(guest).to receive(:capability).and_return(nil) end end vagrant-2.0.2/test/unit/plugins/guests/linux/cap/mount_smb_shared_folder.rb000066400000000000000000000106051323370221500272210ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestLinux::Cap::MountSMBSharedFolder" do let(:caps) do VagrantPlugins::GuestLinux::Plugin .components .guest_capabilities[:linux] end let(:machine) { double("machine", env: env) } let(:env) { double("env", host: host) } let(:host) { double("host") } let(:guest) { double("guest") } 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, smb_host: "localhost", smb_username: "user", smb_password: "pass" } end let(:cap){ caps.get(:mount_smb_shared_folder) } before do allow(machine).to receive(:communicate).and_return(comm) allow(host).to receive(:capability?).and_return(false) end after do comm.verify_expectations! end describe ".mount_smb_shared_folder" do before do allow(comm).to receive(:sudo).with(any_args).and_return(0) allow(comm).to receive(:execute).with(any_args) allow(machine).to receive(:guest).and_return(guest) allow(guest).to receive(:capability).with(:shell_expand_guest_path, mount_guest_path).and_return(mount_guest_path) end it "generates the expected default mount command" do expect(comm).to receive(:sudo).with(/mount -t cifs/, anything).and_return(0) cap.mount_smb_shared_folder(machine, mount_name, mount_guest_path, folder_options) end it "creates directory on guest machine" do expect(comm).to receive(:sudo).with("mkdir -p #{mount_guest_path}") cap.mount_smb_shared_folder(machine, mount_name, mount_guest_path, folder_options) end it "writes username into guest credentials file" do expect(comm).to receive(:sudo).with(/smb_creds.*username=#{folder_options[:smb_username]}/m) cap.mount_smb_shared_folder(machine, mount_name, mount_guest_path, folder_options) end it "writes password into guest credentials file" do expect(comm).to receive(:sudo).with(/smb_creds.*password=#{folder_options[:smb_password]}/m) cap.mount_smb_shared_folder(machine, mount_name, mount_guest_path, folder_options) end it "removes the credentials file before completion" do expect(comm).to receive(:sudo).with(/rm.+smb_creds_.+/) cap.mount_smb_shared_folder(machine, mount_name, mount_guest_path, folder_options) end it "sends upstart notification after mount" do expect(comm).to receive(:sudo).with(/emit/) cap.mount_smb_shared_folder(machine, mount_name, mount_guest_path, folder_options) end context "with custom mount options" do let(:folder_options) do { owner: mount_owner, group: mount_group, smb_host: "localhost", smb_username: "user", smb_password: "pass", mount_options: ["ro", "sec=custom"] } end it "adds given mount options to command" do expect(comm).to receive(:sudo).with(/ro/, any_args) cap.mount_smb_shared_folder(machine, mount_name, mount_guest_path, folder_options) end it "replaces defined options" do expect(comm).to receive(:sudo).with(/sec=custom/, any_args) cap.mount_smb_shared_folder(machine, mount_name, mount_guest_path, folder_options) end it "does not include replaced options" do expect(comm).not_to receive(:sudo).with(/sec=ntlm/, any_args) cap.mount_smb_shared_folder(machine, mount_name, mount_guest_path, folder_options) end end end describe ".merge_mount_options" do let(:base){ ["opt1", "opt2=on", "opt3", "opt4,opt5=off"] } let(:override){ ["opt8", "opt4=on,opt6,opt7=true"] } context "with no override" do it "should split options into individual options" do result = cap.merge_mount_options(base, []) expect(result.size).to eq(5) end end context "with overrides" do it "should merge all options" do result = cap.merge_mount_options(base, override) expect(result.size).to eq(8) end it "should override options defined in base" do result = cap.merge_mount_options(base, override) expect(result).to include("opt4=on") end end end end vagrant-2.0.2/test/unit/plugins/guests/linux/cap/mount_virtual_box_shared_folder_test.rb000066400000000000000000000235461323370221500320450ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestLinux::Cap::MountVirtualBoxSharedFolder" do let(:caps) do VagrantPlugins::GuestLinux::Plugin .components .guest_capabilities[:linux] 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 vboxsf -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 vboxsf -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 vboxsf -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 vboxsf -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 vboxsf -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 vboxsf -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 vboxsf -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 vboxsf -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 vboxsf -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 vboxsf -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 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 vagrant-2.0.2/test/unit/plugins/guests/linux/cap/network_interfaces_test.rb000066400000000000000000000071621323370221500272740ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestLinux::Cap::NetworkInterfaces" do let(:caps) do VagrantPlugins::GuestLinux::Plugin .components .guest_capabilities[:linux] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".network_interfaces" do let(:cap){ caps.get(:network_interfaces) } it "sorts discovered classic interfaces" do expect(comm).to receive(:sudo).twice.and_yield(:stdout, "eth1\neth2\neth0") result = cap.network_interfaces(machine) expect(result).to eq(["eth0", "eth1", "eth2"]) end it "sorts discovered predictable network interfaces" do expect(comm).to receive(:sudo).twice.and_yield(:stdout, "enp0s8\nenp0s3\nenp0s5") result = cap.network_interfaces(machine) expect(result).to eq(["enp0s3", "enp0s5", "enp0s8"]) end it "sorts discovered classic interfaces naturally" do expect(comm).to receive(:sudo).twice.and_yield(:stdout, "eth1\neth2\neth12\neth0\neth10") result = cap.network_interfaces(machine) expect(result).to eq(["eth0", "eth1", "eth2", "eth10", "eth12"]) end it "sorts discovered predictable network interfaces naturally" do expect(comm).to receive(:sudo).twice.and_yield(:stdout, "enp0s8\nenp0s3\nenp0s5\nenp0s10\nenp1s3") result = cap.network_interfaces(machine) expect(result).to eq(["enp0s3", "enp0s5", "enp0s8", "enp0s10", "enp1s3"]) end it "sorts ethernet devices discovered with classic naming first in list" do expect(comm).to receive(:sudo).twice.and_yield(:stdout, "eth1\neth2\ndocker0\nbridge0\neth0") result = cap.network_interfaces(machine) expect(result).to eq(["eth0", "eth1", "eth2", "bridge0", "docker0"]) end it "sorts ethernet devices discovered with predictable network interfaces naming first in list" do expect(comm).to receive(:sudo).twice.and_yield(:stdout, "enp0s8\ndocker0\nenp0s3\nbridge0\nenp0s5") result = cap.network_interfaces(machine) expect(result).to eq(["enp0s3", "enp0s5", "enp0s8", "bridge0", "docker0"]) end it "sorts ethernet devices discovered with predictable network interfaces naming first in list with less" do expect(comm).to receive(:sudo).twice.and_yield(:stdout, "enp0s3\nenp0s8\ndocker0") result = cap.network_interfaces(machine) expect(result).to eq(["enp0s3", "enp0s8", "docker0"]) end it "does not include ethernet devices aliases within prefix device listing" do expect(comm).to receive(:sudo).twice.and_yield(:stdout, "eth1\neth2\ndocker0\nbridge0\neth0\ndocker1\neth0:0") result = cap.network_interfaces(machine) expect(result).to eq(["eth0", "eth1", "eth2", "bridge0", "docker0", "docker1", "eth0:0"]) end it "does not include ethernet devices aliases within prefix device listing with dot separators" do expect(comm).to receive(:sudo).twice.and_yield(:stdout, "eth1\neth2\ndocker0\nbridge0\neth0\ndocker1\neth0.1@eth0") result = cap.network_interfaces(machine) expect(result).to eq(["eth0", "eth1", "eth2", "bridge0", "docker0", "docker1", "eth0.1@eth0"]) end it "properly sorts non-consistent device name formats" do expect(comm).to receive(:sudo).twice.and_yield(:stdout, "eth0\neth1\ndocker0\nveth437f7f9\nveth06b3e44\nveth8bb7081") result = cap.network_interfaces(machine) expect(result).to eq(["eth0", "eth1", "docker0", "veth8bb7081", "veth437f7f9", "veth06b3e44"]) end end end vagrant-2.0.2/test/unit/plugins/guests/linux/cap/nfs_client_test.rb000066400000000000000000000012651323370221500255220ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestLinux::Cap::NFSClient" do let(:caps) do VagrantPlugins::GuestLinux::Plugin .components .guest_capabilities[:linux] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".nfs_client_installed" do let(:cap) { caps.get(:nfs_client_installed) } it "installs nfs client utilities" do comm.expect_command("test -x /sbin/mount.nfs") cap.nfs_client_installed(machine) end end end vagrant-2.0.2/test/unit/plugins/guests/linux/cap/port_test.rb000066400000000000000000000012721323370221500243600ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestLinux::Cap::Port" do let(:caps) do VagrantPlugins::GuestLinux::Plugin .components .guest_capabilities[:linux] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".port_open_check" do let(:cap) { caps.get(:port_open_check) } it "checks if the port is open" do port = 8080 comm.expect_command("nc -z -w2 127.0.0.1 #{port}") cap.port_open_check(machine, port) end end end vagrant-2.0.2/test/unit/plugins/guests/linux/cap/remove_public_key_test.rb000066400000000000000000000020551323370221500270770ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestLinux::Cap::RemovePublicKey" do let(:caps) do VagrantPlugins::GuestLinux::Plugin .components .guest_capabilities[:linux] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".remove_public_key" do let(:cap) { caps.get(:remove_public_key) } it "removes the public key" do cap.remove_public_key(machine, "ssh-rsa ...") expect(comm.received_commands[0]).to match(/grep -v -x -f '\/tmp\/vagrant-(.+)' ~\/\.ssh\/authorized_keys > ~\/.ssh\/authorized_keys\.tmp/) expect(comm.received_commands[0]).to match(/mv ~\/.ssh\/authorized_keys\.tmp ~\/.ssh\/authorized_keys/) expect(comm.received_commands[0]).to match(/chmod 0600 ~\/.ssh\/authorized_keys/) expect(comm.received_commands[0]).to match(/rm -f '\/tmp\/vagrant-(.+)'/) end end end vagrant-2.0.2/test/unit/plugins/guests/linux/cap/rsync_test.rb000066400000000000000000000057401323370221500245360ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestLinux::Cap::Rsync" do let(:caps) do VagrantPlugins::GuestLinux::Plugin .components .guest_capabilities[:linux] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } let(:guest_directory){ "/guest/directory/path" } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".rsync_installed" do let(:cap) { caps.get(:rsync_installed) } it "checks if the command is installed" do comm.expect_command("which rsync") cap.rsync_installed(machine) end end describe ".rsync_command" do let(:cap) { caps.get(:rsync_command) } it "provides the rsync command to use" do expect(cap.rsync_command(machine)).to eq("sudo rsync") end end describe ".rsync_pre" do let(:cap) { caps.get(:rsync_pre) } it "creates target directory on guest" do comm.expect_command("mkdir -p #{guest_directory}") cap.rsync_pre(machine, :guestpath => guest_directory) end end describe ".rsync_post" do let(:cap) { caps.get(:rsync_post) } let(:host_directory){ '.' } let(:owner) { "vagrant-user" } let(:group) { "vagrant-group" } let(:excludes) { false } let(:options) do { hostpath: host_directory, guestpath: guest_directory, owner: owner, group: group, exclude: excludes } end it "chowns files within the guest directory" do comm.expect_command( "find #{guest_directory} '!' -type l -a '(' ! -user #{owner} -or " \ "! -group #{group} ')' -exec chown #{owner}:#{group} '{}' +" ) cap.rsync_post(machine, options) end context "with excludes provided" do let(:excludes){ ["tmp", "state/*", "path/with a/space"] } it "ignores files that are excluded" do # comm.expect_command( # "find #{guest_directory} -path #{Shellwords.escape(File.join(guest_directory, excludes.first))} -prune -o " \ # "-path #{Shellwords.escape(File.join(guest_directory, excludes.last))} -prune -o '!' " \ # "-path -type l -a '(' ! -user " \ # "#{owner} -or ! -group #{group} ')' -exec chown #{owner}:#{group} '{}' +" # ) cap.rsync_post(machine, options) excludes.each do |ex_path| expect(comm.received_commands.first).to include("-path #{Shellwords.escape(File.join(guest_directory, ex_path))} -prune") end end it "properly escapes excluded directories" do cap.rsync_post(machine, options) exclude_with_space = excludes.detect{|ex| ex.include?(' ')} escaped_exclude_with_space = Shellwords.escape(exclude_with_space) expect(comm.received_commands.first).not_to include(exclude_with_space) expect(comm.received_commands.first).to include(escaped_exclude_with_space) end end end end vagrant-2.0.2/test/unit/plugins/guests/linux/cap/shell_expand_guest_path_test.rb000066400000000000000000000025701323370221500302670ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestLinux::Cap::ShellExpandGuestPath" do let(:caps) do VagrantPlugins::GuestLinux::Plugin .components .guest_capabilities[:linux] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end describe "#shell_expand_guest_path" do let(:cap) { caps.get(:shell_expand_guest_path) } it "expands the path" do path = "/home/vagrant/folder" allow(machine.communicate).to receive(:execute). with(any_args).and_yield(:stdout, "/home/vagrant/folder") cap.shell_expand_guest_path(machine, path) end it "raises an exception if no path was detected" do path = "/home/vagrant/folder" expect { cap.shell_expand_guest_path(machine, path) }. to raise_error(Vagrant::Errors::ShellExpandFailed) end it "returns a path with a space in it" do path = "/home/vagrant folder/folder" path_with_spaces = "/home/vagrant\\ folder/folder" allow(machine.communicate).to receive(:execute). with(any_args).and_yield(:stdout, path_with_spaces) expect(machine.communicate).to receive(:execute).with("echo; printf #{path_with_spaces}") cap.shell_expand_guest_path(machine, path) end end end vagrant-2.0.2/test/unit/plugins/guests/netbsd/000077500000000000000000000000001323370221500213635ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/netbsd/cap/000077500000000000000000000000001323370221500221265ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/netbsd/cap/shell_expand_guest_path_test.rb000066400000000000000000000025651323370221500304130ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestNetBSD::Cap::ShellExpandGuestPath" do let(:caps) do VagrantPlugins::GuestNetBSD::Plugin .components .guest_capabilities[:netbsd] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end describe "#shell_expand_guest_path" do let(:cap) { caps.get(:shell_expand_guest_path) } it "expands the path" do path = "/home/vagrant/folder" allow(machine.communicate).to receive(:execute). with(any_args).and_yield(:stdout, "/home/vagrant/folder") cap.shell_expand_guest_path(machine, path) end it "raises an exception if no path was detected" do path = "/home/vagrant/folder" expect { cap.shell_expand_guest_path(machine, path) }. to raise_error(Vagrant::Errors::ShellExpandFailed) end it "returns a path with a space in it" do path = "/home/vagrant folder/folder" path_with_spaces = "/home/vagrant\\ folder/folder" allow(machine.communicate).to receive(:execute). with(any_args).and_yield(:stdout, path_with_spaces) expect(machine.communicate).to receive(:execute).with("printf #{path_with_spaces}") cap.shell_expand_guest_path(machine, path) end end end vagrant-2.0.2/test/unit/plugins/guests/omnios/000077500000000000000000000000001323370221500214105ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/omnios/cap/000077500000000000000000000000001323370221500221535ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/omnios/cap/change_host_name_test.rb000066400000000000000000000021441323370221500270220ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestOmniOS::Cap:RSync" do let(:caps) do VagrantPlugins::GuestOmniOS::Plugin .components .guest_capabilities[:omnios] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".change_host_name" do let(:cap) { caps.get(:change_host_name) } let(:name) { "banana-rama.example.com" } it "sets the hostname if unset" do comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 1) cap.change_host_name(machine, name) expect(comm.received_commands[1]).to match(/echo '#{name}' > \/etc\/nodename/) expect(comm.received_commands[1]).to match(/hostname '#{name}'/) end it "does not set the hostname if set" do comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 0) cap.change_host_name(machine, name) expect(comm.received_commands.size).to eq(1) end end end vagrant-2.0.2/test/unit/plugins/guests/omnios/cap/mount_nfs_folder_test.rb000066400000000000000000000020461323370221500271040ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestOmniOS::Cap:RSync" do let(:caps) do VagrantPlugins::GuestOmniOS::Plugin .components .guest_capabilities[:omnios] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".mount_nfs_folder" do let(:cap) { caps.get(:mount_nfs_folder) } let(:ip) { "1.2.3.4" } let(:hostpath) { "/host" } let(:guestpath) { "/guest" } it "mounts the folder" do folders = { "/vagrant-nfs" => { type: :nfs, guestpath: "/guest", hostpath: "/host", } } cap.mount_nfs_folder(machine, ip, folders) expect(comm.received_commands[0]).to match(/mkdir -p '#{guestpath}'/) expect(comm.received_commands[0]).to match(/'1.2.3.4:#{hostpath}' '#{guestpath}'/) end it "mounts with options" end end vagrant-2.0.2/test/unit/plugins/guests/omnios/cap/rsync_test.rb000066400000000000000000000012401323370221500246720ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestOmniOS::Cap:RSync" do let(:caps) do VagrantPlugins::GuestOmniOS::Plugin .components .guest_capabilities[:omnios] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".rsync_install" do let(:cap) { caps.get(:rsync_install) } it "installs rsync" do cap.rsync_install(machine) expect(comm.received_commands[0]).to match(/pkg install rsync/) end end end vagrant-2.0.2/test/unit/plugins/guests/openbsd/000077500000000000000000000000001323370221500215365ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/openbsd/cap/000077500000000000000000000000001323370221500223015ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/openbsd/cap/halt_test.rb000066400000000000000000000020241323370221500246130ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestOpenBSD::Cap::Halt" do let(:caps) do VagrantPlugins::GuestOpenBSD::Plugin .components .guest_capabilities[:openbsd] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".halt" do let(:cap) { caps.get(:halt) } it "runs the shutdown command" do comm.expect_command("/sbin/shutdown -p -h now") cap.halt(machine) end it "ignores an IOError" do comm.stub_command("/sbin/shutdown -p -h now", raise: IOError) expect { cap.halt(machine) }.to_not raise_error end it "ignores a Vagrant::Errors::SSHDisconnected" do comm.stub_command("/sbin/shutdown -p -h now", raise: Vagrant::Errors::SSHDisconnected) expect { cap.halt(machine) }.to_not raise_error end end end vagrant-2.0.2/test/unit/plugins/guests/openbsd/cap/rsync_test.rb000066400000000000000000000030071323370221500250230ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestOpenBSD::Cap::RSync" do let(:caps) do VagrantPlugins::GuestOpenBSD::Plugin .components .guest_capabilities[:openbsd] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".rsync_install" do let(:cap) { caps.get(:rsync_install) } describe "successful installation" do it "installs rsync" do cap.rsync_install(machine) expect(comm.received_commands[0]).to match(/pkg_add -I rsync/) expect(comm.received_commands[1]).to match(/pkg_info/) end end describe "failure installation" do before do expect(comm).to receive(:execute).and_raise(Vagrant::Errors::RSyncNotInstalledInGuest, {command: '', output: ''}) end it "raises custom exception" do expect{ cap.rsync_install(machine) }.to raise_error(Vagrant::Errors::RSyncNotInstalledInGuest) end end end describe ".rsync_installed" do let(:cap) { caps.get(:rsync_installed) } it "checks if rsync is installed" do comm.expect_command("which rsync") cap.rsync_installed(machine) end end describe ".rsync_command" do let(:cap) { caps.get(:rsync_command) } it "defaults to 'sudo rsync'" do expect(cap.rsync_command(machine)).to eq("sudo rsync") end end end vagrant-2.0.2/test/unit/plugins/guests/openbsd/cap/shell_expand_guest_path_test.rb000066400000000000000000000025701323370221500305620ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestOpenBSD::Cap::ShellExpandGuestPath" do let(:caps) do VagrantPlugins::GuestOpenBSD::Plugin .components .guest_capabilities[:openbsd] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end describe "#shell_expand_guest_path" do let(:cap) { caps.get(:shell_expand_guest_path) } it "expands the path" do path = "/home/vagrant/folder" allow(machine.communicate).to receive(:execute). with(any_args).and_yield(:stdout, "/home/vagrant/folder") cap.shell_expand_guest_path(machine, path) end it "raises an exception if no path was detected" do path = "/home/vagrant/folder" expect { cap.shell_expand_guest_path(machine, path) }. to raise_error(Vagrant::Errors::ShellExpandFailed) end it "returns a path with a space in it" do path = "/home/vagrant folder/folder" path_with_spaces = "/home/vagrant\\ folder/folder" allow(machine.communicate).to receive(:execute). with(any_args).and_yield(:stdout, path_with_spaces) expect(machine.communicate).to receive(:execute).with("printf #{path_with_spaces}") cap.shell_expand_guest_path(machine, path) end end end vagrant-2.0.2/test/unit/plugins/guests/photon/000077500000000000000000000000001323370221500214135ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/photon/cap/000077500000000000000000000000001323370221500221565ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/photon/cap/change_host_name_test.rb000066400000000000000000000022471323370221500270310ustar00rootroot00000000000000# Copyright (c) 2015 VMware, Inc. All Rights Reserved. require_relative "../../../../base" describe "VagrantPlugins::GuestPhoton::Cap::ChangeHostName" do let(:caps) do VagrantPlugins::GuestPhoton::Plugin .components .guest_capabilities[:photon] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".change_host_name" do let(:cap) { caps.get(:change_host_name) } let(:name) { "banana-rama.example.com" } it "sets the hostname" do comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 1) cap.change_host_name(machine, name) expect(comm.received_commands[1]).to match(/echo '#{name}' > \/etc\/hostname/) expect(comm.received_commands[1]).to match(/hostname '#{name}'/) end it "does not change the hostname if already set" do comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 0) cap.change_host_name(machine, name) expect(comm.received_commands.size).to eq(1) end end end vagrant-2.0.2/test/unit/plugins/guests/photon/cap/configure_networks_test.rb000066400000000000000000000023501323370221500274570ustar00rootroot00000000000000# Copyright (c) 2015 VMware, Inc. All Rights Reserved. require_relative "../../../../base" describe "VagrantPlugins::GuestPhoton::Cap:ConfigureNetworks" do let(:caps) do VagrantPlugins::GuestPhoton::Plugin .components .guest_capabilities[:photon] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) comm.stub_command("ifconfig | grep 'eth' | cut -f1 -d' '", stdout: "eth1\neth2") end after do comm.verify_expectations! end describe ".configure_networks" do let(:cap) { caps.get(:configure_networks) } let(:network_1) do { interface: 0, type: "dhcp", } end let(:network_2) do { interface: 1, type: "static", ip: "33.33.33.10", netmask: "255.255.0.0", gateway: "33.33.0.1", } end it "creates and starts the networks" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[1]).to match(/ifconfig eth1/) expect(comm.received_commands[1]).to match(/ifconfig eth2 33.33.33.10 netmask 255.255.0.0/) end end end vagrant-2.0.2/test/unit/plugins/guests/photon/cap/docker_test.rb000066400000000000000000000013411323370221500250100ustar00rootroot00000000000000# Copyright (c) 2015 VMware, Inc. All Rights Reserved. require_relative "../../../../base" describe "VagrantPlugins::GuestPhoton::Cap:Docker" do let(:caps) do VagrantPlugins::GuestPhoton::Plugin .components .guest_capabilities[:photon] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".docker_daemon_running" do let(:cap) { caps.get(:docker_daemon_running) } it "installs rsync" do comm.expect_command("test -S /run/docker.sock") cap.docker_daemon_running(machine) end end end vagrant-2.0.2/test/unit/plugins/guests/pld/000077500000000000000000000000001323370221500206635ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/pld/cap/000077500000000000000000000000001323370221500214265ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/pld/cap/change_host_name_test.rb000066400000000000000000000020211323370221500262670ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestPld::Cap::ChangeHostName" do let(:caps) do VagrantPlugins::GuestPld::Plugin .components .guest_capabilities[:pld] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".change_host_name" do let(:cap) { caps.get(:change_host_name) } let(:name) { "banana-rama.example.com" } it "sets the hostname" do comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 1) cap.change_host_name(machine, name) expect(comm.received_commands[1]).to match(/hostname '#{name}'/) end it "does not change the hostname if already set" do comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 0) cap.change_host_name(machine, name) expect(comm.received_commands.size).to eq(1) end end end vagrant-2.0.2/test/unit/plugins/guests/pld/cap/flavor_test.rb000066400000000000000000000006541323370221500243100ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestPld::Cap::Flavor" do let(:caps) do VagrantPlugins::GuestPld::Plugin .components .guest_capabilities[:pld] end let(:machine) { double("machine") } describe ".flavor" do let(:cap) { caps.get(:flavor) } let(:name) { "banana-rama.example.com" } it "is pld" do expect(cap.flavor(machine)).to be(:pld) end end end vagrant-2.0.2/test/unit/plugins/guests/pld/cap/network_scripts_dir_test.rb000066400000000000000000000010131323370221500271030ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestPld::Cap::NetworkScriptsDir" do let(:caps) do VagrantPlugins::GuestPld::Plugin .components .guest_capabilities[:pld] end let(:machine) { double("machine") } describe ".network_scripts_dir" do let(:cap) { caps.get(:network_scripts_dir) } let(:name) { "banana-rama.example.com" } it "is /etc/sysconfig/interfaces" do expect(cap.network_scripts_dir(machine)).to eq("/etc/sysconfig/interfaces") end end end vagrant-2.0.2/test/unit/plugins/guests/redhat/000077500000000000000000000000001323370221500213535ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/redhat/cap/000077500000000000000000000000001323370221500221165ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/redhat/cap/change_host_name_test.rb000066400000000000000000000026151323370221500267700ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestRedHat::Cap::ChangeHostName" do let(:caps) do VagrantPlugins::GuestRedHat::Plugin .components .guest_capabilities[:redhat] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".change_host_name" do let(:cap) { caps.get(:change_host_name) } let(:name) { "banana-rama.example.com" } it "sets the hostname" 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(/\/etc\/sysconfig\/network-scripts\/ifcfg/) 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/) end it "does not change the hostname if already set" do comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 0) cap.change_host_name(machine, name) expect(comm.received_commands.size).to eq(1) end end end vagrant-2.0.2/test/unit/plugins/guests/redhat/cap/configure_networks_test.rb000066400000000000000000000151221323370221500274200ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestRedHat::Cap::ConfigureNetworks" do let(:caps) do VagrantPlugins::GuestRedHat::Plugin .components .guest_capabilities[:redhat] end let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } let(:config) { double("config", vm: vm) } let(:guest) { double("guest") } let(:machine) { double("machine", guest: guest, config: config) } let(:networks){ [[{}], [{}]] } let(:vm){ double("vm", networks: networks) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".configure_networks" do let(:cap) { caps.get(:configure_networks) } before do allow(guest).to receive(:capability) .with(:flavor) .and_return(:rhel) allow(guest).to receive(:capability) .with(:network_scripts_dir) .and_return("/scripts") allow(guest).to receive(:capability) .with(:network_interfaces) .and_return(["eth1", "eth2"]) end let(:network_1) do { interface: 0, type: "dhcp", } end let(:network_2) do { interface: 1, type: "static", ip: "33.33.33.10", netmask: "255.255.0.0", gateway: "33.33.0.1", } end context "with NetworkManager installed" do before do allow(cap).to receive(:nmcli?).and_return true end context "with devices managed by NetworkManager" do before do allow(cap).to receive(:nm_controlled?).and_return true end context "with nm_controlled option omitted" do it "creates and starts the networks via nmcli" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/nmcli/) expect(comm.received_commands[0]).to_not match(/(ifdown|ifup)/) end end context "with nm_controlled option set to true" do let(:networks){ [[{nm_controlled: true}], [{nm_controlled: true}]] } it "creates and starts the networks via nmcli" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/nmcli/) expect(comm.received_commands[0]).to_not match(/(ifdown|ifup)/) end end context "with nm_controlled option set to false" do let(:networks){ [[{nm_controlled: false}], [{nm_controlled: false}]] } it "creates and starts the networks via ifup and disables devices in NetworkManager" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/nmcli.*disconnect/) expect(comm.received_commands[0]).to match(/ifup/) expect(comm.received_commands[0]).to_not match(/ifdown/) end end context "with nm_controlled option set to false on first device" do let(:networks){ [[{nm_controlled: false}], [{nm_controlled: true}]] } it "creates and starts the networks with one managed manually and one NetworkManager controlled" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/nmcli.*disconnect.*eth1/) expect(comm.received_commands[0]).to match(/ifup.*eth1/) expect(comm.received_commands[0]).to_not match(/ifdown/) end end end context "with devices not managed by NetworkManager" do before do allow(cap).to receive(:nm_controlled?).and_return false end context "with nm_controlled option omitted" do it "creates and starts the networks manually" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/ifdown/) expect(comm.received_commands[0]).to match(/ifup/) expect(comm.received_commands[0]).to_not match(/nmcli c up/) expect(comm.received_commands[0]).to_not match(/nmcli d disconnect/) end end context "with nm_controlled option set to true" do let(:networks){ [[{nm_controlled: true}], [{nm_controlled: true}]] } it "creates and starts the networks via nmcli" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/NetworkManager/) expect(comm.received_commands[0]).to match(/ifdown/) expect(comm.received_commands[0]).to_not match(/ifup/) end end context "with nm_controlled option set to false" do let(:networks){ [[{nm_controlled: false}], [{nm_controlled: false}]] } it "creates and starts the networks via ifup " do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/ifup/) expect(comm.received_commands[0]).to match(/ifdown/) expect(comm.received_commands[0]).to_not match(/nmcli c up/) expect(comm.received_commands[0]).to_not match(/nmcli d disconnect/) end end context "with nm_controlled option set to false on first device" do let(:networks){ [[{nm_controlled: false}], [{nm_controlled: true}]] } it "creates and starts the networks with one managed manually and one NetworkManager controlled" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to_not match(/nmcli.*disconnect/) expect(comm.received_commands[0]).to match(/ifdown/) expect(comm.received_commands[0]).to match(/ifup.*eth1/) end end end end context "without NetworkManager installed" do before do allow(cap).to receive(:nmcli?).and_return false end context "with nm_controlled option omitted" do it "creates and starts the networks manually" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/ifdown/) expect(comm.received_commands[0]).to match(/ifup/) expect(comm.received_commands[0]).to_not match(/nmcli/) end end context "with nm_controlled option set" do let(:networks){ [[{nm_controlled: false}], [{nm_controlled: true}]] } it "raises an error" do expect{ cap.configure_networks(machine, [network_1, network_2]) }.to raise_error(Vagrant::Errors::NetworkManagerNotInstalled) end end end end end vagrant-2.0.2/test/unit/plugins/guests/redhat/cap/flavor_test.rb000066400000000000000000000020001323370221500247630ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestRedHat::Cap::Flavor" do let(:caps) do VagrantPlugins::GuestRedHat::Plugin .components .guest_capabilities[:redhat] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".flavor" do let(:cap) { caps.get(:flavor) } { "CentOS Linux 2.4 release 7" => :rhel_7, "Red Hat Enterprise Linux release 7" => :rhel_7, "Scientific Linux release 7" => :rhel_7, "CloudLinux release 7.2 (Valeri Kubasov)" => :rhel_7, "CentOS" => :rhel, "RHEL 6" => :rhel, "banana" => :rhel, }.each do |str, expected| it "returns #{expected} for #{str}" do comm.stub_command("cat /etc/redhat-release", stdout: str) expect(cap.flavor(machine)).to be(expected) end end end end vagrant-2.0.2/test/unit/plugins/guests/redhat/cap/network_scripts_dir_test.rb000066400000000000000000000010361323370221500276000ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestRedHat::Cap::NetworkScriptsDir" do let(:caps) do VagrantPlugins::GuestRedHat::Plugin .components .guest_capabilities[:redhat] end let(:machine) { double("machine") } describe ".network_scripts_dir" do let(:cap) { caps.get(:network_scripts_dir) } let(:name) { "banana-rama.example.com" } it "is /etc/sysconfig/network-scripts" do expect(cap.network_scripts_dir(machine)).to eq("/etc/sysconfig/network-scripts") end end end vagrant-2.0.2/test/unit/plugins/guests/redhat/cap/nfs_client_test.rb000066400000000000000000000014421323370221500256270ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestRedHat::Cap:NFSClient" do let(:caps) do VagrantPlugins::GuestRedHat::Plugin .components .guest_capabilities[:redhat] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".nfs_client_install" do let(:cap) { caps.get(:nfs_client_install) } it "installs rsync" do cap.nfs_client_install(machine) expect(comm.received_commands[0]).to match(/install nfs-utils nfs-utils-lib portmap/) expect(comm.received_commands[0]).to match(/\/bin\/systemctl restart rpcbind nfs/) end end end vagrant-2.0.2/test/unit/plugins/guests/redhat/cap/rsync_test.rb000066400000000000000000000012341323370221500246400ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestRedHat::Cap:RSync" do let(:caps) do VagrantPlugins::GuestRedHat::Plugin .components .guest_capabilities[:redhat] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".rsync_install" do let(:cap) { caps.get(:rsync_install) } it "installs rsync" do cap.rsync_install(machine) expect(comm.received_commands[0]).to match(/install rsync/) end end end vagrant-2.0.2/test/unit/plugins/guests/slackware/000077500000000000000000000000001323370221500220605ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/slackware/cap/000077500000000000000000000000001323370221500226235ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/slackware/cap/change_host_name_test.rb000066400000000000000000000022011323370221500274640ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestSlackware::Cap::ChangeHostName" do let(:caps) do VagrantPlugins::GuestSlackware::Plugin .components .guest_capabilities[:slackware] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".change_host_name" do let(:cap) { caps.get(:change_host_name) } let(:name) { "banana-rama.example.com" } it "sets the hostname" do comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 1) cap.change_host_name(machine, name) expect(comm.received_commands[1]).to match(/echo '#{name}' > \/etc\/hostname/) expect(comm.received_commands[1]).to match(/hostname -F \/etc\/hostname/) end it "does not change the hostname if already set" do comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 0) cap.change_host_name(machine, name) expect(comm.received_commands.size).to eq(1) end end end vagrant-2.0.2/test/unit/plugins/guests/slackware/cap/configure_networks_test.rb000066400000000000000000000022671323370221500301330ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestSlackware::Cap::ConfigureNetworks" do let(:caps) do VagrantPlugins::GuestSlackware::Plugin .components .guest_capabilities[:slackware] end let(:guest) { double("guest") } let(:machine) { double("machine", guest: guest) } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".configure_networks" do let(:cap) { caps.get(:configure_networks) } before do allow(guest).to receive(:capability).with(:network_interfaces) .and_return(["eth1", "eth2"]) end let(:network_1) do { interface: 0, type: "dhcp", } end let(:network_2) do { interface: 1, type: "static", ip: "33.33.33.10", netmask: "255.255.0.0", gateway: "33.33.0.1", } end it "creates and starts the networks" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/\/etc\/rc.d\/rc.inet1/) end end end vagrant-2.0.2/test/unit/plugins/guests/smartos/000077500000000000000000000000001323370221500215745ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/smartos/cap/000077500000000000000000000000001323370221500223375ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/smartos/cap/change_host_name_test.rb000066400000000000000000000027071323370221500272130ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestSmartos::Cap::ChangeHostName" do let(:caps) do VagrantPlugins::GuestSmartos::Plugin .components .guest_capabilities[:smartos] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } let(:config) { double("config", smartos: VagrantPlugins::GuestSmartos::Config.new) } before do allow(machine).to receive(:communicate).and_return(comm) allow(machine).to receive(:config).and_return(config) end after do comm.verify_expectations! end describe ".change_host_name" do let(:cap) { caps.get(:change_host_name) } it "changes the hostname if appropriate" do cap.change_host_name(machine, "testhost") expect(comm.received_commands[0]).to match(/if hostname | grep 'testhost' ; then/) expect(comm.received_commands[0]).to match(/exit 0/) expect(comm.received_commands[0]).to match(/fi/) expect(comm.received_commands[0]).to match(/if \[ -d \/usbkey \] && \[ "\$\(zonename\)" == "global" \] ; then/) expect(comm.received_commands[0]).to match(/pfexec sed -i '' 's\/hostname=\.\*\/hostname=testhost\/' \/usbkey\/config/) expect(comm.received_commands[0]).to match(/fi/) expect(comm.received_commands[0]).to match(/pfexec echo 'testhost' > \/etc\/nodename/) expect(comm.received_commands[0]).to match(/pfexec hostname testhost/) end end end vagrant-2.0.2/test/unit/plugins/guests/smartos/cap/configure_networks_test.rb000066400000000000000000000041471323370221500276460ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::VagrantPlugins::Cap::ConfigureNetworks" do let(:plugin) { VagrantPlugins::GuestSmartos::Plugin.components.guest_capabilities[:smartos].get(:configure_networks) } let(:machine) { double("machine") } let(:config) { double("config", smartos: VagrantPlugins::GuestSmartos::Config.new) } let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(communicator) allow(machine).to receive(:config).and_return(config) end after do communicator.verify_expectations! end describe ".configure_networks" do let(:interface) { "eth0" } let(:device) { "e1000g#{interface}" } describe 'dhcp' do let(:network) { {interface: interface, type: :dhcp} } it "plumbs the device" do communicator.expect_command(%Q(pfexec /sbin/ifconfig #{device} plumb)) plugin.configure_networks(machine, [network]) end it "starts dhcp for the device" do communicator.expect_command(%Q(pfexec /sbin/ifconfig #{device} dhcp start)) plugin.configure_networks(machine, [network]) end end describe 'static' do let(:network) { {interface: interface, type: :static, ip: '1.1.1.1', netmask: '255.255.255.0'} } it "plumbs the network" do communicator.expect_command(%Q(pfexec /sbin/ifconfig #{device} plumb)) plugin.configure_networks(machine, [network]) end it "starts sets netmask and IP for the device" do communicator.expect_command(%Q(pfexec /sbin/ifconfig #{device} inet 1.1.1.1 netmask 255.255.255.0)) plugin.configure_networks(machine, [network]) end it "starts enables the device" do communicator.expect_command(%Q(pfexec /sbin/ifconfig #{device} up)) plugin.configure_networks(machine, [network]) end it "starts writes out a hostname file" do communicator.expect_command(%Q(pfexec sh -c "echo '1.1.1.1' > /etc/hostname.#{device}")) plugin.configure_networks(machine, [network]) end end end end vagrant-2.0.2/test/unit/plugins/guests/smartos/cap/halt_test.rb000066400000000000000000000023071323370221500246550ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestSmartos::Cap::Halt" do let(:plugin) { VagrantPlugins::GuestSmartos::Plugin.components.guest_capabilities[:smartos].get(:halt) } let(:machine) { double("machine") } let(:config) { double("config", smartos: double("smartos", suexec_cmd: 'pfexec')) } let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } let(:shutdown_command){ "pfexec /usr/sbin/poweroff" } before do allow(machine).to receive(:communicate).and_return(communicator) allow(machine).to receive(:config).and_return(config) end after do communicator.verify_expectations! end describe ".halt" do it "sends a shutdown signal" do communicator.expect_command(shutdown_command) plugin.halt(machine) end it "ignores an IOError" do communicator.stub_command(shutdown_command, raise: IOError) expect { plugin.halt(machine) }.to_not raise_error end it "ignores a Vagrant::Errors::SSHDisconnected" do communicator.stub_command(shutdown_command, raise: Vagrant::Errors::SSHDisconnected) expect { plugin.halt(machine) }.to_not raise_error end end end vagrant-2.0.2/test/unit/plugins/guests/smartos/cap/insert_public_key_test.rb000066400000000000000000000026551323370221500274450ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestSmartos::Cap::InsertPublicKey" do let(:caps) do VagrantPlugins::GuestSmartos::Plugin .components .guest_capabilities[:smartos] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".insert_public_key" do let(:cap) { caps.get(:insert_public_key) } it "inserts the public key" do cap.insert_public_key(machine, "ssh-rsa ...") expect(comm.received_commands[0]).to match(/if \[ -d \/usbkey \] && \[ "\$\(zonename\)" == "global" \] ; then/) expect(comm.received_commands[0]).to match(/printf 'ssh-rsa ...\\n' >> \/usbkey\/config.inc\/authorized_keys/) expect(comm.received_commands[0]).to match(/cp \/usbkey\/config.inc\/authorized_keys ~\/.ssh\/authorized_keys/) expect(comm.received_commands[0]).to match(/else/) expect(comm.received_commands[0]).to match(/mkdir -p ~\/.ssh/) expect(comm.received_commands[0]).to match(/chmod 0700 ~\/.ssh/) expect(comm.received_commands[0]).to match(/printf 'ssh-rsa ...\\n' >> ~\/.ssh\/authorized_keys/) expect(comm.received_commands[0]).to match(/chmod 0600 ~\/.ssh\/authorized_keys/) expect(comm.received_commands[0]).to match(/fi/) end end end vagrant-2.0.2/test/unit/plugins/guests/smartos/cap/mount_nfs_test.rb000066400000000000000000000027731323370221500257440ustar00rootroot00000000000000require_relative "../../../../base" require_relative "../../../../../../plugins/guests/smartos/config" describe "VagrantPlugins::GuestSmartos::Cap::MountNFS" do let(:caps) do VagrantPlugins::GuestSmartos::Plugin .components .guest_capabilities[:smartos] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } let(:config) { double("config", smartos: VagrantPlugins::GuestSmartos::Config.new) } before do allow(machine).to receive(:communicate).and_return(comm) allow(machine).to receive(:config).and_return(config) end after do comm.verify_expectations! end describe ".mount_nfs_folder" do let(:cap) { caps.get(:mount_nfs_folder) } it "mounts the folder" do cap.mount_nfs_folder(machine, '1.1.1.1', {'nfs' => {guestpath: '/mountpoint', hostpath: '/some/share'}}) expect(comm.received_commands[0]).to match(/if \[ -d \/usbkey \] && \[ "\$\(zonename\)" == "global" \] ; then/) expect(comm.received_commands[0]).to match(/pfexec mkdir -p \/usbkey\/config.inc/) expect(comm.received_commands[0]).to match(/printf '1\.1\.1\.1:\/some\/share:\/mountpoint' | pfexec tee -a \/usbkey\/config.inc\/nfs_mounts/) expect(comm.received_commands[0]).to match(/fi/) expect(comm.received_commands[0]).to match(/pfexec mkdir -p \/mountpoint/) expect(comm.received_commands[0]).to match(/pfexec \/usr\/sbin\/mount -F nfs '1\.1\.1\.1:\/some\/share' '\/mountpoint'/) end end end vagrant-2.0.2/test/unit/plugins/guests/smartos/cap/remove_public_key_test.rb000066400000000000000000000023341323370221500274300ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestSmartos::Cap::RemovePublicKey" do let(:caps) do VagrantPlugins::GuestSmartos::Plugin .components .guest_capabilities[:smartos] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".remove_public_key" do let(:cap) { caps.get(:remove_public_key) } it "removes the public key" do cap.remove_public_key(machine, "ssh-rsa keyvalue comment") expect(comm.received_commands[0]).to match(/if test -f \/usbkey\/config.inc\/authorized_keys ; then/) expect(comm.received_commands[0]).to match(/sed -i '' '\/\^.*ssh-rsa keyvalue comment.*\$\/d' \/usbkey\/config.inc\/authorized_keys/) expect(comm.received_commands[0]).to match(/fi/) expect(comm.received_commands[0]).to match(/if test -f ~\/.ssh\/authorized_keys ; then/) expect(comm.received_commands[0]).to match(/sed -i '' '\/\^.*ssh-rsa keyvalue comment.*\$\/d' ~\/.ssh\/authorized_keys/) expect(comm.received_commands[0]).to match(/fi/) end end end vagrant-2.0.2/test/unit/plugins/guests/smartos/cap/rsync_test.rb000066400000000000000000000032371323370221500250660ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::VagrantPlugins::Cap::Rsync" do let(:plugin) { VagrantPlugins::GuestSmartos::Plugin.components.guest_capabilities[:smartos].get(:rsync_installed) } let(:machine) { double("machine") } let(:config) { double("config", smartos: VagrantPlugins::GuestSmartos::Config.new) } let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(communicator) allow(machine).to receive(:config).and_return(config) end after do communicator.verify_expectations! end describe ".rsync_installed" do describe "when rsync is in the path" do it "is true" do communicator.stub_command("which rsync", stdout: '/usr/bin/rsync', exit_code: 0) expect(plugin.rsync_installed(machine)).to be true end end describe "when rsync is not in the path" do it "is false" do communicator.stub_command("which rsync", stdout: '', exit_code: 1) expect(plugin.rsync_installed(machine)).to be false end end end describe ".rsync_pre" do it 'makes the guestpath directory with pfexec' do communicator.expect_command("pfexec mkdir -p '/sync_dir'") plugin.rsync_pre(machine, guestpath: '/sync_dir') end end describe ".rsync_post" do it 'chowns incorrectly owned files in sync dir' do communicator.expect_command("pfexec find /sync_dir '!' -type l -a '(' ! -user somebody -or ! -group somegroup ')' -exec chown somebody:somegroup '{}' +") plugin.rsync_post(machine, guestpath: '/sync_dir', owner: 'somebody', group: 'somegroup') end end end vagrant-2.0.2/test/unit/plugins/guests/solaris/000077500000000000000000000000001323370221500215605ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/solaris/cap/000077500000000000000000000000001323370221500223235ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/solaris/cap/halt_test.rb000066400000000000000000000022011323370221500246320ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestSolaris::Cap::Halt" do let(:caps) do VagrantPlugins::GuestSolaris::Plugin .components .guest_capabilities[:solaris] end let(:shutdown_command){ "sudo /usr/sbin/shutdown -y -i5 -g0" } let(:machine) { double("machine", config: double("config", solaris: double("solaris", suexec_cmd: 'sudo'))) } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".halt" do let(:cap) { caps.get(:halt) } it "runs the shutdown command" do comm.expect_command(shutdown_command) cap.halt(machine) end it "ignores an IOError" do comm.stub_command(shutdown_command, raise: IOError) expect { cap.halt(machine) }.to_not raise_error end it "ignores a Vagrant::Errors::SSHDisconnected" do comm.stub_command(shutdown_command, raise: Vagrant::Errors::SSHDisconnected) expect { cap.halt(machine) }.to_not raise_error end end end vagrant-2.0.2/test/unit/plugins/guests/solaris11/000077500000000000000000000000001323370221500217225ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/solaris11/cap/000077500000000000000000000000001323370221500224655ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/solaris11/cap/change_host_name_test.rb000066400000000000000000000026011323370221500273320ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestSolaris11::Cap::ChangeHostName" do let(:caps) do VagrantPlugins::GuestSolaris11::Plugin .components .guest_capabilities[:solaris11] end let(:machine) { double("machine", config: double("config", solaris11: double("solaris11", suexec_cmd: 'sudo'))) } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".change_host_name" do let(:cap) { caps.get(:change_host_name) } let(:name) { "solaris11.domain.com" } it "changes the hostname" do allow(machine.communicate).to receive(:test).and_return(false) allow(machine.communicate).to receive(:execute) expect(machine.communicate).to receive(:execute).with("sudo /usr/sbin/svccfg -s system/identity:node setprop config/nodename=\"#{name}\"") expect(machine.communicate).to receive(:execute).with("sudo /usr/sbin/svccfg -s system/identity:node setprop config/loopback=\"#{name}\"") expect(machine.communicate).to receive(:execute).with("sudo /usr/sbin/svccfg -s system/identity:node refresh ") expect(machine.communicate).to receive(:execute).with("sudo /usr/sbin/svcadm restart system/identity:node ") cap.change_host_name(machine, name) end end end vagrant-2.0.2/test/unit/plugins/guests/solaris11/cap/configure_networks_test.rb000066400000000000000000000031151323370221500277660ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestSolaris11::Cap::ConfigureNetworks" do let(:caps) do VagrantPlugins::GuestSolaris11::Plugin .components .guest_capabilities[:solaris11] end let(:machine) { double("machine", config: double("config", solaris11: double("solaris11", suexec_cmd: 'sudo', device: 'net'))) } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".configufre_networks" do let(:cap) { caps.get(:configure_networks) } let(:network_1) do { interface: 0, type: "dhcp", } end let(:network_2) do { interface: 1, type: "static", ip: "33.33.33.10", netmask: "255.255.0.0", gateway: "33.33.0.1", } end let(:networks) { [network_1, network_2] } it "configures the guests network if static" do allow(machine.communicate).to receive(:test).and_return(true) cap.configure_networks(machine, networks) expect(comm.received_commands[1]).to eq("sudo ipadm delete-addr net1/v4") expect(comm.received_commands[2]).to eq("sudo ipadm create-addr -T static -a 33.33.33.10/16 net1/v4") end it "configures the guests network if dhcp" do allow(machine.communicate).to receive(:test).and_return(true) cap.configure_networks(machine, networks) expect(comm.received_commands[0]).to eq("sudo ipadm create-addr -T addrconf net0/v4") end end end vagrant-2.0.2/test/unit/plugins/guests/suse/000077500000000000000000000000001323370221500210635ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/suse/cap/000077500000000000000000000000001323370221500216265ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/suse/cap/change_host_name_test.rb000066400000000000000000000022261323370221500264760ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestSUSE::Cap::ChangeHostName" do let(:caps) do VagrantPlugins::GuestSUSE::Plugin .components .guest_capabilities[:suse] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".change_host_name" do let(:cap) { caps.get(:change_host_name) } let(:name) { "banana-rama.example.com" } let(:basename) { "banana-rama" } it "sets the hostname" do comm.stub_command("hostname -f | grep '^#{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}'/) end it "does not change the hostname if already set" do comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 0) cap.change_host_name(machine, name) expect(comm.received_commands.size).to eq(1) end end end vagrant-2.0.2/test/unit/plugins/guests/suse/cap/configure_networks_test.rb000066400000000000000000000027501323370221500271330ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestSUSE::Cap::ConfigureNetworks" do let(:caps) do VagrantPlugins::GuestSUSE::Plugin .components .guest_capabilities[:suse] end let(:guest) { double("guest") } let(:machine) { double("machine", guest: guest) } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".configure_networks" do let(:cap) { caps.get(:configure_networks) } before do allow(guest).to receive(:capability).with(:network_scripts_dir) .and_return("/scripts") allow(guest).to receive(:capability).with(:network_interfaces) .and_return(["eth1", "eth2"]) end let(:network_1) do { interface: 0, type: "dhcp", } end let(:network_2) do { interface: 1, type: "static", ip: "33.33.33.10", netmask: "255.255.0.0", gateway: "33.33.0.1", } end it "creates and starts the networks" do cap.configure_networks(machine, [network_1, network_2]) expect(comm.received_commands[0]).to match(/\/sbin\/ifdown 'eth1'/) expect(comm.received_commands[0]).to match(/\/sbin\/ifup 'eth1'/) expect(comm.received_commands[0]).to match(/\/sbin\/ifdown 'eth2'/) expect(comm.received_commands[0]).to match(/\/sbin\/ifup 'eth2'/) end end end vagrant-2.0.2/test/unit/plugins/guests/suse/cap/halt_test.rb000066400000000000000000000017751323370221500241540ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestSUSE::Cap::Halt" do let(:caps) do VagrantPlugins::GuestSUSE::Plugin .components .guest_capabilities[:suse] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".halt" do let(:cap) { caps.get(:halt) } it "runs the shutdown command" do comm.expect_command("/sbin/shutdown -h now") cap.halt(machine) end it "does not raise an IOError" do comm.stub_command("shutdown -h now", raise: IOError) expect { cap.halt(machine) }.to_not raise_error end it "ignores a Vagrant::Errors::SSHDisconnected" do comm.stub_command("shutdown -h now", raise: Vagrant::Errors::SSHDisconnected) expect { cap.halt(machine) }.to_not raise_error end end end vagrant-2.0.2/test/unit/plugins/guests/suse/cap/network_scripts_dir_test.rb000066400000000000000000000007341323370221500273140ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestSUSE::Cap::NetworkScriptsDir" do let(:caps) do VagrantPlugins::GuestSUSE::Plugin .components .guest_capabilities[:suse] end let(:machine) { double("machine") } describe ".network_scripts_dir" do let(:cap) { caps.get(:network_scripts_dir) } it "runs /etc/sysconfig/network" do expect(cap.network_scripts_dir(machine)).to eq("/etc/sysconfig/network") end end end vagrant-2.0.2/test/unit/plugins/guests/suse/cap/nfs_client_test.rb000066400000000000000000000015341323370221500253410ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestSUSE::Cap::NFSClient" do let(:caps) do VagrantPlugins::GuestSUSE::Plugin .components .guest_capabilities[:suse] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".nfs_client_install" do let(:cap) { caps.get(:nfs_client_install) } 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/) end end end vagrant-2.0.2/test/unit/plugins/guests/suse/cap/rsync_test.rb000066400000000000000000000015501323370221500243510ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestSUSE::Cap::RSync" do let(:caps) do VagrantPlugins::GuestSUSE::Plugin .components .guest_capabilities[:suse] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".rsync_install" do let(:cap) { caps.get(:rsync_install) } it "installs rsync" do comm.expect_command("zypper -n install rsync") cap.rsync_install(machine) end end describe ".rsync_installed" do let(:cap) { caps.get(:rsync_installed) } it "checks if rsync is installed" do comm.expect_command("test -f /usr/bin/rsync") cap.rsync_installed(machine) end end end vagrant-2.0.2/test/unit/plugins/guests/tinycore/000077500000000000000000000000001323370221500217405ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/tinycore/cap/000077500000000000000000000000001323370221500225035ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/tinycore/cap/change_host_name_test.rb000066400000000000000000000016051323370221500273530ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestTinyCore::Cap::ChangeHostName" do let(:described_class) do VagrantPlugins::GuestTinyCore::Plugin.components.guest_capabilities[:tinycore].get(:change_host_name) end let(:machine) { double("machine") } let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } let(:old_hostname) { 'boot2docker' } 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 "refreshes the hostname service with the sethostname command" do communicator.expect_command(%q(/usr/bin/sethostname newhostname.newdomain.tld)) described_class.change_host_name(machine, 'newhostname.newdomain.tld') end end end vagrant-2.0.2/test/unit/plugins/guests/tinycore/cap/halt_test.rb000066400000000000000000000020401323370221500250130ustar00rootroot00000000000000require_relative "../../../../base" describe "VagrantPlugins::GuestTinyCore::Cap::Halt" do let(:caps) do VagrantPlugins::GuestTinyCore::Plugin .components .guest_capabilities[:tinycore] end let(:shutdown_command){ "poweroff" } let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do allow(machine).to receive(:communicate).and_return(comm) end after do comm.verify_expectations! end describe ".halt" do let(:cap) { caps.get(:halt) } it "runs the shutdown command" do comm.expect_command(shutdown_command) cap.halt(machine) end it "ignores an IOError" do comm.stub_command(shutdown_command, raise: IOError) expect { cap.halt(machine) }.to_not raise_error end it "ignores a Vagrant::Errors::SSHDisconnected" do comm.stub_command(shutdown_command, raise: Vagrant::Errors::SSHDisconnected) expect { cap.halt(machine) }.to_not raise_error end end end vagrant-2.0.2/test/unit/plugins/guests/windows/000077500000000000000000000000001323370221500215765ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/windows/cap/000077500000000000000000000000001323370221500223415ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/guests/windows/cap/change_host_name_test.rb000066400000000000000000000023511323370221500272100ustar00rootroot00000000000000require_relative "../../../../base" require Vagrant.source_root.join("plugins/guests/windows/cap/change_host_name") describe "VagrantPlugins::GuestWindows::Cap::ChangeHostName" do let(:described_class) do VagrantPlugins::GuestWindows::Plugin.components.guest_capabilities[:windows].get(:change_host_name) 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 describe ".change_host_name" do let(:rename_script) { <<-EOH $computer = Get-WmiObject -Class Win32_ComputerSystem $retval = $computer.rename("newhostname").returnvalue if ($retval -eq 0) { shutdown /r /t 5 /f /d p:4:1 /c "Vagrant Rename Computer" } exit $retval EOH } it "changes the hostname" do communicator.stub_command( 'if (!([System.Net.Dns]::GetHostName() -eq \'newhostname\')) { exit 0 } exit 1', exit_code: 0) communicator.stub_command(rename_script, exit_code: 0) described_class.change_host_name_and_wait(machine, 'newhostname', 0) end end end vagrant-2.0.2/test/unit/plugins/guests/windows/cap/halt_test.rb000066400000000000000000000016021323370221500246540ustar00rootroot00000000000000require_relative "../../../../base" require Vagrant.source_root.join("plugins/guests/windows/cap/halt") describe "VagrantPlugins::GuestWindows::Cap::Halt" do let(:described_class) do VagrantPlugins::GuestWindows::Plugin.components.guest_capabilities[:windows].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 describe ".halt" do it "cancels any existing scheduled shut down" do communicator.expect_command("shutdown -a") described_class.halt(machine) end it "shuts down immediately" do communicator.expect_command('shutdown /s /t 1 /c "Vagrant Halt" /f /d p:4:1') described_class.halt(machine) end end end vagrant-2.0.2/test/unit/plugins/guests/windows/cap/insert_public_key_test.rb000066400000000000000000000052271323370221500274450ustar00rootroot00000000000000require "tempfile" require_relative "../../../../base" require_relative "../../../../../../plugins/communicators/winssh/communicator" describe "VagrantPlugins::GuestWindows::Cap::InsertPublicKey" do let(:caps) do VagrantPlugins::GuestWindows::Plugin .components .guest_capabilities[:windows] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } let(:auth_keys_check_result){ 1 } before do @tempfile = Tempfile.new("vagrant-test") allow(Tempfile).to receive(:new).and_return(@tempfile) allow(comm).to receive(:is_a?).and_return(true) allow(machine).to receive(:communicate).and_return(comm) allow(comm).to receive(:execute).with(/echo .+/, shell: "cmd").and_yield(:stdout, "TEMP\r\nHOME\r\n") allow(comm).to receive(:execute).with(/dir .+\.ssh/, shell: "cmd") allow(comm).to receive(:execute).with(/dir .+authorized_keys/, shell: "cmd", error_check: false).and_return(auth_keys_check_result) end after do @tempfile.delete end describe ".insert_public_key" do let(:cap) { caps.get(:insert_public_key) } context "when authorized_keys exists on guest" do let(:auth_keys_check_result){ 0 } before do expect(@tempfile).to receive(:delete).and_return(true) expect(@tempfile).to receive(:delete).and_call_original end it "inserts the public key" do expect(comm).to receive(:download) expect(comm).to receive(:upload) expect(comm).to receive(:execute).with(/Set-Acl .*/, shell: "powershell") cap.insert_public_key(machine, "ssh-rsa ...") expect(File.read(@tempfile.path)).to include("ssh-rsa ...") end end context "when authorized_keys does not exist on guest" do before do expect(@tempfile).to receive(:delete).and_return(true) expect(@tempfile).to receive(:delete).and_call_original end it "inserts the public key" do expect(comm).to_not receive(:download) expect(comm).to receive(:upload) expect(comm).to receive(:execute).with(/Set-Acl .*/, shell: "powershell") cap.insert_public_key(machine, "ssh-rsa ...") expect(File.read(@tempfile.path)).to include("ssh-rsa ...") end end context "when required directories cannot be fetched from the guest" do before do expect(comm).to receive(:execute).with(/echo .+/, shell: "cmd").and_yield(:stdout, "TEMP\r\n") end it "should raise an error" do expect{ cap.insert_public_key(machine, "ssh-rsa ...") }.to raise_error(VagrantPlugins::GuestWindows::Errors::PublicKeyDirectoryFailure) end end end end vagrant-2.0.2/test/unit/plugins/guests/windows/cap/mount_shared_folder_test.rb000066400000000000000000000105131323370221500277500ustar00rootroot00000000000000require_relative "../../../../base" require Vagrant.source_root.join("plugins/guests/windows/cap/mount_shared_folder") describe "VagrantPlugins::GuestWindows::Cap::MountSharedFolder" do let(:machine) { double("machine") } let(:communicator) { double(:execute) } let(:config) { double("config") } let(:vm) { double("vm") } before do allow(machine).to receive(:communicate).and_return(communicator) allow(communicator).to receive(:execute) allow(machine).to receive(:config).and_return(config) allow(config).to receive(:vm).and_return(vm) allow(vm).to receive(:communicator).and_return(:winrm) end describe "virtualbox" do let(:described_class) do VagrantPlugins::GuestWindows::Plugin.components.guest_capabilities[:windows].get(:mount_virtualbox_shared_folder) end describe ".mount_shared_folder" do it "should call mount_volume script with correct args" do expect(Vagrant::Util::TemplateRenderer).to receive(:render).with( /.+scripts\/mount_volume.ps1/, options: { mount_point: "guestpath", share_name: "name", vm_provider_unc_path: "\\\\vboxsrv\\name", }) described_class.mount_virtualbox_shared_folder(machine, 'name', 'guestpath', {}) end it "should replace invalid Windows share chars" do expect(Vagrant::Util::TemplateRenderer).to receive(:render).with( kind_of(String), options: { mount_point: kind_of(String), share_name: "invalid-windows_sharename", vm_provider_unc_path: "\\\\vboxsrv\\invalid-windows_sharename", }) described_class.mount_virtualbox_shared_folder(machine, "/invalid-windows/sharename", "guestpath", {}) end end end describe "vmware" do let(:described_class) do VagrantPlugins::GuestWindows::Plugin.components.guest_capabilities[:windows].get(:mount_vmware_shared_folder) end describe ".mount_shared_folder" do it "should call mount_volume script with correct args" do expect(Vagrant::Util::TemplateRenderer).to receive(:render).with( /.+scripts\/mount_volume.ps1/, options: { mount_point: "guestpath", share_name: "name", vm_provider_unc_path: "\\\\vmware-host\\Shared Folders\\name", }) described_class.mount_vmware_shared_folder(machine, 'name', 'guestpath', {}) end end end describe "parallels" do let(:described_class) do VagrantPlugins::GuestWindows::Plugin.components.guest_capabilities[:windows].get(:mount_parallels_shared_folder) end describe ".mount_shared_folder" do it "should call mount_volume script with correct args" do expect(Vagrant::Util::TemplateRenderer).to receive(:render).with( /.+scripts\/mount_volume.ps1/, options: { mount_point: "guestpath", share_name: "name", vm_provider_unc_path: "\\\\psf\\name", }) described_class.mount_parallels_shared_folder(machine, 'name', 'guestpath', {}) end end end describe "smb" do let(:described_class) do VagrantPlugins::GuestWindows::Plugin.components.guest_capabilities[:windows].get(:mount_smb_shared_folder) end describe ".mount_shared_folder" do it "should call mount_volume script with correct args" do expect(Vagrant::Util::TemplateRenderer).to receive(:render).with( /.+scripts\/mount_volume.ps1/, options: { mount_point: "guestpath", share_name: "name", vm_provider_unc_path: "\\\\host\\name", }) described_class.mount_smb_shared_folder(machine, 'name', 'guestpath', {:smb_username => "user", :smb_password => "pass", :smb_host => "host"}) end end end describe "virtualbox-ssh" do let(:described_class) do VagrantPlugins::GuestWindows::Plugin.components.guest_capabilities[:windows].get(:mount_virtualbox_shared_folder) end before do allow(vm).to receive(:communicator).and_return(:ssh) end describe ".mount_shared_folder" do it "should call mount_volume script via ssh" do expect(communicator).to receive(:execute).with(/powershell/, shell: "sh") described_class.mount_virtualbox_shared_folder(machine, 'name', 'guestpath', {}) end end end end vagrant-2.0.2/test/unit/plugins/guests/windows/cap/reboot_test.rb000066400000000000000000000032171323370221500252220ustar00rootroot00000000000000require_relative "../../../../base" require Vagrant.source_root.join("plugins/guests/windows/cap/reboot") describe "VagrantPlugins::GuestWindows::Cap::Reboot" do let(:described_class) do VagrantPlugins::GuestWindows::Plugin.components.guest_capabilities[:windows].get(:wait_for_reboot) end let(:vm) { double("vm") } let(:config) { double("config") } let(:machine) { double("machine") } let(:communicator) { double("communicator") } before do allow(machine).to receive(:communicate).and_return(communicator) allow(machine).to receive(:config).and_return(config) allow(config).to receive(:vm).and_return(vm) end describe "winrm communicator" do before do allow(vm).to receive(:communicator).and_return(:winrm) end describe ".wait_for_reboot" do it "runs reboot detect script" do expect(communicator).to receive(:execute).with(/# Function/, { error_check: false }).and_return(0) allow(communicator).to receive(:execute) described_class.wait_for_reboot(machine) end it "fixes symlinks to network shares" do allow(communicator).to receive(:execute).and_return(0) expect(communicator).to receive(:execute).with('net use', { error_check: false }) described_class.wait_for_reboot(machine) end end end describe "ssh communicator" do before do allow(vm).to receive(:communicator).and_return(:ssh) end describe ".wait_for_reboot" do it "does not execute Windows reboot detect script" do expect(communicator).to_not receive(:execute) described_class.wait_for_reboot(machine) end end end end vagrant-2.0.2/test/unit/plugins/guests/windows/cap/remove_public_key_test.rb000066400000000000000000000045201323370221500274310ustar00rootroot00000000000000require "tempfile" require_relative "../../../../base" require_relative "../../../../../../plugins/communicators/winssh/communicator" describe "VagrantPlugins::GuestWindows::Cap::RemovePublicKey" do let(:caps) do VagrantPlugins::GuestWindows::Plugin .components .guest_capabilities[:windows] end let(:machine) { double("machine") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } let(:public_key_insecure){ "ssh-rsa...insecure" } let(:public_key_other){ "ssh-rsa...other" } let(:auth_keys_check_result){ 1 } before do @tempfile = Tempfile.new("vagrant-test") @tempfile.puts(public_key_insecure) @tempfile.puts(public_key_other) @tempfile.flush @tempfile.rewind allow(Tempfile).to receive(:new).and_return(@tempfile) allow(comm).to receive(:is_a?).and_return(true) allow(machine).to receive(:communicate).and_return(comm) allow(comm).to receive(:execute).with(/echo .+/, shell: "cmd").and_yield(:stdout, "TEMP\r\nHOME\r\n") allow(comm).to receive(:execute).with(/dir .+\.ssh/, shell: "cmd") allow(comm).to receive(:execute).with(/dir .+authorized_keys/, shell: "cmd", error_check: false).and_return(auth_keys_check_result) end after do @tempfile.delete end describe ".remove_public_key" do let(:cap) { caps.get(:remove_public_key) } context "when authorized_keys exists on guest" do let(:auth_keys_check_result){ 0 } before do expect(@tempfile).to receive(:delete).and_return(true) expect(@tempfile).to receive(:delete).and_call_original end it "removes the public key" do expect(comm).to receive(:download) expect(comm).to receive(:upload) expect(comm).to receive(:execute).with(/Set-Acl .*/, shell: "powershell") cap.remove_public_key(machine, public_key_insecure) expect(File.read(@tempfile.path)).to include(public_key_other) expect(File.read(@tempfile.path)).to_not include(public_key_insecure) end end context "when authorized_keys does not exist on guest" do it "does nothing" do expect(comm).to_not receive(:download) expect(comm).to receive(:upload) expect(comm).to receive(:execute).with(/Set-Acl .*/, shell: "powershell") cap.remove_public_key(machine, public_key_insecure) end end end end vagrant-2.0.2/test/unit/plugins/guests/windows/cap/rsync_test.rb000066400000000000000000000014141323370221500250630ustar00rootroot00000000000000require_relative "../../../../base" require Vagrant.source_root.join("plugins/guests/windows/cap/rsync") describe "VagrantPlugins::GuestWindows::Cap::RSync" do let(:described_class) do VagrantPlugins::GuestWindows::Plugin.components.guest_capabilities[:windows].get(:rsync_pre) 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 describe ".rsync_pre" do it 'makes the guestpath directory with mkdir' do communicator.expect_command("mkdir -p '/sync_dir'") described_class.rsync_pre(machine, guestpath: '/sync_dir') end end end vagrant-2.0.2/test/unit/plugins/guests/windows/config_test.rb000066400000000000000000000020561323370221500244320ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/guests/windows/config") describe VagrantPlugins::GuestWindows::Config do let(:machine) { double("machine") } subject { described_class.new } it "is valid by default" do subject.finalize! result = subject.validate(machine) expect(result["Windows Guest"]).to be_empty end describe "default values" do before { subject.finalize! } its("set_work_network") { should == false } end describe "attributes" do [:set_work_network].each do |attribute| it "should not default #{attribute} if overridden" do subject.send("#{attribute}=".to_sym, 10) subject.finalize! expect(subject.send(attribute)).to be(10) end it "should return error #{attribute} if nil" do subject.send("#{attribute}=".to_sym, nil) subject.finalize! result = subject.validate(machine) expect(result["Windows Guest"]).to include("windows.#{attribute} cannot be nil.") end end end end vagrant-2.0.2/test/unit/plugins/guests/windows/guest_network_test.rb000066400000000000000000000037541323370221500260730ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/guests/windows/guest_network") describe "VagrantPlugins::GuestWindows::GuestNetwork" do let(:communicator) { double("communicator") } let(:subject) { VagrantPlugins::GuestWindows::GuestNetwork.new(communicator) } describe ".is_dhcp_enabled" do it "should query the NIC by ordinal index" do expect(communicator).to receive(:test).with( /.+Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "Index=7 and DHCPEnabled=True"/). and_return(true) expect(subject.is_dhcp_enabled(7)).to be(true) end it "should return false for non-DHCP NICs" do expect(communicator).to receive(:test).with( /.+Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "Index=8 and DHCPEnabled=True"/). and_return(false) expect(subject.is_dhcp_enabled(8)).to be(false) end end describe ".configure_static_interface" do it "should configure IP using netsh" do expect(communicator).to receive(:execute).with( "netsh interface ip set address \"Local Area Connection 2\" static 192.168.33.10 255.255.255.0"). and_return(0) subject.configure_static_interface(7, "Local Area Connection 2", "192.168.33.10", "255.255.255.0") end end describe ".configure_dhcp_interface" do it "should configure DHCP when DHCP is disabled" do allow(communicator).to receive(:test).and_return(false) # is DHCP enabled? expect(communicator).to receive(:execute).with( "netsh interface ip set address \"Local Area Connection 2\" dhcp"). and_return(0) subject.configure_dhcp_interface(7, "Local Area Connection 2") end it "should not configure DHCP when DHCP is enabled" do allow(communicator).to receive(:test).and_return(true) # is DHCP enabled? expect(communicator).to_not receive(:execute) subject.configure_dhcp_interface(7, "Local Area Connection 2") end end end vagrant-2.0.2/test/unit/plugins/hosts/000077500000000000000000000000001323370221500177325ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/hosts/darwin/000077500000000000000000000000001323370221500212165ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/hosts/darwin/cap/000077500000000000000000000000001323370221500217615ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/hosts/darwin/cap/configured_ip_addresses_test.rb000066400000000000000000000020651323370221500302220ustar00rootroot00000000000000require_relative "../../../../base" require_relative "../../../../../../plugins/hosts/darwin/cap/configured_ip_addresses" describe VagrantPlugins::HostDarwin::Cap::ConfiguredIPAddresses do let(:subject){ VagrantPlugins::HostDarwin::Cap::ConfiguredIPAddresses } let(:interfaces){ ["192.168.1.2"] } before{ allow(Socket).to receive(:getifaddrs).and_return( interfaces.map{|i| double(:socket, addr: Addrinfo.ip(i))}) } it "should get list of available addresses" do expect(subject.configured_ip_addresses(nil)).to eq(["192.168.1.2"]) end context "with loopback address" do let(:interfaces){ ["192.168.1.2", "127.0.0.1"] } it "should not include loopback address" do expect(subject.configured_ip_addresses(nil)).not_to include(["127.0.0.1"]) end end context "with IPv6 address" do let(:interfaces){ ["192.168.1.2", "2001:200:dff:fff1:216:3eff:feb1:44d7"] } it "should not include IPv6 address" do expect(subject.configured_ip_addresses(nil)).not_to include(["2001:200:dff:fff1:216:3eff:feb1:44d7"]) end end end vagrant-2.0.2/test/unit/plugins/hosts/darwin/cap/rdp_test.rb000066400000000000000000000022151323370221500241320ustar00rootroot00000000000000require_relative "../../../../base" require_relative "../../../../../../plugins/hosts/darwin/cap/rdp" describe VagrantPlugins::HostDarwin::Cap::RDP do let(:rdp_info) do { host: "host", port: "port", username: "username", } end it "includes the default options" do path = described_class.generate_config_file(rdp_info) result = File.readlines(path).map(&:chomp) expect(result).to include("drivestoredirect:s:*") expect(result).to include("full address:s:host:port") expect(result).to include("prompt for credentials:i:1") expect(result).to include("username:s:username") end it "includes extra RDP arguments" do rdp_info.merge!(extra_args: ["screen mode id:i:0"]) path = described_class.generate_config_file(rdp_info) result = File.readlines(path).map(&:chomp) expect(result).to include("screen mode id:i:0") end it "opens the RDP file" do env = double(:env) allow(described_class).to receive(:generate_config_file).and_return("/path") expect(Vagrant::Util::Subprocess).to receive(:execute).with("open", "/path") described_class.rdp_client(env, rdp_info) end end vagrant-2.0.2/test/unit/plugins/hosts/darwin/cap/smb_test.rb000066400000000000000000000137241323370221500241350ustar00rootroot00000000000000require_relative "../../../../base" require_relative "../../../../../../plugins/hosts/darwin/cap/smb" describe VagrantPlugins::HostDarwin::Cap::SMB do include_context "unit" let(:subject){ VagrantPlugins::HostDarwin::Cap::SMB } let(:machine){ double(:machine) } let(:env){ double(:env) } let(:options){ {} } let(:result){ Vagrant::Util::Subprocess::Result } before{ allow(subject).to receive(:machine_id).and_return("CUSTOM_ID") } describe ".smb_installed" do it "is installed if sharing binary exists" do expect(File).to receive(:exist?).with("/usr/sbin/sharing").and_return(true) expect(subject.smb_installed(nil)).to be(true) end it "is not installed if sharing binary does not exist" do expect(File).to receive(:exist?).with("/usr/sbin/sharing").and_return(false) expect(subject.smb_installed(nil)).to be(false) end end describe ".smb_start" do before{ allow(Vagrant::Util::Subprocess).to receive(:execute) .and_return(result.new(0, "SMB-NT", "")) } it "should check for NT compatible password" do expect(Vagrant::Util::Subprocess).to receive(:execute).with("pwpolicy", "gethashtypes"). and_return(result.new(0, "SMB-NT", "")) subject.smb_start(env) end it "should raise error if NT compatible password is not set" do expect(Vagrant::Util::Subprocess).to receive(:execute).with("pwpolicy", "gethashtypes"). and_return(result.new(0, "", "")) expect{ subject.smb_start(env) }.to raise_error(VagrantPlugins::SyncedFolderSMB::Errors::SMBCredentialsMissing) end it "should ignore if the command returns non-zero" do expect(Vagrant::Util::Subprocess).to receive(:execute).with("pwpolicy", "gethashtypes"). and_return(result.new(1, "", "")) subject.smb_start(env) end it "should not load smb preferences if it is already loaded" do expect(Vagrant::Util::Subprocess).to receive(:execute).with("launchctl", "list", /preferences/).and_return(result.new(0, "", "")) expect(Vagrant::Util::Subprocess).not_to receive(:execute).with(/sudo/, /launchctl/, "load", "-w", /preferences/) subject.smb_start(env) end it "should load smb preferences if it is not already loaded" do expect(Vagrant::Util::Subprocess).to receive(:execute).with("launchctl", "list", /preferences/).and_return(result.new(1, "", "")) expect(Vagrant::Util::Subprocess).to receive(:execute).with(/sudo/, /launchctl/, "load", "-w", /preferences/).and_return(result.new(0, "", "")) subject.smb_start(env) end it "should raise error if load smb preferences fails" do expect(Vagrant::Util::Subprocess).to receive(:execute).with("launchctl", "list", /preferences/).and_return(result.new(1, "", "")) expect(Vagrant::Util::Subprocess).to receive(:execute).with(/sudo/, /launchctl/, "load", "-w", /preferences/).and_return(result.new(1, "", "")) expect{ subject.smb_start(env) }.to raise_error(VagrantPlugins::SyncedFolderSMB::Errors::SMBStartFailed) end it "should not load smbd if it is already loaded" do expect(Vagrant::Util::Subprocess).to receive(:execute).with("launchctl", "list", /smbd/).and_return(result.new(0, "", "")) expect(Vagrant::Util::Subprocess).not_to receive(:execute).with(/sudo/, /launchctl/, "load", "-w", /smbd/) subject.smb_start(env) end it "should load smbd if it is not already loaded" do expect(Vagrant::Util::Subprocess).to receive(:execute).with("launchctl", "list", /smbd/).and_return(result.new(1, "", "")) expect(Vagrant::Util::Subprocess).to receive(:execute).with(/sudo/, /launchctl/, "load", "-w", /smbd/).and_return(result.new(0, "", "")) subject.smb_start(env) end it "should raise error if load smbd fails" do expect(Vagrant::Util::Subprocess).to receive(:execute).with("launchctl", "list", /smbd/).and_return(result.new(1, "", "")) expect(Vagrant::Util::Subprocess).to receive(:execute).with(/sudo/, /launchctl/, "load", "-w", /smbd/).and_return(result.new(1, "", "")) expect{ subject.smb_start(env) }.to raise_error(VagrantPlugins::SyncedFolderSMB::Errors::SMBStartFailed) end end describe ".smb_cleanup" do after{ subject.smb_cleanup(env, machine, options) } it "should search for shares with generated machine ID" do expect(Vagrant::Util::Subprocess).to receive(:execute).with( "/usr/bin/sudo", /sharing/, "-l").and_return(result.new(0, "", "")) end it "should remove shares individually" do expect(Vagrant::Util::Subprocess).to receive(:execute). with("/usr/bin/sudo", /sharing/, "-l"). and_return(result.new(0, "name: vgt-CUSTOM_ID-1\nname: vgt-CUSTOM_ID-2\n", "")) expect(Vagrant::Util::Subprocess).to receive(:execute).with(/sudo/, /sharing/, anything, /CUSTOM_ID/). twice.and_return(result.new(0, "", "")) end end describe ".smb_prepare" do let(:folders){ {"/first/path" => {hostpath: "/first/host", smb_id: "ID1"}, "/second/path" => {hostpath: "/second/host"}} } before{ allow(Vagrant::Util::Subprocess).to receive(:execute).and_return(result.new(0, "", "")) } it "should provide ID value if not set" do subject.smb_prepare(env, machine, folders, options) expect(folders["/second/path"][:smb_id]).to start_with("vgt-") end it "should not modify ID if already set" do subject.smb_prepare(env, machine, folders, options) expect(folders["/first/path"][:smb_id]).to eq("ID1") end it "should raise error when sharing command fails" do expect(Vagrant::Util::Subprocess).to receive(:execute).and_return(result.new(1, "", "")) expect{ subject.smb_prepare(env, machine, folders, options) }.to raise_error( VagrantPlugins::SyncedFolderSMB::Errors::DefineShareFailed) end it "should add shares individually" do expect(Vagrant::Util::Subprocess).to receive(:execute).with(/sudo/, any_args).twice.and_return(result.new(0, "", "")) subject.smb_prepare(env, machine, folders, options) end end end vagrant-2.0.2/test/unit/plugins/hosts/linux/000077500000000000000000000000001323370221500210715ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/hosts/linux/cap/000077500000000000000000000000001323370221500216345ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/hosts/linux/cap/nfs_test.rb000066400000000000000000000244371323370221500240200ustar00rootroot00000000000000require_relative "../../../../base" require_relative "../../../../../../plugins/hosts/linux/cap/nfs" require_relative "../../../../../../lib/vagrant/util" describe VagrantPlugins::HostLinux::Cap::NFS do include_context "unit" let(:caps) do VagrantPlugins::HostLinux::Plugin .components .host_capabilities[:linux] end let(:tmp_exports_path) do @tmp_exports ||= temporary_file end let(:exports_path){ VagrantPlugins::HostLinux::Cap::NFS::NFS_EXPORTS_PATH } let(:env){ double(:env) } let(:ui){ double(:ui) } let(:host){ double(:host) } before do @original_exports_path = VagrantPlugins::HostLinux::Cap::NFS::NFS_EXPORTS_PATH VagrantPlugins::HostLinux::Cap::NFS.send(:remove_const, :NFS_EXPORTS_PATH) VagrantPlugins::HostLinux::Cap::NFS.const_set(:NFS_EXPORTS_PATH, tmp_exports_path.to_s) allow(Vagrant::Util::Subprocess).to receive(:execute).with("systemctl", "list-units", any_args). and_return(Vagrant::Util::Subprocess::Result.new(1, "", "")) allow(Vagrant::Util::Platform).to receive(:systemd?).and_return(false) end after do VagrantPlugins::HostLinux::Cap::NFS.send(:remove_const, :NFS_EXPORTS_PATH) VagrantPlugins::HostLinux::Cap::NFS.const_set(:NFS_EXPORTS_PATH, @original_exports_path) VagrantPlugins::HostLinux::Cap::NFS.reset! File.unlink(tmp_exports_path.to_s) if File.exist?(tmp_exports_path.to_s) @tmp_exports = nil end describe ".nfs_service_name_systemd" do let(:cap){ VagrantPlugins::HostLinux::Cap::NFS } context "without service match" do it "should use default service name" do expect(cap.nfs_service_name_systemd).to eq(cap.const_get(:NFS_DEFAULT_NAME_SYSTEMD)) end end context "with service match" do let(:custom_nfs_service_name){ "custom-nfs-server-service-name" } before{ expect(Vagrant::Util::Subprocess).to receive(:execute).with("systemctl", "list-units", any_args). and_return(Vagrant::Util::Subprocess::Result.new(0, custom_nfs_service_name, "")) } it "should use the matched service name" do expect(cap.nfs_service_name_systemd).to eq(custom_nfs_service_name) end end end describe ".nfs_service_name_sysv" do let(:cap){ VagrantPlugins::HostLinux::Cap::NFS } context "without service match" do it "should use default service name" do expect(cap.nfs_service_name_sysv).to eq(cap.const_get(:NFS_DEFAULT_NAME_SYSV)) end end context "with service match" do let(:custom_nfs_service_name){ "/etc/init.d/custom-nfs-server-service-name" } before{ expect(Dir).to receive(:glob).with(/.+init\.d.+/).and_return([custom_nfs_service_name]) } it "should use the matched service name" do expect(cap.nfs_service_name_sysv).to eq(File.basename(custom_nfs_service_name)) end end end describe ".nfs_check_command" do let(:cap){ caps.get(:nfs_check_command) } context "without systemd" do before{ expect(Vagrant::Util::Platform).to receive(:systemd?).and_return(false) } it "should use init.d script" do expect(cap.nfs_check_command(env)).to include("init.d") end end context "with systemd" do before do expect(Vagrant::Util::Platform).to receive(:systemd?).and_return(true) end it "should use systemctl" do expect(cap.nfs_check_command(env)).to include("systemctl") end end end describe ".nfs_start_command" do let(:cap){ caps.get(:nfs_start_command) } context "without systemd" do before{ expect(Vagrant::Util::Platform).to receive(:systemd?).and_return(false) } it "should use init.d script" do expect(cap.nfs_start_command(env)).to include("init.d") end end context "with systemd" do before{ expect(Vagrant::Util::Platform).to receive(:systemd?).and_return(true) } it "should use systemctl" do expect(cap.nfs_start_command(env)).to include("systemctl") end end end describe ".nfs_export" do let(:cap){ caps.get(:nfs_export) } before do allow(env).to receive(:host).and_return(host) allow(host).to receive(:capability).with(:nfs_apply_command).and_return("/bin/true") allow(host).to receive(:capability).with(:nfs_check_command).and_return("/bin/true") allow(host).to receive(:capability).with(:nfs_start_command).and_return("/bin/true") allow(ui).to receive(:info) allow(Vagrant::Util::Subprocess).to receive(:execute).and_call_original allow(Vagrant::Util::Subprocess).to receive(:execute).with("sudo", "/bin/true").and_return(double(:result, exit_code: 0)) allow(Vagrant::Util::Subprocess).to receive(:execute).with("/bin/true").and_return(double(:result, exit_code: 0)) end it "should export new entries" do cap.nfs_export(env, ui, SecureRandom.uuid, ["127.0.0.1"], "tmp" => {:hostpath => "/tmp"}) exports_content = File.read(exports_path) expect(exports_content).to match(/\/tmp.*127\.0\.0\.1/) end it "should not remove existing entries" do File.write(exports_path, "/custom/directory hostname1(rw,sync,no_subtree_check)") cap.nfs_export(env, ui, SecureRandom.uuid, ["127.0.0.1"], "tmp" => {:hostpath => "/tmp"}) exports_content = File.read(exports_path) expect(exports_content).to match(/\/tmp.*127\.0\.0\.1/) expect(exports_content).to match(/\/custom\/directory.*hostname1/) end it "should remove entries no longer valid" do valid_id = SecureRandom.uuid other_id = SecureRandom.uuid content =<<-EOH # VAGRANT-BEGIN: #{Process.uid} #{other_id} "/tmp" 127.0.0.1(rw,no_subtree_check,all_squash,anonuid=,anongid=,fsid=) # VAGRANT-END: #{Process.uid} #{other_id} # VAGRANT-BEGIN: #{Process.uid} #{valid_id} "/var" 127.0.0.1(rw,no_subtree_check,all_squash,anonuid=,anongid=,fsid=) # VAGRANT-END: #{Process.uid} #{valid_id} EOH File.write(exports_path, content) cap.nfs_export(env, ui, valid_id, ["127.0.0.1"], "home" => {:hostpath => "/home"}) exports_content = File.read(exports_path) expect(exports_content).to include("/home") expect(exports_content).to include("/tmp") expect(exports_content).not_to include("/var") end it "throws an exception with at least 2 different nfs options" do folders = {"/vagrant"=> {:hostpath=>"/home/vagrant", :linux__nfs_options=>["rw","all_squash"]}, "/var/www/project"=> {:hostpath=>"/home/vagrant", :linux__nfs_options=>["rw","sync"]}} expect { cap.nfs_export(env, ui, SecureRandom.uuid, ["127.0.0.1"], folders) }. to raise_error Vagrant::Errors::NFSDupePerms end it "writes only 1 hostpath for multiple exports" do folders = {"/vagrant"=> {:hostpath=>"/home/vagrant", :linux__nfs_options=>["rw","all_squash"]}, "/var/www/otherproject"=> {:hostpath=>"/newhome/otherproject", :linux__nfs_options=>["rw","all_squash"]}, "/var/www/project"=> {:hostpath=>"/home/vagrant", :linux__nfs_options=>["rw","all_squash"]}} valid_id = SecureRandom.uuid content =<<-EOH \n# 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} EOH cap.nfs_export(env, ui, valid_id, ["127.0.0.1"], folders) exports_content = File.read(exports_path) expect(exports_content).to eq(content) end end describe ".nfs_prune" do let(:cap){ caps.get(:nfs_prune) } before do allow(ui).to receive(:info) allow(Vagrant::Util::Subprocess).to receive(:execute).with("mv", any_args). and_call_original end it "should remove entries no longer valid" do invalid_id = SecureRandom.uuid valid_id = SecureRandom.uuid content =<<-EOH # VAGRANT-BEGIN: #{Process.uid} #{invalid_id} "/tmp" 127.0.0.1(rw,no_subtree_check,all_squash,anonuid=,anongid=,fsid=) # VAGRANT-END: #{Process.uid} #{invalid_id} # VAGRANT-BEGIN: #{Process.uid} #{valid_id} "/var" 127.0.0.1(rw,no_subtree_check,all_squash,anonuid=,anongid=,fsid=) # VAGRANT-END: #{Process.uid} #{valid_id} EOH File.write(exports_path, content) cap.nfs_prune(env, ui, [valid_id]) exports_content = File.read(exports_path) expect(exports_content).to include(valid_id) expect(exports_content).not_to include(invalid_id) expect(exports_content).to include("/var") expect(exports_content).not_to include("/tmp") end end describe ".nfs_write_exports" do before do File.write(tmp_exports_path, "original content") allow(Vagrant::Util::Subprocess).to receive(:execute).with("mv", any_args). and_call_original end it "should write updated contents to file" do described_class.nfs_write_exports("new content") exports_content = File.read(exports_path) expect(exports_content).to include("new content") expect(exports_content).not_to include("original content") end it "should only update contents if different" do original_stat = File.stat(exports_path) described_class.nfs_write_exports("original content") updated_stat = File.stat(exports_path) expect(original_stat).to eq(updated_stat) end it "should retain existing file permissions" do File.chmod(0600, exports_path) original_stat = File.stat(exports_path) described_class.nfs_write_exports("original content") updated_stat = File.stat(exports_path) expect(original_stat.mode).to eq(updated_stat.mode) end it "should raise exception when failing to move new exports file" do expect(Vagrant::Util::Subprocess).to receive(:execute).and_return( Vagrant::Util::Subprocess::Result.new(1, "Failed to move file", "") ) expect{ described_class.nfs_write_exports("new content") }.to raise_error(Vagrant::Errors::NFSExportsFailed) end it "should retain existing file owner and group IDs" do pending("investigate using a simulated FS to test") test_with_simulated_fs end it "should raise custom exception when chown fails" do pending("investigate using a simulated FS to test") test_with_simulated_fs end end end vagrant-2.0.2/test/unit/plugins/hosts/windows/000077500000000000000000000000001323370221500214245ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/hosts/windows/cap/000077500000000000000000000000001323370221500221675ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/hosts/windows/cap/configure_ip_addresses_test.rb000066400000000000000000000024301323370221500302600ustar00rootroot00000000000000require_relative "../../../../base" require_relative "../../../../../../plugins/hosts/windows/cap/configured_ip_addresses" describe VagrantPlugins::HostWindows::Cap::ConfiguredIPAddresses do let(:subject){ VagrantPlugins::HostWindows::Cap::ConfiguredIPAddresses } let(:result){ Vagrant::Util::Subprocess::Result } let(:addresses){ [] } let(:execute_result){ result.new(0, {ip_addresses: addresses}.to_json, "") } before{ allow(Vagrant::Util::PowerShell).to receive(:execute). and_return(execute_result) } it "should return an array" do expect(subject.configured_ip_addresses(nil)).to be_kind_of(Array) end context "with single address returned" do let(:addresses){ "ADDRESS" } it "should return an array" do expect(subject.configured_ip_addresses(nil)).to eq([addresses]) end end context "with multiple addresses returned" do let(:addresses){ ["ADDRESS1", "ADDRESS2"] } it "should return an array" do expect(subject.configured_ip_addresses(nil)).to eq(addresses) end end context "with failed script execution" do let(:execute_result){ result.new(1, "", "") } it "should raise error" do expect{ subject.configured_ip_addresses(nil) }.to raise_error( Vagrant::Errors::PowerShellError) end end end vagrant-2.0.2/test/unit/plugins/hosts/windows/cap/smb_test.rb000066400000000000000000000112461323370221500243400ustar00rootroot00000000000000require_relative "../../../../base" require_relative "../../../../../../plugins/hosts/windows/cap/smb" describe VagrantPlugins::HostWindows::Cap::SMB do let(:subject){ VagrantPlugins::HostWindows::Cap::SMB } let(:machine){ double(:machine, env: double(:machine_env, ui: double(:ui))) } let(:env){ double(:env) } let(:options){ {} } let(:result){ Vagrant::Util::Subprocess::Result } let(:powershell_version){ "3" } let(:smblist){ <<-EOF Name : vgt-CUSTOM_ID-1 Path : /a/path Description : vgt-CUSTOM_ID-1 Name : vgt-CUSTOM_ID-2 Path : /other/path Description : vgt-CUSTOM_ID-2 Name : my-share Path : /my/path Description : Not Vagrant Owned EOF } before do allow(subject).to receive(:machine_id).and_return("CUSTOM_ID") allow(Vagrant::Util::PowerShell).to receive(:version).and_return(powershell_version) allow(Vagrant::Util::PowerShell).to receive(:execute_cmd).and_return("") allow(machine.env.ui).to receive(:warn) allow(subject).to receive(:sleep) end describe ".smb_installed" do context "when powershell version is greater than 2" do it "is valid installation" do expect(subject.smb_installed(nil)).to eq(true) end end context "when powershell version is less than 3" do let(:powershell_version){ "2" } it "is not a valid installation" do expect(subject.smb_installed(nil)).to eq(false) end end end describe ".smb_cleanup" do before do allow(Vagrant::Util::PowerShell).to receive(:execute_cmd).with(/Get-SmbShare/). and_return(smblist) allow(Vagrant::Util::PowerShell).to receive(:execute).and_return(result.new(0, "", "")) end after{ subject.smb_cleanup(env, machine, options) } it "should pause after warning user" do expect(machine.env.ui).to receive(:warn) expect(subject).to receive(:sleep) end it "should remove all shares in single call" do expect(Vagrant::Util::PowerShell).to receive(:execute).with(any_args, sudo: true).once end context "when no shares are defined" do before do expect(Vagrant::Util::PowerShell).to receive(:execute_cmd).with(/Get-SmbShare/). and_return("") end it "should not attempt to remove shares" do expect(Vagrant::Util::PowerShell).not_to receive(:execute).with(any_args, sudo: true) end it "should not warn user" do expect(machine.env.ui).not_to receive(:warn) end end end describe ".smb_prepare" do let(:folders){ {"/first/path" => {hostpath: "/host/1"}, "/second/path" => {hostpath: "/host/2", smb_id: "ID1"}} } let(:options){ {} } before{ allow(Vagrant::Util::PowerShell).to receive(:execute).and_return(result.new(0, "", "")) } it "should add ID when not defined" do subject.smb_prepare(env, machine, folders, options) expect(folders["/first/path"][:smb_id]).to start_with("vgt-") end it "should not modify ID when defined" do subject.smb_prepare(env, machine, folders, options) expect(folders["/second/path"][:smb_id]).to eq("ID1") end it "should pause after warning user" do expect(machine.env.ui).to receive(:warn) expect(subject).to receive(:sleep) subject.smb_prepare(env, machine, folders, options) end it "should add all shares in single call" do expect(Vagrant::Util::PowerShell).to receive(:execute).with(any_args, sudo: true).once subject.smb_prepare(env, machine, folders, options) end context "when share already exists" do let(:shares){ {"ID1" => {"Path" => "/host/2"}} } before do allow(File).to receive(:expand_path).and_call_original expect(subject).to receive(:existing_shares).and_return(shares) end it "should expand paths when comparing existing to requested" do expect(File).to receive(:expand_path).at_least(2).with("/host/2").and_return("expanded_path") subject.smb_prepare(env, machine, folders, options) end context "with different path" do let(:shares){ {"ID1" => {"Path" => "/host/3"}} } it "should raise an error" do expect{ subject.smb_prepare(env, machine, folders, options) }.to raise_error(VagrantPlugins::SyncedFolderSMB::Errors::SMBNameError) end end end context "when no shared are defined" do after{ subject.smb_prepare(env, machine, {}, options) } it "should not attempt to add shares" do expect(Vagrant::Util::PowerShell).not_to receive(:execute).with(any_args, sudo: true) end it "should not warn user" do expect(machine.env.ui).not_to receive(:warn) end end end end vagrant-2.0.2/test/unit/plugins/kernel_v2/000077500000000000000000000000001323370221500204615ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/kernel_v2/config/000077500000000000000000000000001323370221500217265ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/kernel_v2/config/package_test.rb000066400000000000000000000005271323370221500247110ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/kernel_v2/config/package") describe VagrantPlugins::Kernel_V2::PackageConfig do subject { described_class.new } describe "#name" do it "defaults to nil" do subject.finalize! expect(subject.name).to be_nil end end end vagrant-2.0.2/test/unit/plugins/kernel_v2/config/push_test.rb000066400000000000000000000217261323370221500243010ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/kernel_v2/config/push") describe VagrantPlugins::Kernel_V2::PushConfig do include_context "unit" subject { described_class.new } describe "#define" do let(:pushes) { subject.instance_variable_get(:@__defined_pushes) } it "pushes the strategy and block onto the defined pushes array" do subject.define("foo") { "bar" } subject.define("foo") { "zip" } subject.define("foo") { "zap" } expect(pushes.size).to eq(1) expect(pushes[:foo].size).to eq(3) expect(pushes[:foo][0]).to be_a(Array) expect(pushes[:foo][0][0]).to eq(:foo) expect(pushes[:foo][0][1]).to be_a(Proc) end context "when no strategy is given" do it "defaults to the name" do subject.define("foo") { "bar" } expect(pushes.size).to eq(1) expect(pushes[:foo].size).to eq(1) expect(pushes[:foo][0]).to be_a(Array) expect(pushes[:foo][0][0]).to eq(:foo) expect(pushes[:foo][0][1]).to be_a(Proc) end end context "when a strategy is given" do it "uses the strategy" do subject.define("foo", strategy: "bacon") { "bar" } expect(pushes.size).to eq(1) expect(pushes[:foo].size).to eq(1) expect(pushes[:foo][0]).to be_a(Array) expect(pushes[:foo][0][0]).to eq(:bacon) expect(pushes[:foo][0][1]).to be_a(Proc) end end end describe "#merge" do it "appends defined pushes" do a = described_class.new.tap do |i| i.define("foo") { "bar" } i.define("bar") { "bar" } end b = described_class.new.tap do |i| i.define("foo") { "zip" } end result = a.merge(b) pushes = result.instance_variable_get(:@__defined_pushes) expect(pushes[:foo]).to be_a(Array) expect(pushes[:foo].size).to eq(2) expect(pushes[:bar]).to be_a(Array) expect(pushes[:bar].size).to eq(1) end end describe "#__compiled_pushes" do it "raises an exception if not finalized" do subject.instance_variable_set(:@__finalized, false) expect { subject.__compiled_pushes }.to raise_error(RuntimeError) end it "returns a copy of the compiled pushes" do pushes = { foo: "bar" } subject.instance_variable_set(:@__finalized, true) subject.instance_variable_set(:@__compiled_pushes, pushes) expect(subject.__compiled_pushes).to_not be(pushes) expect(subject.__compiled_pushes).to eq(pushes) end end describe "#finalize!" do let(:pushes) { a.merge(b).tap { |r| r.finalize! }.__compiled_pushes } let(:key) { pushes[:foo][0] } let(:config) { pushes[:foo][1] } let(:unset) { Vagrant.plugin("2", :config).const_get(:UNSET_VALUE) } let(:dummy_klass) { Vagrant::Config::V2::DummyConfig } before do register_plugin("2") do |plugin| plugin.name "foo" plugin.push(:foo) do Class.new(Vagrant.plugin("2", :push)) end plugin.config(:foo, :push) do Class.new(Vagrant.plugin("2", :config)) do attr_accessor :bar attr_accessor :zip def initialize @bar = self.class.const_get(:UNSET_VALUE) @zip = self.class.const_get(:UNSET_VALUE) end end end end end it "compiles the proper configuration with a single strategy" do instance = described_class.new.tap do |i| i.define "foo" end instance.finalize! pushes = instance.__compiled_pushes strategy, config = pushes[:foo] expect(strategy).to eq(:foo) expect(config.bar).to be(unset) end it "compiles the proper configuration with a single strategy and block" do instance = described_class.new.tap do |i| i.define "foo" do |b| b.bar = 42 end end instance.finalize! pushes = instance.__compiled_pushes strategy, config = pushes[:foo] expect(strategy).to eq(:foo) expect(config.bar).to eq(42) end it "compiles the proper config with a name and explicit strategy" do instance = described_class.new.tap do |i| i.define "bar", strategy: "foo" end instance.finalize! pushes = instance.__compiled_pushes strategy, config = pushes[:bar] expect(strategy).to eq(:foo) expect(config.bar).to be(unset) end it "compiles the proper config with a name and explicit strategy with block" do instance = described_class.new.tap do |i| i.define "bar", strategy: "foo" do |b| b.bar = 42 end end instance.finalize! pushes = instance.__compiled_pushes strategy, config = pushes[:bar] expect(strategy).to eq(:foo) expect(config.bar).to eq(42) end context "with the same name but different strategy" do context "with no block" do let(:a) do described_class.new.tap do |i| i.define("foo", strategy: "bar") end end let(:b) do described_class.new.tap do |i| i.define("foo", strategy: "zip") end end it "chooses the last config" do expect(key).to eq(:zip) expect(config).to be_kind_of(dummy_klass) end end context "with a block" do let(:a) do described_class.new.tap do |i| i.define("foo", strategy: "bar") do |p| p.bar = "a" end end end let(:b) do described_class.new.tap do |i| i.define("foo", strategy: "zip") do |p| p.zip = "b" end end end it "chooses the last config" do expect(key).to eq(:zip) expect(config).to be_kind_of(dummy_klass) end end context "with a block, then no block" do let(:a) do described_class.new.tap do |i| i.define("foo", strategy: "bar") do |p| p.bar, p.zip = "a", "a" end end end let(:b) do described_class.new.tap do |i| i.define("foo", strategy: "zip") end end it "chooses the last config" do expect(key).to eq(:zip) expect(config).to be_kind_of(dummy_klass) end end context "with no block, then a block" do let(:a) do described_class.new.tap do |i| i.define("foo", strategy: "bar") end end let(:b) do described_class.new.tap do |i| i.define("foo", strategy: "zip") do |p| p.bar, p.zip = "b", "b" end end end it "chooses the last config" do expect(key).to eq(:zip) expect(config).to be_kind_of(dummy_klass) end end end context "with the same name twice" do context "with no block" do let(:a) do described_class.new.tap do |i| i.define("foo") end end let(:b) do described_class.new.tap do |i| i.define("foo") end end it "merges the configs" do expect(key).to eq(:foo) expect(config.bar).to be(unset) expect(config.zip).to be(unset) end end context "with a block" do let(:a) do described_class.new.tap do |i| i.define("foo") do |p| p.bar = "a" end end end let(:b) do described_class.new.tap do |i| i.define("foo") do |p| p.zip = "b" end end end it "merges the configs" do expect(key).to eq(:foo) expect(config.bar).to eq("a") expect(config.zip).to eq("b") end end context "with a block, then no block" do let(:a) do described_class.new.tap do |i| i.define("foo") do |p| p.bar = "a" end end end let(:b) do described_class.new.tap do |i| i.define("foo") end end it "merges the configs" do expect(key).to eq(:foo) expect(config.bar).to eq("a") expect(config.zip).to be(unset) end end context "with no block, then a block" do let(:a) do described_class.new.tap do |i| i.define("foo", strategy: "bar") end end let(:b) do described_class.new.tap do |i| i.define("foo", strategy: "zip") do |p| p.zip = "b" end end end it "merges the configs" do expect(key).to eq(:zip) expect(config).to be_kind_of(dummy_klass) end end end it "sets @__finalized to true" do subject.finalize! expect(subject.instance_variable_get(:@__finalized)).to be(true) end end end vagrant-2.0.2/test/unit/plugins/kernel_v2/config/ssh_test.rb000066400000000000000000000010171323370221500241060ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/kernel_v2/config/ssh") describe VagrantPlugins::Kernel_V2::SSHConfig do subject { described_class.new } describe "#default" do it "defaults to vagrant username" do subject.finalize! expect(subject.default.username).to eq("vagrant") end end describe "#sudo_command" do it "defaults properly" do subject.finalize! expect(subject.sudo_command).to eq("sudo -E -H %c") end end end vagrant-2.0.2/test/unit/plugins/kernel_v2/config/vagrant_test.rb000066400000000000000000000032011323370221500247500ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/kernel_v2/config/vagrant") describe VagrantPlugins::Kernel_V2::VagrantConfig do subject { described_class.new } let(:machine){ double("machine") } describe "#host" do it "defaults to :detect" do subject.finalize! expect(subject.host).to eq(:detect) end it "symbolizes" do subject.host = "foo" subject.finalize! expect(subject.host).to eq(:foo) end end describe "#sensitive" do after{ Vagrant::Util::CredentialScrubber.reset! } it "accepts string value" do subject.sensitive = "test" subject.finalize! expect(subject.sensitive).to eq("test") end it "accepts array of values" do subject.sensitive = ["test1", "test2"] subject.finalize! expect(subject.sensitive).to eq(["test1", "test2"]) end it "does not accept non-string values" do subject.sensitive = 1 subject.finalize! result = subject.validate(machine) expect(result).to be_a(Hash) expect(result.values).not_to be_empty end it "registers single sensitive value to be scrubbed" do subject.sensitive = "test" expect(Vagrant::Util::CredentialScrubber).to receive(:sensitive).with("test") subject.finalize! end it "registers multiple sensitive values to be scrubbed" do subject.sensitive = ["test1", "test2"] expect(Vagrant::Util::CredentialScrubber).to receive(:sensitive).with("test1") expect(Vagrant::Util::CredentialScrubber).to receive(:sensitive).with("test2") subject.finalize! end end end vagrant-2.0.2/test/unit/plugins/kernel_v2/config/vm_test.rb000066400000000000000000000401361323370221500237400ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/kernel_v2/config/vm") describe VagrantPlugins::Kernel_V2::VMConfig do include_context "unit" subject { described_class.new } let(:machine) { double("machine") } def assert_invalid errors = subject.validate(machine) if !errors.values.any? { |v| !v.empty? } raise "No errors: #{errors.inspect}" end end def assert_valid errors = subject.validate(machine) if !errors.values.all? { |v| v.empty? } raise "Errors: #{errors.inspect}" end end def find_network(name) network_definitions = subject.networks.map do |n| n[1] end network_definitions.find {|n| n[:id] == name} end before do env = double("env") allow(env).to receive(:root_path).and_return(nil) allow(machine).to receive(:env).and_return(env) allow(machine).to receive(:provider_config).and_return(nil) allow(machine).to receive(:provider_options).and_return({}) subject.box = "foo" end it "is valid with test defaults" do subject.finalize! assert_valid end describe "#base_mac" do it "defaults properly" do subject.finalize! expect(subject.base_mac).to be_nil end end describe "#box" do it "is required" do subject.box = nil subject.finalize! assert_invalid end it "is not required if the provider says so" do machine.provider_options[:box_optional] = true subject.box = nil subject.finalize! assert_valid end it "is invalid if clone is set" do subject.clone = "foo" subject.finalize! assert_invalid end end context "#box_check_update" do it "defaults to true" do with_temp_env("VAGRANT_BOX_UPDATE_CHECK_DISABLE" => "") do subject.finalize! expect(subject.box_check_update).to be(true) end end it "is false if VAGRANT_BOX_UPDATE_CHECK_DISABLE is set" do with_temp_env("VAGRANT_BOX_UPDATE_CHECK_DISABLE" => "1") do subject.finalize! expect(subject.box_check_update).to be(false) end end end describe "#box_url" do it "defaults to nil" do subject.finalize! expect(subject.box_url).to be_nil end it "turns into an array" do subject.box_url = "foo" subject.finalize! expect(subject.box_url).to eq( ["foo"]) end it "keeps in array" do subject.box_url = ["foo", "bar"] subject.finalize! expect(subject.box_url).to eq( ["foo", "bar"]) end end context "#box_version" do it "defaults to nil" do subject.finalize! expect(subject.box_version).to be_nil end it "errors if invalid version" do subject.box_version = "nope" subject.finalize! expect { assert_valid }.to raise_error(RuntimeError) end it "can have complex constraints" do subject.box_version = ">= 0, ~> 1.0" subject.finalize! assert_valid end ["1", 1, "1.0", 1.0].each do |valid| it "is valid: #{valid}" do subject.box_version = valid subject.finalize! assert_valid end end end describe "#communicator" do it "is nil by default" do subject.finalize! expect(subject.communicator).to be_nil end end describe "#guest" do it "is nil by default" do subject.finalize! expect(subject.guest).to be_nil end it "is symbolized" do subject.guest = "foo" subject.finalize! expect(subject.guest).to eq(:foo) end end describe "#hostname" do ["a", "foo", "foo-bar", "baz0"].each do |valid| it "is valid: #{valid}" do subject.hostname = valid subject.finalize! assert_valid end end end describe "#network(s)" do it "defaults to forwarding SSH by default" do subject.finalize! n = subject.networks expect(n.length).to eq(1) expect(n[0][0]).to eq(:forwarded_port) expect(n[0][1][:guest]).to eq(22) expect(n[0][1][:host]).to eq(2222) expect(n[0][1][:host_ip]).to eq("127.0.0.1") expect(n[0][1][:id]).to eq("ssh") end it "defaults to forwarding WinRM if communicator is winrm" do subject.communicator = "winrm" subject.finalize! n = subject.networks expect(n.length).to eq(3) expect(n[0][0]).to eq(:forwarded_port) expect(n[0][1][:guest]).to eq(5985) expect(n[0][1][:host]).to eq(55985) expect(n[0][1][:host_ip]).to eq("127.0.0.1") expect(n[0][1][:id]).to eq("winrm") expect(n[1][0]).to eq(:forwarded_port) expect(n[1][1][:guest]).to eq(5986) expect(n[1][1][:host]).to eq(55986) expect(n[1][1][:host_ip]).to eq("127.0.0.1") expect(n[1][1][:id]).to eq("winrm-ssl") end it "forwards ssh even if the communicator is winrm" do subject.communicator = "winrm" subject.finalize! n = subject.networks expect(n.length).to eq(3) expect(n[0][0]).to eq(:forwarded_port) expect(n[0][1][:guest]).to eq(5985) expect(n[0][1][:host]).to eq(55985) expect(n[0][1][:host_ip]).to eq("127.0.0.1") expect(n[0][1][:id]).to eq("winrm") expect(n[1][0]).to eq(:forwarded_port) expect(n[1][1][:guest]).to eq(5986) expect(n[1][1][:host]).to eq(55986) expect(n[1][1][:host_ip]).to eq("127.0.0.1") expect(n[1][1][:id]).to eq("winrm-ssl") expect(n[2][0]).to eq(:forwarded_port) expect(n[2][1][:guest]).to eq(22) expect(n[2][1][:host]).to eq(2222) expect(n[2][1][:host_ip]).to eq("127.0.0.1") expect(n[2][1][:id]).to eq("ssh") end it "allows overriding SSH" do subject.network "forwarded_port", guest: 22, host: 14100, id: "ssh" subject.finalize! n = subject.networks expect(n.length).to eq(1) expect(n[0][0]).to eq(:forwarded_port) expect(n[0][1][:guest]).to eq(22) expect(n[0][1][:host]).to eq(14100) expect(n[0][1][:id]).to eq("ssh") end it "allows overriding WinRM" do subject.communicator = :winrm subject.network "forwarded_port", guest: 5985, host: 14100, id: "winrm" subject.finalize! winrm_network = find_network 'winrm' expect(winrm_network[:guest]).to eq(5985) expect(winrm_network[:host]).to eq(14100) expect(winrm_network[:id]).to eq("winrm") end it "allows overriding WinRM SSL" do subject.communicator = :winrm subject.network "forwarded_port", guest: 5986, host: 14100, id: "winrm-ssl" subject.finalize! winrmssl_network = find_network 'winrm-ssl' expect(winrmssl_network[:guest]).to eq(5986) expect(winrmssl_network[:host]).to eq(14100) expect(winrmssl_network[:id]).to eq("winrm-ssl") end it "turns all forwarded port ports to ints" do subject.network "forwarded_port", guest: "45", host: "4545", id: "test" subject.finalize! n = subject.networks.find do |type, data| type == :forwarded_port && data[:id] == "test" end expect(n).to_not be_nil expect(n[1][:guest]).to eq(45) expect(n[1][:host]).to eq(4545) end it "is an error if forwarding a port too low" do subject.network "forwarded_port", guest: "45", host: "-5" subject.finalize! assert_invalid end it "is an error if forwarding a port too high" do subject.network "forwarded_port", guest: "45", host: "74545" subject.finalize! assert_invalid end end describe "#post_up_message" do it "defaults to empty string" do subject.finalize! expect(subject.post_up_message).to eq("") end it "can be set" do subject.post_up_message = "foo" subject.finalize! expect(subject.post_up_message).to eq("foo") end end describe "#provider and #__providers" do it "returns the providers in order" do subject.provider "foo" subject.provider "bar" subject.finalize! expect(subject.__providers).to eq([:foo, :bar]) end describe "merging" do it "prioritizes new orders in later configs" do subject.provider "foo" other = described_class.new other.provider "bar" merged = subject.merge(other) expect(merged.__providers).to eq([:foo, :bar]) end it "prioritizes duplicates in new orders in later configs" do subject.provider "foo" other = described_class.new other.provider "bar" other.provider "foo" merged = subject.merge(other) expect(merged.__providers).to eq([:foo, :bar]) end end end describe "#provider and #get_provider_config" do it "compiles the configurations for a provider" do subject.provider "virtualbox" do |vb| vb.gui = true end subject.provider "virtualbox" do |vb| vb.name = "foo" end subject.finalize! config = subject.get_provider_config(:virtualbox) expect(config.name).to eq("foo") expect(config.gui).to be(true) end it "raises an exception if there is a problem loading" do subject.provider "virtualbox" do |vb| # Purposeful bad variable vm.foo = "bar" end expect { subject.finalize! }. to raise_error(Vagrant::Errors::VagrantfileLoadError) end end describe "#provision" do it "stores the provisioners" do subject.provision("shell", inline: "foo") subject.provision("shell", inline: "bar", run: "always") { |s| s.path = "baz" } subject.finalize! r = subject.provisioners expect(r.length).to eql(2) expect(r[0].run).to be_nil expect(r[0].config.inline).to eql("foo") expect(r[1].config.inline).to eql("bar") expect(r[1].config.path).to eql("baz") expect(r[1].run).to eql(:always) end it "allows provisioner settings to be overriden" do subject.provision("s", path: "foo", type: "shell") { |s| s.inline = "foo" } subject.provision("s", inline: "bar", type: "shell") { |s| s.args = "bar" } subject.finalize! r = subject.provisioners expect(r.length).to eql(1) expect(r[0].config.args).to eql("bar") expect(r[0].config.inline).to eql("bar") expect(r[0].config.path).to eql("foo") end it "marks as invalid if a bad name" do subject.provision("nope", inline: "foo") subject.finalize! r = subject.provisioners expect(r.length).to eql(1) expect(r[0]).to be_invalid end it "allows provisioners that don't define any config" do register_plugin("2") do |p| p.name "foo" # This plugin registers a dummy provisioner # without registering a provisioner config p.provisioner(:foo) do Class.new Vagrant::plugin("2", :provisioner) end end subject.provision("foo") do |c| c.bar = "baz" end # This should succeed without errors expect{ subject.finalize! }.to_not raise_error end it "generates a uuid if no name was provided" do allow(SecureRandom).to receive(:uuid).and_return("MY_CUSTOM_VALUE") subject.provision("shell", path: "foo") { |s| s.inline = "foo" } subject.finalize! r = subject.provisioners expect(r[0].id).to eq("MY_CUSTOM_VALUE") end it "sets id as name if a name was provided" do subject.provision("ghost", type: "shell", path: "motoko") { |s| s.inline = "motoko" } subject.finalize! r = subject.provisioners expect(r[0].id).to eq(:ghost) end describe "merging" do it "ignores non-overriding runs" do subject.provision("shell", inline: "foo", run: "once") other = described_class.new other.provision("shell", inline: "bar", run: "always") merged = subject.merge(other) merged_provs = merged.provisioners expect(merged_provs.length).to eql(2) expect(merged_provs[0].run).to eq("once") expect(merged_provs[1].run).to eq("always") end it "does not merge duplicate provisioners" do subject.provision("shell", inline: "foo") subject.provision("shell", inline: "bar") merged = subject.merge(subject) merged_provs = merged.provisioners expect(merged_provs.length).to eql(2) end it "copies the configs" do subject.provision("shell", inline: "foo") subject_provs = subject.provisioners other = described_class.new other.provision("shell", inline: "bar") merged = subject.merge(other) merged_provs = merged.provisioners expect(merged_provs.length).to eql(2) expect(merged_provs[0].config.inline). to eq(subject_provs[0].config.inline) expect(merged_provs[0].config.object_id). to_not eq(subject_provs[0].config.object_id) end it "uses the proper order when merging overrides" do subject.provision("original", inline: "foo", type: "shell") subject.provision("other", inline: "other", type: "shell") other = described_class.new other.provision("shell", inline: "bar") other.provision("original", inline: "foo-overload", type: "shell") merged = subject.merge(other) merged_provs = merged.provisioners expect(merged_provs.length).to eql(3) expect(merged_provs[0].config.inline). to eq("other") expect(merged_provs[1].config.inline). to eq("bar") expect(merged_provs[2].config.inline). to eq("foo-overload") end it "can preserve order for overrides" do subject.provision("original", inline: "foo", type: "shell") subject.provision("other", inline: "other", type: "shell") other = described_class.new other.provision("shell", inline: "bar") other.provision( "original", inline: "foo-overload", type: "shell", preserve_order: true) merged = subject.merge(other) merged_provs = merged.provisioners expect(merged_provs.length).to eql(3) expect(merged_provs[0].config.inline). to eq("foo-overload") expect(merged_provs[1].config.inline). to eq("other") expect(merged_provs[2].config.inline). to eq("bar") end end end describe "#synced_folder(s)" do it "defaults to sharing the current directory" do subject.finalize! sf = subject.synced_folders expect(sf.length).to eq(1) expect(sf).to have_key("/vagrant") expect(sf["/vagrant"][:disabled]).to_not be end it "allows overriding settings on the /vagrant sf" do subject.synced_folder(".", "/vagrant", disabled: true) subject.finalize! sf = subject.synced_folders expect(sf.length).to eq(1) expect(sf).to have_key("/vagrant") expect(sf["/vagrant"][:disabled]).to be(true) end it "allows overriding previously set options" do subject.synced_folder(".", "/vagrant", disabled: true) subject.synced_folder(".", "/vagrant", foo: :bar) subject.finalize! sf = subject.synced_folders expect(sf.length).to eq(1) expect(sf).to have_key("/vagrant") expect(sf["/vagrant"][:disabled]).to be(false) expect(sf["/vagrant"][:foo]).to eq(:bar) end it "is not an error if guest path is empty" do subject.synced_folder(".", "") subject.finalize! assert_valid end it "allows providing custom name via options" do subject.synced_folder(".", "/vagrant", name: "my-vagrant-folder") sf = subject.synced_folders expect(sf).to have_key("my-vagrant-folder") expect(sf["my-vagrant-folder"][:guestpath]).to eq("/vagrant") expect(sf["my-vagrant-folder"][:hostpath]).to eq(".") end it "allows providing custom name without guest path" do subject.synced_folder(".", name: "my-vagrant-folder") sf = subject.synced_folders expect(sf).to have_key("my-vagrant-folder") expect(sf["my-vagrant-folder"][:hostpath]).to eq(".") end end describe "#usable_port_range" do it "defaults properly" do subject.finalize! expect(subject.usable_port_range).to eq( Range.new(2200, 2250)) end end end vagrant-2.0.2/test/unit/plugins/providers/000077500000000000000000000000001323370221500206075ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/providers/docker/000077500000000000000000000000001323370221500220565ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/providers/docker/action/000077500000000000000000000000001323370221500233335ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/providers/docker/action/create_test.rb000066400000000000000000000030141323370221500261600ustar00rootroot00000000000000require_relative "../../../../base" require_relative "../../../../../../plugins/providers/docker/action/create" describe VagrantPlugins::DockerProvider::Action::Create 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", create: "abcd1234") } subject { described_class.new(app, env) } after do sandbox.close end describe "#call" do it "calls the next action in the chain" do called = false app = ->(*args) { called = true } action = described_class.new(app, env) action.call(env) expect(called).to eq(true) end end describe "#forwarded_ports" do it "does not clobber ports with different protocols" do subject.instance_variable_set(:@machine, machine) machine.config.vm.network "forwarded_port", guest: 8125, host: 8125, protocol: "tcp" machine.config.vm.network "forwarded_port", guest: 8125, host: 8125, protocol: "udp" result = subject.forwarded_ports(false) expect(result).to eq(["8125:8125", "8125:8125/udp"]) end end end vagrant-2.0.2/test/unit/plugins/providers/docker/command/000077500000000000000000000000001323370221500234745ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/providers/docker/command/exec_test.rb000066400000000000000000000020241323370221500260020ustar00rootroot00000000000000require_relative "../../../../base" require_relative "../../../../../../plugins/providers/docker/command/exec" describe VagrantPlugins::DockerProvider::Command::Exec do include_context "unit" include_context "command plugin helpers" let(:sandbox) do isolated_environment end let(:argv) { [] } let(:env) { sandbox.create_vagrant_env } let(:vagrantfile_path) { File.join(env.cwd, "Vagrantfile") } subject { described_class.new(argv, env) } before(:all) do I18n.load_path << Vagrant.source_root.join("templates/locales/providers_docker.yml") I18n.reload! end before do allow(Vagrant.plugin("2").manager).to receive(:commands).and_return({}) allow(subject).to receive(:exec_command) end after do sandbox.close end describe "#execute" do describe "without a command" do let(:argv) { [] } it "raises an error" do expect { subject.execute }.to raise_error(VagrantPlugins::DockerProvider::Errors::ExecCommandRequired) end end end end vagrant-2.0.2/test/unit/plugins/providers/docker/config_test.rb000066400000000000000000000135531323370221500247160ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/platform" require Vagrant.source_root.join("plugins/providers/docker/config") describe VagrantPlugins::DockerProvider::Config do include_context "unit" let(:machine) { double("machine") } let(:build_dir) do Dir.mktmpdir("vagrant-test-docker-provider-build-dir").tap do |dir| File.open(File.join(dir, "Dockerfile"), "wb+") do |f| f.write("Hello") end end end after do FileUtils.rm_rf(build_dir) end def assert_invalid errors = subject.validate(machine) if !errors.values.any? { |v| !v.empty? } raise "No errors: #{errors.inspect}" end end def assert_valid errors = subject.validate(machine) if !errors.values.all? { |v| v.empty? } raise "Errors: #{errors.inspect}" end end def valid_defaults subject.image = "foo" end describe "defaults" do before { subject.finalize! } its(:build_dir) { should be_nil } its(:expose) { should eq([]) } its(:cmd) { should eq([]) } its(:env) { should eq({}) } its(:force_host_vm) { should be(false) } its(:host_vm_build_dir_options) { should be_nil } its(:image) { should be_nil } its(:name) { should be_nil } its(:privileged) { should be(false) } its(:stop_timeout) { should eq(1) } its(:vagrant_machine) { should be_nil } its(:vagrant_vagrantfile) { should be_nil } its(:auth_server) { should be_nil } its(:email) { should eq("") } its(:username) { should eq("") } its(:password) { should eq("") } end before do # By default lets be Linux for validations allow(Vagrant::Util::Platform).to receive(:linux).and_return(true) allow(Vagrant::Util::Platform).to receive(:linux?).and_return(true) end it "should be invalid if both build dir and image are set" do subject.build_dir = build_dir subject.image = "foo" subject.finalize! assert_invalid end describe "#build_dir" do it "should be valid if not set with image" do subject.build_dir = nil subject.image = "foo" subject.finalize! assert_valid end it "should be valid with a valid directory" do subject.build_dir = build_dir subject.finalize! assert_valid end end describe "#compose" do before do valid_defaults end it "should be valid when enabled" do subject.compose = true subject.finalize! assert_valid end it "should be invalid when force_host_vm is enabled" do subject.compose = true subject.force_host_vm = true subject.finalize! assert_invalid end end describe "#create_args" do before do valid_defaults end it "is invalid if it isn't an array" do subject.create_args = "foo" subject.finalize! assert_invalid end end describe "#expose" do before do valid_defaults end it "uniqs the ports" do subject.expose = [1, 1, 4, 5] subject.finalize! assert_valid expect(subject.expose).to eq([1, 4, 5]) end end describe "#image" do it "should be valid if set" do subject.image = "foo" subject.finalize! assert_valid end it "should be invalid if not set" do subject.image = nil subject.finalize! assert_invalid end end describe "#link" do before do valid_defaults end it "should be valid with good links" do subject.link "foo:bar" subject.link "db:blah" subject.finalize! assert_valid end it "should be invalid if not name:alias" do subject.link "foo" subject.finalize! assert_invalid end it "should be invalid if too many colons" do subject.link "foo:bar:baz" subject.finalize! assert_invalid end end describe "#merge" do let(:one) { described_class.new } let(:two) { described_class.new } subject { one.merge(two) } context "#build_dir and #image" do it "overrides image if build_dir is set previously" do one.build_dir = "foo" two.image = "bar" expect(subject.build_dir).to be_nil expect(subject.image).to eq("bar") end it "overrides image if build_dir is set previously" do one.image = "foo" two.build_dir = "bar" expect(subject.image).to be_nil expect(subject.build_dir).to eq("bar") end it "preserves if both set" do one.image = "foo" two.image = "baz" two.build_dir = "bar" expect(subject.image).to eq("baz") expect(subject.build_dir).to eq("bar") end end context "env vars" do it "should merge the values" do one.env["foo"] = "bar" two.env["bar"] = "baz" expect(subject.env).to eq({ "foo" => "bar", "bar" => "baz", }) end end context "exposed ports" do it "merges the exposed ports" do one.expose << 1234 two.expose = [42, 54] expect(subject.expose).to eq([ 1234, 42, 54]) end end context "links" do it "should merge the links" do one.link "foo" two.link "bar" expect(subject._links).to eq([ "foo", "bar"]) end end end describe "#vagrant_machine" do before { valid_defaults } it "should convert to a symbol" do subject.vagrant_machine = "foo" subject.finalize! assert_valid expect(subject.vagrant_machine).to eq(:foo) end end describe "#vagrant_vagrantfile" do before { valid_defaults } it "should be valid if set to a file" do subject.vagrant_vagrantfile = temporary_file.to_s subject.finalize! assert_valid end it "should not be valid if set to a non-existent place" do subject.vagrant_vagrantfile = "/i/shouldnt/exist" subject.finalize! assert_invalid end end end vagrant-2.0.2/test/unit/plugins/providers/docker/driver_compose_test.rb000066400000000000000000000225371323370221500264730ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("lib/vagrant/util/deep_merge") require Vagrant.source_root.join("plugins/providers/docker/driver") describe VagrantPlugins::DockerProvider::Driver::Compose do let(:cmd_executed) { @cmd } let(:cid) { 'side-1-song-10' } let(:docker_yml){ double("docker-yml", path: "/tmp-file") } let(:machine){ double("machine", env: env, name: :docker_1, id: :docker_id, provider_config: provider_config) } let(:compose_configuration){ {} } let(:provider_config) do double("provider-config", compose: true, compose_configuration: compose_configuration ) end let(:env) do double("env", cwd: Pathname.new("/compose/cwd"), local_data_path: local_data_path ) end let(:composition_content){ "--- {}\n" } let(:composition_path) do double("composition-path", to_s: "docker-compose.yml", exist?: true, read: composition_content, delete: true ) end let(:data_directory){ double("data-directory", join: composition_path) } let(:local_data_path){ double("local-data-path") } let(:compose_execute_up){ ["docker-compose", "-f", "docker-compose.yml", "-p", "cwd", "up", "--remove-orphans", "-d", {}] } subject{ described_class.new(machine) } before do allow(Vagrant::Util::Which).to receive(:which).and_return("/dev/null/docker-compose") allow(env).to receive(:lock).and_yield allow(Pathname).to receive(:new).with(local_data_path).and_return(local_data_path) allow(Pathname).to receive(:new).with('/host/path').and_call_original allow(local_data_path).to receive(:join).and_return(data_directory) allow(data_directory).to receive(:mkpath) allow(FileUtils).to receive(:mv) allow(Tempfile).to receive(:new).with("vagrant-docker-compose").and_return(docker_yml) allow(docker_yml).to receive(:write) allow(docker_yml).to receive(:close) allow(subject).to receive(:execute) do |*args| args.delete_if{|i| i.is_a?(Hash) } @cmd = args.join(' ') end end describe '#create' do let(:params) { { image: 'jimi/hendrix:eletric-ladyland', cmd: ['play', 'voodoo-chile'], ports: '8080:80', volumes: '/host/path:guest/path', detach: true, links: [[:janis, 'joplin'], [:janis, 'janis']], env: {key: 'value'}, name: cid, hostname: 'jimi-hendrix', privileged: true } } before { expect(subject).to receive(:execute).with(*compose_execute_up) } after { subject.create(params) } it 'sets container name' do expect(docker_yml).to receive(:write).with(/#{machine.name}/) end it 'forwards ports' do expect(docker_yml).to receive(:write).with(/#{params[:ports]}/) end it 'shares folders' do expect(docker_yml).to receive(:write).with(/#{params[:volumes]}/) end context 'when links are provided as strings' do before{ params[:links] = ["linkl1:linkr1", "linkl2:linkr2"] } it 'links containers' do params[:links].flatten.map{|l| l.split(':')}.each do |link| expect(docker_yml).to receive(:write).with(/#{link}/) end subject.create(params) end end context 'with relative path in share folders' do before do params[:volumes] = './path:guest/path' allow(Pathname).to receive(:new).with('./path').and_call_original allow(Pathname).to receive(:new).with('/compose/cwd/path').and_call_original end it 'should expand the relative host directory' do expect(docker_yml).to receive(:write).with(%r{/compose/cwd/path}) end end it 'links containers' do params[:links].each do |link| expect(docker_yml).to receive(:write).with(/#{link}/) end subject.create(params) end it 'sets environmental variables' do expect(docker_yml).to receive(:write).with(/key.*value/) end it 'is able to run a privileged container' do expect(docker_yml).to receive(:write).with(/privileged/) end it 'sets the hostname if specified' do expect(docker_yml).to receive(:write).with(/#{params[:hostname]}/) end it 'executes the provided command' do expect(docker_yml).to receive(:write).with(/#{params[:image]}/) end end describe '#created?' do let(:result) { subject.created?(cid) } it 'performs the check on all containers list' do subject.created?(cid) expect(cmd_executed).to match(/docker ps \-a \-q/) end context 'when container exists' do before { allow(subject).to receive(:execute) .and_return("foo\n#{cid}\nbar") } it { expect(result).to be_truthy } end context 'when container does not exist' do before { allow(subject).to receive(:execute) .and_return("foo\n#{cid}extra\nbar") } it { expect(result).to be_falsey } end end describe '#pull' do it 'should pull images' do expect(subject).to receive(:execute).with('docker', 'pull', 'foo') subject.pull('foo') end end describe '#running?' do let(:result) { subject.running?(cid) } it 'performs the check on the running containers list' do subject.running?(cid) expect(cmd_executed).to match(/docker ps \-q/) expect(cmd_executed).to_not include('-a') end context 'when container exists' do before { allow(subject).to receive(:execute) .and_return("foo\n#{cid}\nbar") } it { expect(result).to be_truthy } end context 'when container does not exist' do before { allow(subject).to receive(:execute) .and_return("foo\n#{cid}extra\nbar") } it { expect(result).to be_falsey } end end describe '#privileged?' do it 'identifies privileged containers' do allow(subject).to receive(:inspect_container) .and_return({'HostConfig' => {"Privileged" => true}}) expect(subject).to be_privileged(cid) end it 'identifies unprivileged containers' do allow(subject).to receive(:inspect_container) .and_return({'HostConfig' => {"Privileged" => false}}) expect(subject).to_not be_privileged(cid) end end describe '#start' do context 'when container is running' do before { allow(subject).to receive(:running?).and_return(true) } it 'does not start the container' do expect(subject).not_to receive(:execute).with('docker', 'start', cid) subject.start(cid) end end context 'when container is not running' do before { allow(subject).to receive(:running?).and_return(false) } it 'starts the container' do expect(subject).to receive(:execute).with('docker', 'start', cid) subject.start(cid) end end end describe '#stop' do context 'when container is running' do before { allow(subject).to receive(:running?).and_return(true) } it 'stops the container' do expect(subject).to receive(:execute).with('docker', 'stop', '-t', '1', cid) subject.stop(cid, 1) end it "stops the container with the set timeout" do expect(subject).to receive(:execute).with('docker', 'stop', '-t', '5', cid) subject.stop(cid, 5) end end context 'when container is not running' do before { allow(subject).to receive(:running?).and_return(false) } it 'does not stop container' do expect(subject).not_to receive(:execute).with('docker', 'stop', '-t', '1', cid) subject.stop(cid, 1) end end end describe '#rm' do context 'when container has been created' do before { allow(subject).to receive(:created?).and_return(true) } it 'removes the container' do expect(subject).to receive(:execute).with("docker-compose", "-f", "docker-compose.yml", "-p", "cwd", "rm", "-f", "docker_1", {}) subject.rm(cid) end end context 'when container has not been created' do before { allow(subject).to receive(:created?).and_return(false) } it 'does not attempt to remove the container' do expect(subject).not_to receive(:execute).with("docker-compose", "-f", "docker-compose.yml", "-p", "cwd", "rm", "-f", "docker_1", {}) subject.rm(cid) end end end describe '#inspect_container' do let(:data) { '[{"json": "value"}]' } before { allow(subject).to receive(:execute).and_return(data) } it 'inspects the container' do expect(subject).to receive(:execute).with('docker', 'inspect', cid) subject.inspect_container(cid) end it 'parses the json output' do expect(subject.inspect_container(cid)).to eq('json' => 'value') end end describe '#all_containers' do let(:containers) { "container1\ncontainer2" } before { allow(subject).to receive(:execute).and_return(containers) } it 'returns an array of all known containers' do expect(subject).to receive(:execute).with('docker', 'ps', '-a', '-q', '--no-trunc') expect(subject.all_containers).to eq(['container1', 'container2']) end end describe '#docker_bridge_ip' do let(:containers) { " inet 123.456.789.012/16 " } before { allow(subject).to receive(:execute).and_return(containers) } it 'returns an array of all known containers' do expect(subject).to receive(:execute).with('/sbin/ip', '-4', 'addr', 'show', 'scope', 'global', 'docker0') expect(subject.docker_bridge_ip).to eq('123.456.789.012') end end end vagrant-2.0.2/test/unit/plugins/providers/docker/driver_test.rb000066400000000000000000000157251323370221500247470ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/providers/docker/driver") describe VagrantPlugins::DockerProvider::Driver do let(:cmd_executed) { @cmd } let(:cid) { 'side-1-song-10' } before do allow(subject).to receive(:execute) { |*args| @cmd = args.join(' ') } end describe '#create' do let(:params) { { image: 'jimi/hendrix:eletric-ladyland', cmd: ['play', 'voodoo-chile'], ports: '8080:80', volumes: '/host/path:guest/path', detach: true, links: [[:janis, 'joplin'], [:janis, 'janis']], env: {key: 'value'}, name: cid, hostname: 'jimi-hendrix', privileged: true } } before { subject.create(params) } it 'runs a detached docker image' do expect(cmd_executed).to match(/^docker run .+ -d .+ #{Regexp.escape params[:image]}/) end it 'sets container name' do expect(cmd_executed).to match(/--name #{Regexp.escape params[:name]}/) end it 'forwards ports' do expect(cmd_executed).to match(/-p #{params[:ports]} .+ #{Regexp.escape params[:image]}/) end it 'shares folders' do expect(cmd_executed).to match(/-v #{params[:volumes]} .+ #{Regexp.escape params[:image]}/) end it 'links containers' do params[:links].each do |link| expect(cmd_executed).to match(/--link #{link.join(':')} .+ #{Regexp.escape params[:image]}/) end end it 'sets environmental variables' do expect(cmd_executed).to match(/-e key=value .+ #{Regexp.escape params[:image]}/) end it 'is able to run a privileged container' do expect(cmd_executed).to match(/--privileged .+ #{Regexp.escape params[:image]}/) end it 'sets the hostname if specified' do expect(cmd_executed).to match(/-h #{params[:hostname]} #{Regexp.escape params[:image]}/) end it 'executes the provided command' do expect(cmd_executed).to match(/#{Regexp.escape params[:image]} #{Regexp.escape params[:cmd].join(' ')}/) end end describe '#created?' do let(:result) { subject.created?(cid) } it 'performs the check on all containers list' do subject.created?(cid) expect(cmd_executed).to match(/docker ps \-a \-q/) end context 'when container exists' do before { allow(subject).to receive(:execute).and_return("foo\n#{cid}\nbar") } it { expect(result).to be_truthy } end context 'when container does not exist' do before { allow(subject).to receive(:execute).and_return("foo\n#{cid}extra\nbar") } it { expect(result).to be_falsey } end end describe '#pull' do it 'should pull images' do expect(subject).to receive(:execute).with('docker', 'pull', 'foo') subject.pull('foo') end end describe '#running?' do let(:result) { subject.running?(cid) } it 'performs the check on the running containers list' do subject.running?(cid) expect(cmd_executed).to match(/docker ps \-q/) expect(cmd_executed).to_not include('-a') end context 'when container exists' do before { allow(subject).to receive(:execute).and_return("foo\n#{cid}\nbar") } it { expect(result).to be_truthy } end context 'when container does not exist' do before { allow(subject).to receive(:execute).and_return("foo\n#{cid}extra\nbar") } it { expect(result).to be_falsey } end end describe '#privileged?' do it 'identifies privileged containers' do allow(subject).to receive(:inspect_container).and_return({'HostConfig' => {"Privileged" => true}}) expect(subject).to be_privileged(cid) end it 'identifies unprivileged containers' do allow(subject).to receive(:inspect_container).and_return({'HostConfig' => {"Privileged" => false}}) expect(subject).to_not be_privileged(cid) end end describe '#start' do context 'when container is running' do before { allow(subject).to receive(:running?).and_return(true) } it 'does not start the container' do expect(subject).to_not receive(:execute).with('docker', 'start', cid) subject.start(cid) end end context 'when container is not running' do before { allow(subject).to receive(:running?).and_return(false) } it 'starts the container' do expect(subject).to receive(:execute).with('docker', 'start', cid) subject.start(cid) end end end describe '#stop' do context 'when container is running' do before { allow(subject).to receive(:running?).and_return(true) } it 'stops the container' do expect(subject).to receive(:execute).with('docker', 'stop', '-t', '1', cid) subject.stop(cid, 1) end it "stops the container with the set timeout" do expect(subject).to receive(:execute).with('docker', 'stop', '-t', '5', cid) subject.stop(cid, 5) end end context 'when container is not running' do before { allow(subject).to receive(:running?).and_return(false) } it 'does not stop container' do expect(subject).to_not receive(:execute).with('docker', 'stop', '-t', '1', cid) subject.stop(cid, 1) end end end describe '#rm' do context 'when container has been created' do before { allow(subject).to receive(:created?).and_return(true) } it 'removes the container' do expect(subject).to receive(:execute).with('docker', 'rm', '-f', '-v', cid) subject.rm(cid) end end context 'when container has not been created' do before { allow(subject).to receive(:created?).and_return(false) } it 'does not attempt to remove the container' do expect(subject).to_not receive(:execute).with('docker', 'rm', '-f', '-v', cid) subject.rm(cid) end end end describe '#inspect_container' do let(:data) { '[{"json": "value"}]' } before { allow(subject).to receive(:execute).and_return(data) } it 'inspects the container' do expect(subject).to receive(:execute).with('docker', 'inspect', cid) subject.inspect_container(cid) end it 'parses the json output' do expect(subject.inspect_container(cid)).to eq('json' => 'value') end end describe '#all_containers' do let(:containers) { "container1\ncontainer2" } before { allow(subject).to receive(:execute).and_return(containers) } it 'returns an array of all known containers' do expect(subject).to receive(:execute).with('docker', 'ps', '-a', '-q', '--no-trunc') expect(subject.all_containers).to eq(['container1', 'container2']) end end describe '#docker_bridge_ip' do let(:containers) { " inet 123.456.789.012/16 " } before { allow(subject).to receive(:execute).and_return(containers) } it 'returns an array of all known containers' do expect(subject).to receive(:execute).with('/sbin/ip', '-4', 'addr', 'show', 'scope', 'global', 'docker0') expect(subject.docker_bridge_ip).to eq('123.456.789.012') end end end vagrant-2.0.2/test/unit/plugins/providers/docker/synced_folder_test.rb000066400000000000000000000016001323370221500262570ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/providers/docker/synced_folder") describe VagrantPlugins::DockerProvider::SyncedFolder do subject { described_class.new } describe "#usable?" do let(:machine) { double("machine") } before do allow(machine).to receive(:provider_name).and_return(:docker) end it "is usable" do expect(subject).to be_usable(machine) end it "is not usable if provider isn't docker" do allow(machine).to receive(:provider_name).and_return(:virtualbox) expect(subject).to_not be_usable(machine) end it "raises an error if bad provider if specified" do allow(machine).to receive(:provider_name).and_return(:virtualbox) expect { subject.usable?(machine, true) }. to raise_error(VagrantPlugins::DockerProvider::Errors::SyncedFolderNonDocker) end end end vagrant-2.0.2/test/unit/plugins/providers/hyperv/000077500000000000000000000000001323370221500221245ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/providers/hyperv/config_test.rb000066400000000000000000000025661323370221500247660ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/providers/hyperv/config") describe VagrantPlugins::HyperV::Config do describe "#ip_address_timeout" do it "can be set" do subject.ip_address_timeout = 180 subject.finalize! expect(subject.ip_address_timeout).to eq(180) end it "defaults to a number" do subject.finalize! expect(subject.ip_address_timeout).to eq(120) end end describe "#vlan_id" do it "can be set" do subject.vlan_id = 100 subject.finalize! expect(subject.vlan_id).to eq(100) end end describe "#mac" do it "can be set" do subject.mac = "001122334455" subject.finalize! expect(subject.mac).to eq("001122334455") end end describe "#vmname" do it "can be set" do subject.vmname = "test" subject.finalize! expect(subject.vmname).to eq("test") end end describe "#memory" do it "can be set" do subject.memory = 512 subject.finalize! expect(subject.memory).to eq(512) end end describe "#maxmemory" do it "can be set" do subject.maxmemory = 1024 subject.finalize! expect(subject.maxmemory).to eq(1024) end end describe "#cpus" do it "can be set" do subject.cpus = 2 subject.finalize! expect(subject.cpus).to eq(2) end end end vagrant-2.0.2/test/unit/plugins/providers/hyperv/provider_test.rb000066400000000000000000000065061323370221500253510ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/providers/hyperv/provider") describe VagrantPlugins::HyperV::Provider do let(:machine) { double("machine") } let(:platform) { double("platform") } let(:powershell) { double("powershell") } subject { described_class.new(machine) } before do stub_const("Vagrant::Util::Platform", platform) stub_const("Vagrant::Util::PowerShell", powershell) allow(machine).to receive(:id).and_return("foo") allow(platform).to receive(:windows?).and_return(true) allow(platform).to receive(:windows_admin?).and_return(true) allow(platform).to receive(:windows_hyperv_admin?).and_return(true) allow(powershell).to receive(:available?).and_return(true) end describe ".usable?" do subject { described_class } it "returns false if not windows" do allow(platform).to receive(:windows?).and_return(false) expect(subject).to_not be_usable end it "returns false if neither an admin nor a hyper-v admin" do allow(platform).to receive(:windows_admin?).and_return(false) allow(platform).to receive(:windows_hyperv_admin?).and_return(false) expect(subject).to_not be_usable end it "returns true if not an admin but is a hyper-v admin" do allow(platform).to receive(:windows_admin?).and_return(false) allow(platform).to receive(:windows_hyperv_admin?).and_return(true) expect(subject).to be_usable end it "returns false if powershell is not available" do allow(powershell).to receive(:available?).and_return(false) expect(subject).to_not be_usable end it "raises an exception if not windows" do allow(platform).to receive(:windows?).and_return(false) expect { subject.usable?(true) }. to raise_error(VagrantPlugins::HyperV::Errors::WindowsRequired) end it "raises an exception if neither an admin nor a hyper-v admin" do allow(platform).to receive(:windows_admin?).and_return(false) allow(platform).to receive(:windows_hyperv_admin?).and_return(false) expect { subject.usable?(true) }. to raise_error(VagrantPlugins::HyperV::Errors::AdminRequired) end it "raises an exception if neither an admin nor a hyper-v admin" do allow(platform).to receive(:windows_admin?).and_return(false) allow(platform).to receive(:windows_hyperv_admin?).and_return(false) expect { subject.usable?(true) }. to raise_error(VagrantPlugins::HyperV::Errors::AdminRequired) end it "raises an exception if powershell is not available" do allow(powershell).to receive(:available?).and_return(false) expect { subject.usable?(true) }. to raise_error(VagrantPlugins::HyperV::Errors::PowerShellRequired) end end describe "#driver" do it "is initialized" do expect(subject.driver).to be_kind_of(VagrantPlugins::HyperV::Driver) end end describe "#state" do 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(machine).to receive(:id).and_return("foo") expect(machine).to receive(:action).with(:read_state). and_return({ machine_state_id: :bar }) expect(subject.state.id).to eq(:bar) end end end vagrant-2.0.2/test/unit/plugins/providers/virtualbox/000077500000000000000000000000001323370221500230065ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/providers/virtualbox/action/000077500000000000000000000000001323370221500242635ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/providers/virtualbox/action/network_fix_ipv6_test.rb000066400000000000000000000132401323370221500311520ustar00rootroot00000000000000require_relative "../base" require 'socket' describe VagrantPlugins::ProviderVirtualBox::Action::NetworkFixIPv6 do include_context "unit" include_context "virtualbox" let(:iso_env) do env = isolated_environment env.vagrantfile("") env.create_vagrant_env end let(:machine) do iso_env.machine(iso_env.machine_names[0], :dummy).tap do |m| allow(m.provider).to receive(:driver).and_return(driver) end end let(:env) {{ machine: machine }} let(:app) { lambda { |*args| }} let(:driver) { double("driver") } subject { described_class.new(app, env) } it "ignores nil IP addresses" do allow(machine.config.vm).to receive(:networks) .and_return(private_network: { ip: nil }) expect { subject.call(env) }.to_not raise_error end it "blank nil IP addresses" do allow(machine.config.vm).to receive(:networks) .and_return(private_network: { ip: "" }) expect { subject.call(env) }.to_not raise_error end context "with IPv6 interfaces" do let(:socket) { double("socket") } before do # This address is only used to trigger the fixup code. It doesn't matter # what it is. allow(machine.config.vm).to receive(:networks) .and_return(private_network: { ip: 'fe:80::' }) allow(UDPSocket).to receive(:new).with(Socket::AF_INET6) .and_return(socket) allow(socket).to receive(:connect) end it "only checks the interfaces associated with the VM" do all_networks = [{name: "vboxnet0", ipv6: "dead:beef::", ipv6_prefix: 64, status: 'Up' }, {name: "vboxnet1", ipv6: "badd:badd::", ipv6_prefix: 64, status: 'Up' } ] ifaces = { 1 => {type: :hostonly, hostonly: "vboxnet0"} } allow(machine.provider.driver).to receive(:read_network_interfaces) .and_return(ifaces) allow(machine.provider.driver).to receive(:read_host_only_interfaces) .and_return(all_networks) subject.call(env) expect(socket).to have_received(:connect) .with(all_networks[0][:ipv6] + (['ffff']*4).join(':'), 80) end it "correctly uses the netmask to figure out the probe address" do all_networks = [{name: "vboxnet0", ipv6: "dead:beef::", ipv6_prefix: 113, status: 'Up' } ] ifaces = { 1 => {type: :hostonly, hostonly: "vboxnet0"} } allow(machine.provider.driver).to receive(:read_network_interfaces) .and_return(ifaces) allow(machine.provider.driver).to receive(:read_host_only_interfaces) .and_return(all_networks) subject.call(env) expect(socket).to have_received(:connect) .with(all_networks[0][:ipv6] + '7fff', 80) end it "should ignore interfaces that are down" do all_networks = [{name: "vboxnet0", ipv6: "dead:beef::", ipv6_prefix: 64, status: 'Down' } ] ifaces = { 1 => {type: :hostonly, hostonly: "vboxnet0"} } allow(machine.provider.driver).to receive(:read_network_interfaces) .and_return(ifaces) allow(machine.provider.driver).to receive(:read_host_only_interfaces) .and_return(all_networks) subject.call(env) expect(socket).to_not have_received(:connect) end it "should ignore interfaces without an IPv6 address" do all_networks = [{name: "vboxnet0", ipv6: "", ipv6_prefix: 0, status: 'Up' } ] ifaces = { 1 => {type: :hostonly, hostonly: "vboxnet0"} } allow(machine.provider.driver).to receive(:read_network_interfaces) .and_return(ifaces) allow(machine.provider.driver).to receive(:read_host_only_interfaces) .and_return(all_networks) subject.call(env) expect(socket).to_not have_received(:connect) end it "should ignore nat interfaces" do all_networks = [{name: "vboxnet0", ipv6: "", ipv6_prefix: 0, status: 'Up' } ] ifaces = { 1 => {type: :nat} } allow(machine.provider.driver).to receive(:read_network_interfaces) .and_return(ifaces) allow(machine.provider.driver).to receive(:read_host_only_interfaces) .and_return(all_networks) subject.call(env) expect(socket).to_not have_received(:connect) end it "should reconfigure an interface if unreachable" do all_networks = [{name: "vboxnet0", ipv6: "dead:beef::", ipv6_prefix: 64, status: 'Up' } ] ifaces = { 1 => {type: :hostonly, hostonly: "vboxnet0"} } allow(machine.provider.driver).to receive(:read_network_interfaces) .and_return(ifaces) allow(machine.provider.driver).to receive(:read_host_only_interfaces) .and_return(all_networks) allow(socket).to receive(:connect) .with(all_networks[0][:ipv6] + (['ffff']*4).join(':'), 80) .and_raise Errno::EHOSTUNREACH allow(machine.provider.driver).to receive(:reconfig_host_only) subject.call(env) expect(machine.provider.driver).to have_received(:reconfig_host_only) .with(all_networks[0]) end end end vagrant-2.0.2/test/unit/plugins/providers/virtualbox/action/network_test.rb000066400000000000000000000135341323370221500273460ustar00rootroot00000000000000require_relative "../base" require "vagrant/util/platform" describe VagrantPlugins::ProviderVirtualBox::Action::Network do include_context "unit" include_context "virtualbox" 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) 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 }} let(:app) { lambda { |*args| }} let(:driver) { double("driver") } let(:nics) { {} } subject { described_class.new(app, env) } before do allow(driver).to receive(:enable_adapters) allow(driver).to receive(:read_network_interfaces) { nics } end it "calls the next action in the chain" do called = false app = lambda { |*args| called = true } action = described_class.new(app, env) action.call(env) expect(called).to eq(true) end it "creates a host-only interface with an IPv6 address :1" do guest = double("guest") machine.config.vm.network 'private_network', { type: :static, ip: 'dead:beef::100' } #allow(driver).to receive(:read_bridged_interfaces) { [] } allow(driver).to receive(:read_host_only_interfaces) { [] } #allow(driver).to receive(:read_dhcp_servers) { [] } allow(machine).to receive(:guest) { guest } allow(driver).to receive(:create_host_only_network) {{ name: 'vboxnet0' }} allow(guest).to receive(:capability) interface_ip = 'dead:beef::1' subject.call(env) expect(driver).to have_received(:create_host_only_network).with({ adapter_ip: interface_ip, netmask: 64, }) expect(guest).to have_received(:capability).with(:configure_networks, [{ type: :static6, adapter_ip: 'dead:beef::1', ip: 'dead:beef::100', netmask: 64, auto_config: true, interface: nil }]) end it "raises the appropriate error when provided with an invalid IP address" do guest = double("guest") machine.config.vm.network 'private_network', { ip: '192.168.33.06' } expect{ subject.call(env) }.to raise_error(Vagrant::Errors::NetworkAddressInvalid) end it "raises no invalid network error when provided with a valid IP address" do guest = double("guest") machine.config.vm.network 'private_network', { ip: '192.168.33.6' } expect{ subject.call(env) }.not_to raise_error(Vagrant::Errors::NetworkAddressInvalid) end context "with a dhcp private network" do let(:bridgedifs) { [] } let(:hostonlyifs) { [] } let(:dhcpservers) { [] } let(:guest) { double("guest") } let(:network_args) {{ type: :dhcp }} before do machine.config.vm.network 'private_network', network_args allow(driver).to receive(:read_bridged_interfaces) { bridgedifs } allow(driver).to receive(:read_host_only_interfaces) { hostonlyifs } allow(driver).to receive(:read_dhcp_servers) { dhcpservers } allow(machine).to receive(:guest) { guest } end it "creates a host only interface and a dhcp server using default ips, then tells the guest to configure the network after boot" do allow(driver).to receive(:create_host_only_network) {{ name: 'vboxnet0' }} allow(driver).to receive(:create_dhcp_server) allow(guest).to receive(:capability) subject.call(env) expect(driver).to have_received(:create_host_only_network).with({ adapter_ip: '172.28.128.1', netmask: '255.255.255.0', }) expect(driver).to have_received(:create_dhcp_server).with('vboxnet0', { adapter_ip: "172.28.128.1", auto_config: true, ip: "172.28.128.1", mac: nil, name: nil, netmask: "255.255.255.0", nic_type: nil, type: :dhcp, dhcp_ip: "172.28.128.2", dhcp_lower: "172.28.128.3", dhcp_upper: "172.28.128.254", adapter: 2 }) expect(guest).to have_received(:capability).with(:configure_networks, [{ type: :dhcp, adapter_ip: "172.28.128.1", ip: "172.28.128.1", netmask: "255.255.255.0", auto_config: true, interface: nil }]) end context "when the default vbox dhcpserver is present from a fresh vbox install (see issue #3803)" do let(:dhcpservers) {[ { network_name: 'HostInterfaceNetworking-vboxnet0', network: 'vboxnet0', ip: '192.168.56.100', netmask: '255.255.255.0', lower: '192.168.56.101', upper: '192.168.56.254' } ]} it "removes the invalid dhcpserver so it won't collide with any host only interface" do allow(driver).to receive(:remove_dhcp_server) allow(driver).to receive(:create_host_only_network) {{ name: 'vboxnet0' }} allow(driver).to receive(:create_dhcp_server) allow(guest).to receive(:capability) subject.call(env) expect(driver).to have_received(:remove_dhcp_server).with('HostInterfaceNetworking-vboxnet0') end context "but the user has intentionally configured their network just that way" do let (:network_args) {{ type: :dhcp, adapter_ip: '192.168.56.1', dhcp_ip: '192.168.56.100', dhcp_lower: '192.168.56.101', dhcp_upper: '192.168.56.254' }} it "does not attempt to remove the dhcpserver" do allow(driver).to receive(:remove_dhcp_server) allow(driver).to receive(:create_host_only_network) {{ name: 'vboxnet0' }} allow(driver).to receive(:create_dhcp_server) allow(guest).to receive(:capability) subject.call(env) expect(driver).not_to have_received(:remove_dhcp_server).with('HostInterfaceNetworking-vboxnet0') end end end end end vagrant-2.0.2/test/unit/plugins/providers/virtualbox/action/prepare_nfs_settings_test.rb000066400000000000000000000130441323370221500320750ustar00rootroot00000000000000require_relative "../base" require "vagrant/util/platform" describe VagrantPlugins::ProviderVirtualBox::Action::PrepareNFSSettings do include_context "unit" include_context "virtualbox" 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) do iso_env.machine(iso_env.machine_names[0], :dummy).tap do |m| allow(m.provider).to receive(:driver).and_return(driver) end end let(:env) {{ machine: machine }} let(:app) { lambda { |*args| }} let(:driver) { double("driver") } let(:host) { double("host") } subject { described_class.new(app, env) } before do env[:test] = true allow(machine.env).to receive(:host) { host } allow(host).to receive(:capability).with(:nfs_installed) { true } # We don't care about smb support so return not installed allow(host).to receive(:capability?).with(:smb_installed).and_return(false) end it "calls the next action in the chain" do allow(driver).to receive(:read_network_interfaces).and_return({2 => {type: :hostonly, hostonly: "vmnet2"}}) allow(driver).to receive(:read_host_only_interfaces).and_return([{name: "vmnet2", ip: "1.2.3.4"}]) allow(driver).to receive(:read_guest_ip).with(1).and_return("2.3.4.5") called = false app = lambda { |*args| called = true } action = described_class.new(app, env) action.call(env) expect(called).to eq(true) end context "with an nfs synced folder" do let(:host_only_interfaces) { [{name: "vmnet2", ip: "1.2.3.4"}] } before do # We can't be on Windows, because NFS gets disabled on Windows allow(Vagrant::Util::Platform).to receive(:windows?).and_return(false) env[:machine].config.vm.synced_folder("/host/path", "/guest/path", type: "nfs") env[:machine].config.finalize! # Stub out the stuff so it just works by default allow(driver).to receive(:read_network_interfaces).and_return({ 2 => {type: :hostonly, hostonly: "vmnet2"}, }) allow(driver).to receive(:read_host_only_interfaces).and_return(host_only_interfaces) allow(driver).to receive(:read_guest_ip).with(1).and_return("2.3.4.5") # override sleep to 0 so test does not take seconds retry_options = subject.retry_options allow(subject).to receive(:retry_options).and_return(retry_options.merge(sleep: 0)) end context "with host interface netmask defined" do context "with machine IP included within host interface range" do let(:host_only_interfaces) { [{name: "vmnet2", ip: "2.3.4.1", netmask: "255.255.255.0"}] } it "sets nfs_host_ip and nfs_machine_ip properly" do subject.call(env) expect(env[:nfs_host_ip]).to eq("2.3.4.1") expect(env[:nfs_machine_ip]).to eq("2.3.4.5") end end context "with machine IP included within host interface range" do let(:host_only_interfaces) { [{name: "vmnet2", ip: "1.2.3.4", netmask: "255.255.255.0"}] } it "raises an error when the machine IP is not within host interface range" do expect{ subject.call(env) }.to raise_error(Vagrant::Errors::NFSNoHostonlyNetwork) end end end it "sets nfs_host_ip and nfs_machine_ip properly" do subject.call(env) expect(env[:nfs_host_ip]).to eq("1.2.3.4") expect(env[:nfs_machine_ip]).to eq("2.3.4.5") end it "raises an error when no host only adapter is configured" do allow(driver).to receive(:read_network_interfaces) {{}} expect { subject.call(env) }. to raise_error(Vagrant::Errors::NFSNoHostonlyNetwork) end it "retries through guest property not found errors" do raise_then_return = [ lambda { raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, guest_property: 'stub' }, lambda { "2.3.4.5" } ] allow(driver).to receive(:read_guest_ip) { raise_then_return.shift.call } subject.call(env) expect(env[:nfs_host_ip]).to eq("1.2.3.4") expect(env[:nfs_machine_ip]).to eq("2.3.4.5") end it "raises an error informing the user of a bug when the guest IP cannot be found" do allow(driver).to receive(:read_guest_ip) { raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, guest_property: 'stub' } expect { subject.call(env) }. to raise_error(Vagrant::Errors::NFSNoGuestIP) end it "allows statically configured guest IPs to work for NFS, even when guest property would fail" do env[:machine].config.vm.network :private_network, ip: "11.12.13.14" allow(driver).to receive(:read_guest_ip) { raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, guest_property: "stub" } subject.call(env) expect(env[:nfs_host_ip]).to eq("1.2.3.4") expect(env[:nfs_machine_ip]).to eq(["11.12.13.14"]) end it "allows statically configured guest IPs to co-exist with dynamic host only IPs for NFS" do env[:machine].config.vm.network :private_network, ip: "11.12.13.14" subject.call(env) expect(env[:nfs_host_ip]).to eq("1.2.3.4") expect(env[:nfs_machine_ip]).to eq(["11.12.13.14", "2.3.4.5"]) end it "allows the use of scoped hash overrides as options" do env[:machine].config.vm.network :private_network, virtualbox__ip: "11.12.13.14" subject.call(env) expect(env[:nfs_host_ip]).to eq("1.2.3.4") expect(env[:nfs_machine_ip]).to eq(["11.12.13.14", "2.3.4.5"]) end end end vagrant-2.0.2/test/unit/plugins/providers/virtualbox/action/prepare_nfs_valid_ids_test.rb000066400000000000000000000021721323370221500321730ustar00rootroot00000000000000require_relative "../base" describe VagrantPlugins::ProviderVirtualBox::Action::PrepareNFSValidIds do include_context "unit" include_context "virtualbox" 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) do iso_env.machine(iso_env.machine_names[0], :dummy).tap do |m| allow(m.provider).to receive(:driver).and_return(driver) end end let(:env) {{ machine: machine }} let(:app) { lambda { |*args| }} let(:driver) { double("driver") } subject { described_class.new(app, env) } before do allow(driver).to receive(:read_vms).and_return({}) end it "calls the next action in the chain" do called = false app = lambda { |*args| called = true } action = described_class.new(app, env) action.call(env) expect(called).to eq(true) end it "sets nfs_valid_ids" do hash = {"foo" => "1", "bar" => "4"} allow(driver).to receive(:read_vms).and_return(hash) subject.call(env) expect(env[:nfs_valid_ids]).to eql(hash.values) end end vagrant-2.0.2/test/unit/plugins/providers/virtualbox/base.rb000066400000000000000000000002301323370221500242400ustar00rootroot00000000000000# base test helper for virtualbox unit tests require_relative "../../../base" require_relative "support/shared/virtualbox_driver_version_4_x_examples" vagrant-2.0.2/test/unit/plugins/providers/virtualbox/cap/000077500000000000000000000000001323370221500235515ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/providers/virtualbox/cap/public_address_test.rb000066400000000000000000000023721323370221500301240ustar00rootroot00000000000000require_relative "../base" require Vagrant.source_root.join("plugins/providers/virtualbox/cap/public_address") describe VagrantPlugins::ProviderVirtualBox::Cap::PublicAddress 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) do iso_env.machine(iso_env.machine_names[0], :dummy).tap do |m| allow(m).to receive(:state).and_return(state) end end let(:state) do double(:state) end describe "#public_address" do it "returns nil when the machine is not running" do allow(state).to receive(:id).and_return(:not_created) expect(described_class.public_address(machine)).to be(nil) end it "returns nil when there is no ssh info" do allow(state).to receive(:id).and_return(:not_created) allow(machine).to receive(:ssh_info).and_return(nil) expect(described_class.public_address(machine)).to be(nil) end it "returns the host" do allow(state).to receive(:id).and_return(:running) allow(machine).to receive(:ssh_info).and_return(host: "test") expect(described_class.public_address(machine)).to eq("test") end end end vagrant-2.0.2/test/unit/plugins/providers/virtualbox/cap_test.rb000066400000000000000000000022571323370221500251430ustar00rootroot00000000000000require_relative "base" require Vagrant.source_root.join("plugins/providers/virtualbox/cap") describe VagrantPlugins::ProviderVirtualBox::Cap 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) do iso_env.machine(iso_env.machine_names[0], :dummy).tap do |m| allow(m.provider).to receive(:driver).and_return(driver) allow(m).to receive(:state).and_return(state) end end let(:driver) { double("driver") } let(:state) { double("state", id: :running) } describe "#forwarded_ports" do it "returns all the forwarded ports" do allow(driver).to receive(:read_forwarded_ports).and_return([ [nil, nil, 123, 456], [nil, nil, 245, 245], ]) expect(described_class.forwarded_ports(machine)).to eq({ 123 => 456, 245 => 245, }) end it "returns nil when the machine is not running" do allow(machine).to receive(:state).and_return(double(:state, id: :stopped)) expect(described_class.forwarded_ports(machine)).to be(nil) end end end vagrant-2.0.2/test/unit/plugins/providers/virtualbox/config_test.rb000066400000000000000000000035551323370221500256470ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/providers/virtualbox/config") describe VagrantPlugins::ProviderVirtualBox::Config do let(:machine) { double("machine") } def assert_invalid errors = subject.validate(machine) if !errors.values.any? { |v| !v.empty? } raise "No errors: #{errors.inspect}" end end def assert_valid errors = subject.validate(machine) if !errors.values.all? { |v| v.empty? } raise "Errors: #{errors.inspect}" end end def valid_defaults subject.image = "foo" end before do vm_config = double("vm_config") allow(vm_config).to receive(:networks).and_return([]) config = double("config") allow(config).to receive(:vm).and_return(vm_config) allow(machine).to receive(:config).and_return(config) end its "valid by default" do subject.finalize! assert_valid end context "defaults" do before { subject.finalize! } it { expect(subject.check_guest_additions).to be(true) } it { expect(subject.gui).to be(false) } it { expect(subject.name).to be_nil } it { expect(subject.functional_vboxsf).to be(true) } it "should have one NAT adapter" do expect(subject.network_adapters).to eql({ 1 => [:nat, {}], }) end end describe "#merge" do let(:one) { described_class.new } let(:two) { described_class.new } subject { one.merge(two) } it "merges the customizations" do one.customize ["foo"] two.customize ["bar"] expect(subject.customizations).to eq([ ["pre-boot", ["foo"]], ["pre-boot", ["bar"]]]) end end describe "#network_adapter" do it "configures additional adapters" do subject.network_adapter(2, :bridged, auto_config: true) expect(subject.network_adapters[2]).to eql( [:bridged, auto_config: true]) end end end vagrant-2.0.2/test/unit/plugins/providers/virtualbox/driver/000077500000000000000000000000001323370221500243015ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/providers/virtualbox/driver/version_4_0_test.rb000066400000000000000000000004421323370221500300140ustar00rootroot00000000000000require_relative "../base" describe VagrantPlugins::ProviderVirtualBox::Driver::Version_4_0 do include_context "virtualbox" let(:vbox_version) { "4.0.0" } subject { VagrantPlugins::ProviderVirtualBox::Driver::Meta.new(uuid) } it_behaves_like "a version 4.x virtualbox driver" end vagrant-2.0.2/test/unit/plugins/providers/virtualbox/driver/version_4_1_test.rb000066400000000000000000000004421323370221500300150ustar00rootroot00000000000000require_relative "../base" describe VagrantPlugins::ProviderVirtualBox::Driver::Version_4_1 do include_context "virtualbox" let(:vbox_version) { "4.1.0" } subject { VagrantPlugins::ProviderVirtualBox::Driver::Meta.new(uuid) } it_behaves_like "a version 4.x virtualbox driver" end vagrant-2.0.2/test/unit/plugins/providers/virtualbox/driver/version_4_2_test.rb000066400000000000000000000004421323370221500300160ustar00rootroot00000000000000require_relative "../base" describe VagrantPlugins::ProviderVirtualBox::Driver::Version_4_2 do include_context "virtualbox" let(:vbox_version) { "4.2.0" } subject { VagrantPlugins::ProviderVirtualBox::Driver::Meta.new(uuid) } it_behaves_like "a version 4.x virtualbox driver" end vagrant-2.0.2/test/unit/plugins/providers/virtualbox/driver/version_4_3_test.rb000066400000000000000000000004441323370221500300210ustar00rootroot00000000000000require_relative "../base" describe VagrantPlugins::ProviderVirtualBox::Driver::Version_4_3 do include_context "virtualbox" let(:vbox_version) { "4.3.0" } subject { VagrantPlugins::ProviderVirtualBox::Driver::Meta.new(uuid) } it_behaves_like "a version 4.x virtualbox driver" end vagrant-2.0.2/test/unit/plugins/providers/virtualbox/driver/version_5_0_test.rb000066400000000000000000000033211323370221500300140ustar00rootroot00000000000000require_relative "../base" describe VagrantPlugins::ProviderVirtualBox::Driver::Version_5_0 do include_context "virtualbox" let(:vbox_version) { "5.0.0" } subject { VagrantPlugins::ProviderVirtualBox::Driver::Meta.new(uuid) } it_behaves_like "a version 4.x virtualbox driver" describe "#shared_folders" do let(:folders) { [{:name=>"folder", :hostpath=>"/Users/brian/vagrant-folder", :transient=>false, :SharedFoldersEnableSymlinksCreate=>true}]} let(:folders_disabled) { [{:name=>"folder", :hostpath=>"/Users/brian/vagrant-folder", :transient=>false, :SharedFoldersEnableSymlinksCreate=>false}]} it "enables SharedFoldersEnableSymlinksCreate if true" do expect(subprocess).to receive(:execute). with("VBoxManage", "setextradata", anything, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/folder", "1", {:notify=>[:stdout, :stderr]}). and_return(subprocess_result(exit_code: 0)) expect(subprocess).to receive(:execute). with("VBoxManage", "sharedfolder", "add", anything, "--name", "folder", "--hostpath", "/Users/brian/vagrant-folder", {:notify=>[:stdout, :stderr]}). and_return(subprocess_result(exit_code: 0)) subject.share_folders(folders) end it "disables SharedFoldersEnableSymlinksCreate if false" do expect(subprocess).to receive(:execute). with("VBoxManage", "sharedfolder", "add", anything, "--name", "folder", "--hostpath", "/Users/brian/vagrant-folder", {:notify=>[:stdout, :stderr]}). and_return(subprocess_result(exit_code: 0)) subject.share_folders(folders_disabled) end end end vagrant-2.0.2/test/unit/plugins/providers/virtualbox/support/000077500000000000000000000000001323370221500245225ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/providers/virtualbox/support/shared/000077500000000000000000000000001323370221500257705ustar00rootroot00000000000000virtualbox_driver_version_4_x_examples.rb000066400000000000000000000201111323370221500362200ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/providers/virtualbox/support/sharedshared_examples "a version 4.x virtualbox driver" do |options| before do raise ArgumentError, "Need virtualbox context to use these shared examples." if !(defined? vbox_context) end describe "read_dhcp_servers" do before { expect(subprocess).to receive(:execute). with("VBoxManage", "list", "dhcpservers", an_instance_of(Hash)). and_return(subprocess_result(stdout: output)) } context "with empty output" do let(:output) { "" } it "returns an empty list" do expect(subject.read_dhcp_servers).to eq([]) end end context "with a single dhcp server" do let(:output) { <<-OUTPUT.gsub(/^ */, '') NetworkName: HostInterfaceNetworking-vboxnet0 IP: 172.28.128.2 NetworkMask: 255.255.255.0 lowerIPAddress: 172.28.128.3 upperIPAddress: 172.28.128.254 Enabled: Yes OUTPUT } it "returns a list with one entry describing that server" do expect(subject.read_dhcp_servers).to eq([{ network_name: 'HostInterfaceNetworking-vboxnet0', network: 'vboxnet0', ip: '172.28.128.2', netmask: '255.255.255.0', lower: '172.28.128.3', upper: '172.28.128.254', }]) end end context "with a multiple dhcp servers" do let(:output) { <<-OUTPUT.gsub(/^ */, '') NetworkName: HostInterfaceNetworking-vboxnet0 IP: 172.28.128.2 NetworkMask: 255.255.255.0 lowerIPAddress: 172.28.128.3 upperIPAddress: 172.28.128.254 Enabled: Yes NetworkName: HostInterfaceNetworking-vboxnet1 IP: 10.0.0.2 NetworkMask: 255.255.255.0 lowerIPAddress: 10.0.0.3 upperIPAddress: 10.0.0.254 Enabled: Yes OUTPUT } it "returns a list with one entry for each server" do expect(subject.read_dhcp_servers).to eq([ {network_name: 'HostInterfaceNetworking-vboxnet0', network: 'vboxnet0', ip: '172.28.128.2', netmask: '255.255.255.0', lower: '172.28.128.3', upper: '172.28.128.254'}, {network_name: 'HostInterfaceNetworking-vboxnet1', network: 'vboxnet1', ip: '10.0.0.2', netmask: '255.255.255.0', lower: '10.0.0.3', upper: '10.0.0.254'}, ]) end end end describe "read_guest_property" do it "reads the guest property of the machine referenced by the UUID" do key = "/Foo/Bar" expect(subprocess).to receive(:execute). with("VBoxManage", "guestproperty", "get", uuid, key, an_instance_of(Hash)). and_return(subprocess_result(stdout: "Value: Baz\n")) expect(subject.read_guest_property(key)).to eq("Baz") end it "raises a virtualBoxGuestPropertyNotFound exception when the value is not set" do key = "/Not/There" expect(subprocess).to receive(:execute). with("VBoxManage", "guestproperty", "get", uuid, key, an_instance_of(Hash)). and_return(subprocess_result(stdout: "No value set!")) expect { subject.read_guest_property(key) }. to raise_error Vagrant::Errors::VirtualBoxGuestPropertyNotFound end end describe "read_guest_ip" do it "reads the guest property for the provided adapter number" do key = "/VirtualBox/GuestInfo/Net/1/V4/IP" expect(subprocess).to receive(:execute). with("VBoxManage", "guestproperty", "get", uuid, key, an_instance_of(Hash)). and_return(subprocess_result(stdout: "Value: 127.1.2.3")) value = subject.read_guest_ip(1) expect(value).to eq("127.1.2.3") end it "does not accept 0.0.0.0 as a valid IP address" do key = "/VirtualBox/GuestInfo/Net/1/V4/IP" expect(subprocess).to receive(:execute). with("VBoxManage", "guestproperty", "get", uuid, key, an_instance_of(Hash)). and_return(subprocess_result(stdout: "Value: 0.0.0.0")) expect { subject.read_guest_ip(1) }. to raise_error Vagrant::Errors::VirtualBoxGuestPropertyNotFound end end describe "read_host_only_interfaces" do before { expect(subprocess).to receive(:execute). with("VBoxManage", "list", "hostonlyifs", an_instance_of(Hash)). and_return(subprocess_result(stdout: output)) } context "with empty output" do let(:output) { "" } it "returns an empty list" do expect(subject.read_host_only_interfaces).to eq([]) end end context "with a single host only interface" do let(:output) { <<-OUTPUT.gsub(/^ */, '') Name: vboxnet0 GUID: 786f6276-656e-4074-8000-0a0027000000 DHCP: Disabled IPAddress: 172.28.128.1 NetworkMask: 255.255.255.0 IPV6Address: IPV6NetworkMaskPrefixLength: 0 HardwareAddress: 0a:00:27:00:00:00 MediumType: Ethernet Status: Up VBoxNetworkName: HostInterfaceNetworking-vboxnet0 OUTPUT } it "returns a list with one entry describing that interface" do expect(subject.read_host_only_interfaces).to eq([{ name: 'vboxnet0', ip: '172.28.128.1', netmask: '255.255.255.0', ipv6_prefix: '0', status: 'Up', }]) end end context "with multiple host only interfaces" do let(:output) { <<-OUTPUT.gsub(/^ */, '') Name: vboxnet0 GUID: 786f6276-656e-4074-8000-0a0027000000 DHCP: Disabled IPAddress: 172.28.128.1 NetworkMask: 255.255.255.0 IPV6Address: IPV6NetworkMaskPrefixLength: 0 HardwareAddress: 0a:00:27:00:00:00 MediumType: Ethernet Status: Up VBoxNetworkName: HostInterfaceNetworking-vboxnet0 Name: vboxnet1 GUID: 5764a976-8479-8388-1245-8a0048080840 DHCP: Disabled IPAddress: 10.0.0.1 NetworkMask: 255.255.255.0 IPV6Address: IPV6NetworkMaskPrefixLength: 0 HardwareAddress: 0a:00:27:00:00:01 MediumType: Ethernet Status: Up VBoxNetworkName: HostInterfaceNetworking-vboxnet1 OUTPUT } it "returns a list with one entry for each interface" do expect(subject.read_host_only_interfaces).to eq([ {name: 'vboxnet0', ip: '172.28.128.1', netmask: '255.255.255.0', ipv6_prefix: "0", status: 'Up'}, {name: 'vboxnet1', ip: '10.0.0.1', netmask: '255.255.255.0', ipv6_prefix: "0", status: 'Up'}, ]) end end context "with an IPv6 host-only interface" do let(:output) { <<-OUTPUT.gsub(/^ */, '') Name: vboxnet1 GUID: 786f6276-656e-4174-8000-0a0027000001 DHCP: Disabled IPAddress: 192.168.57.1 NetworkMask: 255.255.255.0 IPV6Address: fde4:8dba:82e1:: IPV6NetworkMaskPrefixLength: 64 HardwareAddress: 0a:00:27:00:00:01 MediumType: Ethernet Status: Up VBoxNetworkName: HostInterfaceNetworking-vboxnet1 OUTPUT } it "returns a list with one entry describing that interface" do expect(subject.read_host_only_interfaces).to eq([{ name: 'vboxnet1', ip: '192.168.57.1', netmask: '255.255.255.0', ipv6: 'fde4:8dba:82e1::', ipv6_prefix: '64', status: 'Up', }]) end end end describe "remove_dhcp_server" do it "removes the dhcp server with the specified network name" do expect(subprocess).to receive(:execute). with("VBoxManage", "dhcpserver", "remove", "--netname", "HostInterfaceNetworking-vboxnet0", an_instance_of(Hash)). and_return(subprocess_result(stdout: '')) subject.remove_dhcp_server("HostInterfaceNetworking-vboxnet0") end end end vagrant-2.0.2/test/unit/plugins/providers/virtualbox/synced_folder_test.rb000066400000000000000000000165531323370221500272240ustar00rootroot00000000000000require "vagrant" require Vagrant.source_root.join("test/unit/base") require Vagrant.source_root.join("plugins/providers/virtualbox/config") require Vagrant.source_root.join("plugins/providers/virtualbox/synced_folder") describe VagrantPlugins::ProviderVirtualBox::SyncedFolder do include_context "unit" let(:machine) do double("machine").tap do |m| allow(m).to receive(:provider_config).and_return(VagrantPlugins::ProviderVirtualBox::Config.new) allow(m).to receive(:provider_name).and_return(:virtualbox) end end subject { described_class.new } before do machine.provider_config.finalize! end describe "#usable?" do it "should be with virtualbox provider" do allow(machine).to receive(:provider_name).and_return(:virtualbox) expect(subject).to be_usable(machine) end it "should not be with another provider" do allow(machine).to receive(:provider_name).and_return(:vmware_fusion) expect(subject).not_to be_usable(machine) end it "should not be usable if not functional vboxsf" do machine.provider_config.functional_vboxsf = false expect(subject).to_not be_usable(machine) end end describe "#prepare" do let(:driver) { double("driver") } let(:provider) { double("driver", driver: driver) } let(:folders) { {"/folder"=> {:SharedFoldersEnableSymlinksCreate=>true, :guestpath=>"/folder", :hostpath=>"/Users/brian/vagrant-folder", :disabled=>false, :__vagrantfile=>true}} } let(:folders_disabled) { {"/folder"=> {:SharedFoldersEnableSymlinksCreate=>false, :guestpath=>"/folder", :hostpath=>"/Users/brian/vagrant-folder", :disabled=>false, :__vagrantfile=>true}} } let(:folders_nosymvar) { {"/folder"=> {:guestpath=>"/folder", :hostpath=>"/Users/brian/vagrant-folder", :disabled=>false, :__vagrantfile=>true}} } before do allow(machine).to receive(:provider).and_return(provider) allow(machine).to receive(:env) allow(subject).to receive(:display_symlink_create_warning) end it "should prepare and share the folders" do expect(driver).to receive(:share_folders).with([{:name=>"folder", :hostpath=>"/Users/brian/vagrant-folder", :transient=>false, :SharedFoldersEnableSymlinksCreate=>true}]) subject.prepare(machine, folders, nil) end it "should prepare and share the folders without symlinks enabled" do expect(driver).to receive(:share_folders).with([{:name=>"folder", :hostpath=>"/Users/brian/vagrant-folder", :transient=>false, :SharedFoldersEnableSymlinksCreate=>false}]) subject.prepare(machine, folders_disabled, nil) end it "should prepare and share the folders without symlinks enabled with env var set" do stub_env('VAGRANT_DISABLE_VBOXSYMLINKCREATE'=>'1') expect(driver).to receive(:share_folders).with([{:name=>"folder", :hostpath=>"/Users/brian/vagrant-folder", :transient=>false, :SharedFoldersEnableSymlinksCreate=>false}]) subject.prepare(machine, folders_nosymvar, nil) end it "should prepare and share the folders and override symlink setting" do stub_env('VAGRANT_DISABLE_VBOXSYMLINKCREATE'=>'1') expect(driver).to receive(:share_folders).with([{:name=>"folder", :hostpath=>"/Users/brian/vagrant-folder", :transient=>false, :SharedFoldersEnableSymlinksCreate=>true}]) subject.prepare(machine, folders, nil) end end describe "#os_friendly_id" do it "should not replace normal chars" do expect(subject.send(:os_friendly_id, 'perfectly_valid0_name')).to eq('perfectly_valid0_name') end it "should replace spaces" do expect(subject.send(:os_friendly_id, 'Program Files')).to eq('Program_Files') end it "should replace leading underscore" do expect(subject.send(:os_friendly_id, '_vagrant')).to eq('vagrant') end it "should replace slash" do expect(subject.send(:os_friendly_id, 'va/grant')).to eq('va_grant') end it "should replace leading underscore and slash" do expect(subject.send(:os_friendly_id, '/vagrant')).to eq('vagrant') end it "should replace backslash" do expect(subject.send(:os_friendly_id, 'foo\\bar')).to eq('foo_bar') end end describe "#share_folders" do let(:folders){ {'folder1' => {hostpath: '/vagrant', transient: true}, 'folder2' => {hostpath: '/vagrant2', transient: false}} } let(:symlink_create_disable){ nil } let(:driver){ double("driver") } before do allow(subject).to receive(:display_symlink_create_warning) allow(machine).to receive(:env) allow(subject).to receive(:driver).and_return(driver) allow(driver).to receive(:share_folders) allow(ENV).to receive(:[]).with("VAGRANT_DISABLE_VBOXSYMLINKCREATE").and_return(symlink_create_disable) end it "should only add transient folder" do expect(driver).to receive(:share_folders).with(any_args) do |defs| expect(defs.size).to eq(1) end subject.send(:share_folders, machine, folders, true) end it "should display symlink create warning" do expect(subject).to receive(:display_symlink_create_warning) subject.send(:share_folders, machine, folders, true) end context "with create symlink globally disabled" do let(:symlink_create_disable){ "1" } it "should not enable option within definitions" do expect(driver).to receive(:share_folders).with(any_args) do |defs| expect(defs.first[:SharedFoldersEnableSymlinksCreate]).to be(false) end subject.send(:share_folders, machine, folders, true) end it "should not display symlink warning" do expect(subject).not_to receive(:display_symlink_create_warning) subject.send(:share_folders, machine, folders, true) end end end describe "#display_symlink_create_warning" do let(:env){ double("env", ui: double("ui"), data_dir: double("data_dir")) } let(:gate_file){ double("gate") } before{ allow(gate_file).to receive(:to_path).and_return("PATH") } after{ subject.send(:display_symlink_create_warning, env) } context "gate file does not exist" do before do allow(env.data_dir).to receive(:join).and_return(gate_file) allow(gate_file).to receive(:exist?).and_return(false) allow(FileUtils).to receive(:touch) allow(env.ui).to receive(:warn) end it "should create file" do expect(FileUtils).to receive(:touch).with("PATH") end it "should output warning to user" do expect(env.ui).to receive(:warn) end end context "gate file does exist" do before do allow(env.data_dir).to receive(:join).and_return(gate_file) allow(gate_file).to receive(:exist?).and_return(true) allow(FileUtils).to receive(:touch) allow(env.ui).to receive(:warn) end it "should not create/update file" do expect(FileUtils).not_to receive(:touch).with("PATH") end it "should not output warning to user" do expect(env.ui).not_to receive(:warn) end end end end vagrant-2.0.2/test/unit/plugins/provisioners/000077500000000000000000000000001323370221500213345ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/provisioners/ansible/000077500000000000000000000000001323370221500227515ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/provisioners/ansible/config/000077500000000000000000000000001323370221500242165ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/provisioners/ansible/config/guest_test.rb000066400000000000000000000060161323370221500267340ustar00rootroot00000000000000require_relative "../../../../base" require_relative "../../support/shared/config" require_relative "shared" require Vagrant.source_root.join("plugins/provisioners/ansible/config/guest") describe VagrantPlugins::Ansible::Config::Guest do include_context "unit" subject { described_class.new } # FIXME: machine.ui.warn stub is not working as expected... let(:machine) { double("machine", env: Vagrant::Environment.new) } let(:communicator) { double("communicator") } let(:existing_file) { "this/path/is/a/stub" } it "supports a list of options" do supported_options = %w( become become_user compatibility_mode config_file extra_vars galaxy_command galaxy_role_file galaxy_roles_path groups host_vars install install_mode inventory_path limit pip_args playbook playbook_command provisioning_path raw_arguments skip_tags start_at_task sudo sudo_user tags tmp_path vault_password_file verbose version ) expect(get_provisioner_option_names(described_class)).to eql(supported_options) end describe "default options handling" do it_behaves_like "options shared by both Ansible provisioners" it "assigns default values to unset guest-specific options" do subject.finalize! expect(subject.install).to be(true) expect(subject.install_mode).to eql(:default) expect(subject.provisioning_path).to eql("/vagrant") expect(subject.tmp_path).to eql("/tmp/vagrant-ansible") end end describe "#validate" do before do subject.playbook = existing_file end it_behaves_like "an Ansible provisioner", "/vagrant", "local" it "falls back to :default install_mode for any invalid setting" do subject.install_mode = "from_source" subject.finalize! result = subject.validate(machine) expect(subject.install_mode).to eql(:default) end it "supports :pip install_mode" do subject.install_mode = "pip" subject.finalize! result = subject.validate(machine) expect(subject.install_mode).to eql(:pip) end it "supports :pip_args_only install_mode" do subject.install_mode = "pip_args_only" subject.finalize! result = subject.validate(machine) expect(subject.install_mode).to eql(:pip_args_only) end end end vagrant-2.0.2/test/unit/plugins/provisioners/ansible/config/host_test.rb000066400000000000000000000100471323370221500265610ustar00rootroot00000000000000require_relative "../../../../base" require_relative "../../support/shared/config" require_relative "shared" require Vagrant.source_root.join("plugins/provisioners/ansible/config/host") describe VagrantPlugins::Ansible::Config::Host, :skip_windows => true do include_context "unit" subject { described_class.new } let(:machine) { double("machine", env: Vagrant::Environment.new) } let(:existing_file) { File.expand_path(__FILE__) } it "supports a list of options" do supported_options = %w( ask_become_pass ask_sudo_pass ask_vault_pass become become_user compatibility_mode config_file extra_vars force_remote_user galaxy_command galaxy_role_file galaxy_roles_path groups host_key_checking host_vars inventory_path limit playbook playbook_command raw_arguments raw_ssh_args skip_tags start_at_task sudo sudo_user tags vault_password_file verbose version ) expect(get_provisioner_option_names(described_class)).to eql(supported_options) end describe "default options handling" do it_behaves_like "options shared by both Ansible provisioners" it "assigns default values to unset host-specific options" do subject.finalize! expect(subject.ask_become_pass).to be(false) expect(subject.ask_sudo_pass).to be(false) # deprecated expect(subject.ask_vault_pass).to be(false) expect(subject.force_remote_user).to be(true) expect(subject.host_key_checking).to be(false) expect(subject.raw_ssh_args).to be_nil end end describe "force_remote_user option" do it_behaves_like "any VagrantConfigProvisioner strict boolean attribute", :force_remote_user, true end describe "host_key_checking option" do it_behaves_like "any VagrantConfigProvisioner strict boolean attribute", :host_key_checking, false end describe "ask_become_pass option" do it_behaves_like "any VagrantConfigProvisioner strict boolean attribute", :ask_become_pass, false end describe "ask_sudo_pass option" do before do # Filter the deprecation notice allow($stdout).to receive(:puts) end it_behaves_like "any VagrantConfigProvisioner strict boolean attribute", :ask_sudo_pass, false it_behaves_like "any deprecated option", :ask_sudo_pass, :ask_become_pass, true end describe "ask_vault_pass option" do it_behaves_like "any VagrantConfigProvisioner strict boolean attribute", :ask_vault_pass, false end describe "#validate" do before do subject.playbook = existing_file end it_behaves_like "an Ansible provisioner", "", "remote" it "returns an error if the raw_ssh_args is of the wrong data type" do subject.raw_ssh_args = { arg1: 1, arg2: "foo" } subject.finalize! result = subject.validate(machine) expect(result["ansible remote provisioner"]).to eql([ I18n.t("vagrant.provisioners.ansible.errors.raw_ssh_args_invalid", type: subject.raw_ssh_args.class.to_s, value: subject.raw_ssh_args.to_s) ]) end it "converts a raw_ssh_args option defined as a String into an Array" do subject.raw_arguments = "-o ControlMaster=no" subject.finalize! result = subject.validate(machine) expect(subject.raw_arguments).to eql(["-o ControlMaster=no"]) end end end vagrant-2.0.2/test/unit/plugins/provisioners/ansible/config/shared.rb000066400000000000000000000150271323370221500260160ustar00rootroot00000000000000shared_examples_for 'options shared by both Ansible provisioners' do it "assigns default values to unset common options" do subject.finalize! expect(subject.become).to be(false) expect(subject.become_user).to be_nil expect(subject.compatibility_mode).to eql(VagrantPlugins::Ansible::COMPATIBILITY_MODE_AUTO) expect(subject.config_file).to be_nil expect(subject.extra_vars).to be_nil expect(subject.galaxy_command).to eql("ansible-galaxy install --role-file=%{role_file} --roles-path=%{roles_path} --force") expect(subject.galaxy_role_file).to be_nil expect(subject.galaxy_roles_path).to be_nil expect(subject.groups).to eq({}) expect(subject.host_vars).to eq({}) expect(subject.inventory_path).to be_nil expect(subject.limit).to be_nil expect(subject.playbook).to be_nil expect(subject.playbook_command).to eql("ansible-playbook") expect(subject.raw_arguments).to be_nil expect(subject.skip_tags).to be_nil expect(subject.start_at_task).to be_nil expect(subject.sudo).to be(false) # deprecated expect(subject.sudo_user).to be_nil # deprecated expect(subject.tags).to be_nil expect(subject.vault_password_file).to be_nil expect(subject.verbose).to be(false) expect(subject.version).to be_empty end end shared_examples_for 'any deprecated option' do |deprecated_option, new_option, option_value| it "shows the deprecation message" do expect($stdout).to receive(:puts).with("DEPRECATION: The '#{deprecated_option}' option for the Ansible provisioner is deprecated.").and_return(nil) expect($stdout).to receive(:puts).with("Please use the '#{new_option}' option instead.").and_return(nil) expect($stdout).to receive(:puts).with("The '#{deprecated_option}' option will be removed in a future release of Vagrant.\n\n").and_return(nil) subject.send("#{deprecated_option}=", option_value) subject.finalize! end end shared_examples_for 'an Ansible provisioner' do | path_prefix, ansible_setup | provisioner_label = "ansible #{ansible_setup} provisioner" provisioner_system = ansible_setup == "local" ? "guest" : "host" it "returns an error if the playbook option is undefined" do subject.playbook = nil subject.finalize! result = subject.validate(machine) expect(result[provisioner_label]).to eql([ I18n.t("vagrant.provisioners.ansible.errors.no_playbook") ]) end describe "compatibility_mode option" do VagrantPlugins::Ansible::COMPATIBILITY_MODES.each do |valid_mode| it "supports compatibility mode '#{valid_mode}'" do subject.compatibility_mode = valid_mode subject.finalize! result = subject.validate(machine) expect(subject.compatibility_mode).to eql(valid_mode) end end it "returns an error if the compatibility mode is not set" do subject.compatibility_mode = nil subject.finalize! result = subject.validate(machine) expect(result[provisioner_label]).to eql([ I18n.t("vagrant.provisioners.ansible.errors.no_compatibility_mode", valid_modes: "'auto', '1.8', '2.0'") ]) end %w(invalid 1.9 2.3).each do |invalid_mode| it "returns an error if the compatibility mode is invalid (e.g. '#{invalid_mode}')" do subject.compatibility_mode = invalid_mode subject.finalize! result = subject.validate(machine) expect(result[provisioner_label]).to eql([ I18n.t("vagrant.provisioners.ansible.errors.no_compatibility_mode", valid_modes: "'auto', '1.8', '2.0'") ]) end end end it "passes if the extra_vars option is a hash" do subject.extra_vars = { var1: 1, var2: "foo" } subject.finalize! result = subject.validate(machine) expect(result[provisioner_label]).to eql([]) end it "returns an error if the extra_vars option is of wrong data type" do subject.extra_vars = ["var1", 3, "var2", 5] subject.finalize! result = subject.validate(machine) expect(result[provisioner_label]).to eql([ I18n.t("vagrant.provisioners.ansible.errors.extra_vars_invalid", type: subject.extra_vars.class.to_s, value: subject.extra_vars.to_s) ]) end it "converts a raw_arguments option defined as a String into an Array" do subject.raw_arguments = "--foo=bar" subject.finalize! result = subject.validate(machine) expect(subject.raw_arguments).to eql(%w(--foo=bar)) end it "returns an error if the raw_arguments is of the wrong data type" do subject.raw_arguments = { arg1: 1, arg2: "foo" } subject.finalize! result = subject.validate(machine) expect(result[provisioner_label]).to eql([ I18n.t("vagrant.provisioners.ansible.errors.raw_arguments_invalid", type: subject.raw_arguments.class.to_s, value: subject.raw_arguments.to_s) ]) end it "it collects and returns all detected errors" do subject.compatibility_mode = nil subject.playbook = nil subject.extra_vars = ["var1", 3, "var2", 5] subject.raw_arguments = { arg1: 1, arg2: "foo" } subject.finalize! result = subject.validate(machine) expect(result[provisioner_label].size).to eql(4) expect(result[provisioner_label]).to include( I18n.t("vagrant.provisioners.ansible.errors.no_compatibility_mode", valid_modes: "'auto', '1.8', '2.0'")) expect(result[provisioner_label]).to include( I18n.t("vagrant.provisioners.ansible.errors.no_playbook")) expect(result[provisioner_label]).to include( I18n.t("vagrant.provisioners.ansible.errors.extra_vars_invalid", type: subject.extra_vars.class.to_s, value: subject.extra_vars.to_s)) expect(result[provisioner_label]).to include( I18n.t("vagrant.provisioners.ansible.errors.raw_arguments_invalid", type: subject.raw_arguments.class.to_s, value: subject.raw_arguments.to_s)) end describe "become option" do it_behaves_like "any VagrantConfigProvisioner strict boolean attribute", :become, false end describe "sudo option" do before do # Filter the deprecation notice allow($stdout).to receive(:puts) end it_behaves_like "any VagrantConfigProvisioner strict boolean attribute", :sudo, false it_behaves_like "any deprecated option", :sudo, :become, true end describe "sudo_user option" do before do # Filter the deprecation notice allow($stdout).to receive(:puts) end it_behaves_like "any deprecated option", :sudo_user, :become_user, "foo" end end vagrant-2.0.2/test/unit/plugins/provisioners/ansible/provisioner_test.rb000066400000000000000000001475121323370221500267260ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/provisioners/ansible/config/host") require Vagrant.source_root.join("plugins/provisioners/ansible/provisioner/host") # # Helper Functions # def find_last_argument_after(ref_index, ansible_playbook_args, arg_pattern) subset = ansible_playbook_args[(ref_index + 1)..(ansible_playbook_args.length-2)].reverse subset.each do |i| return true if i =~ arg_pattern end return false end describe VagrantPlugins::Ansible::Provisioner::Host do include_context "unit" subject { described_class.new(machine, config) } let(:iso_env) do # We have to create a Vagrantfile so there is a Vagrant Environment to provide: # - a location for the generated inventory # - multi-machines configuration env = isolated_environment env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" config.vm.define :machine1 config.vm.define :machine2 end VF env.create_vagrant_env end let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } let(:config) { VagrantPlugins::Ansible::Config::Host.new } let(:ssh_info) {{ private_key_path: ['/path/to/my/key'], keys_only: true, username: 'testuser', host: '127.0.0.1', port: 2223 }} let(:default_execute_result) { Vagrant::Util::Subprocess::Result.new(0, "", "") } let(:existing_file) { File.expand_path(__FILE__) } let(:generated_inventory_dir) { File.join(machine.env.local_data_path, %w(provisioners ansible inventory)) } let(:generated_inventory_file) { File.join(generated_inventory_dir, 'vagrant_ansible_inventory') } before do allow(Vagrant::Util::Platform).to receive(:solaris?).and_return(false) allow(machine).to receive(:ssh_info).and_return(ssh_info) allow(machine.env).to receive(:active_machines) .and_return([[iso_env.machine_names[0], :dummy], [iso_env.machine_names[1], :dummy]]) stubbed_ui = Vagrant::UI::Colored.new allow(stubbed_ui).to receive(:detail).and_return("") allow(stubbed_ui).to receive(:warn).and_return("") allow(machine.env).to receive(:ui).and_return(stubbed_ui) config.playbook = 'playbook.yml' end # # Class methods for code reuse across examples # def self.it_should_check_ansible_version() it "execute 'ansible --version' before executing 'ansible-playbook'" do expect(Vagrant::Util::Subprocess).to receive(:execute). once.with('ansible', '--version', { :notify => [:stdout, :stderr] }) expect(Vagrant::Util::Subprocess).to receive(:execute). once.with('ansible-playbook', any_args) end end def self.it_should_set_arguments_and_environment_variables( expected_args_count = 5, expected_vars_count = 4, expected_host_key_checking = false, expected_transport_mode = "ssh") it "sets implicit arguments in a specific order" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| expect(args[1]).to eq("--connection=ssh") expect(args[2]).to eq("--timeout=30") inventory_count = args.count { |x| x =~ /^--inventory-file=.+$/ } expect(inventory_count).to be > 0 expect(args[args.length-2]).to eq("playbook.yml") }.and_return(default_execute_result) end it "sets --limit argument" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| all_limits = args.select { |x| x =~ /^(--limit=|-l)/ } if config.raw_arguments raw_limits = config.raw_arguments.select { |x| x =~ /^(--limit=|-l)/ } expect(all_limits.length - raw_limits.length).to eq(1) expect(all_limits.last).to eq(raw_limits.last) else if config.limit limit = config.limit.kind_of?(Array) ? config.limit.join(',') : config.limit expect(all_limits.last).to eq("--limit=#{limit}") else expect(all_limits.first).to eq("--limit=#{machine.name}") end end }.and_return(default_execute_result) end it "exports environment variables" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| cmd_opts = args.last if expected_host_key_checking expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to_not include("-o UserKnownHostsFile=/dev/null") else expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o UserKnownHostsFile=/dev/null") end expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o IdentitiesOnly=yes") expect(cmd_opts[:env]['ANSIBLE_FORCE_COLOR']).to eql("true") expect(cmd_opts[:env]).to_not include("ANSIBLE_NOCOLOR") expect(cmd_opts[:env]['ANSIBLE_HOST_KEY_CHECKING']).to eql(expected_host_key_checking.to_s) expect(cmd_opts[:env]['PYTHONUNBUFFERED']).to eql(1) }.and_return(default_execute_result) end # "roughly" verify that only expected args/vars have been defined by the provisioner it "sets the expected number of arguments and environment variables" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| expect(args.length - 2).to eq(expected_args_count) expect(args.last[:env].length).to eq(expected_vars_count) }.and_return(default_execute_result) end it "enables '#{expected_transport_mode}' as default transport mode" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| index = args.rindex("--connection=#{expected_transport_mode}") expect(index).to be > 0 expect(find_last_argument_after(index, args, /--connection=\w+/)).to be(false) }.and_return(default_execute_result) end end def self.it_should_set_optional_arguments(arg_map) it "sets optional arguments" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| arg_map.each_pair do |vagrant_option, ansible_argument| index = args.index(ansible_argument) if config.send(vagrant_option) expect(index).to be > 0 else expect(index).to be_nil end end }.and_return(default_execute_result) end end def self.it_should_explicitly_enable_ansible_ssh_control_persist_defaults it "configures ControlPersist (like Ansible defaults) via ANSIBLE_SSH_ARGS" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| cmd_opts = args.last expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o ControlMaster=auto") expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o ControlPersist=60s") }.and_return(default_execute_result) end end def self.it_should_create_and_use_generated_inventory(with_user = true) it "generates an inventory with all active machines" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| expect(config.inventory_path).to be_nil expect(File.exists?(generated_inventory_file)).to be(true) inventory_content = File.read(generated_inventory_file) _ssh = config.compatibility_mode == VagrantPlugins::Ansible::COMPATIBILITY_MODE_V2_0 ? "" : "_ssh" if with_user expect(inventory_content).to include("#{machine.name} ansible#{_ssh}_host=#{machine.ssh_info[:host]} ansible#{_ssh}_port=#{machine.ssh_info[:port]} ansible#{_ssh}_user='#{machine.ssh_info[:username]}' ansible_ssh_private_key_file='#{machine.ssh_info[:private_key_path][0]}'\n") else expect(inventory_content).to include("#{machine.name} ansible#{_ssh}_host=#{machine.ssh_info[:host]} ansible#{_ssh}_port=#{machine.ssh_info[:port]} ansible_ssh_private_key_file='#{machine.ssh_info[:private_key_path][0]}'\n") end expect(inventory_content).to include("# MISSING: '#{iso_env.machine_names[1]}' machine was probably removed without using Vagrant. This machine should be recreated.\n") }.and_return(default_execute_result) end it "sets as ansible inventory the directory containing the auto-generated inventory file" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| inventory_index = args.rindex("--inventory-file=#{generated_inventory_dir}") expect(inventory_index).to be > 0 expect(find_last_argument_after(inventory_index, args, /--inventory-file=\w+/)).to be(false) }.and_return(default_execute_result) end end def ensure_that_config_is_valid # Abort the test when an invalid configuration is detected config.validate(machine) if config._detected_errors.length > 0 raise "Invalid Provisioner Configuration! Detected Errors:\n#{config._detected_errors.to_s}" end end describe "#provision" do before do unless RSpec.current_example.metadata[:skip_before] config.finalize! allow(Vagrant::Util::Subprocess).to receive(:execute) .and_return(Vagrant::Util::Subprocess::Result.new(0, "", "")) allow(subject).to receive(:check_path) end end after do unless RSpec.current_example.metadata[:skip_after] ensure_that_config_is_valid subject.provision end end describe 'checking existence of Ansible configuration files' do STUBBED_INVALID_PATH = "/test/239nfmd/invalid_path".freeze it 'raises an error when the `playbook` file does not exist', skip_before: true, skip_after: true do allow(subject).to receive(:check_path).and_raise(VagrantPlugins::Ansible::Errors::AnsibleError, _key: :config_file_not_found, config_option: "playbook", path: STUBBED_INVALID_PATH, system: "host") config.playbook = STUBBED_INVALID_PATH config.finalize! ensure_that_config_is_valid expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsibleError, "`playbook` does not exist on the host: #{STUBBED_INVALID_PATH}") end %w(config_file extra_vars inventory_path galaxy_role_file vault_password_file).each do |option_name| it "raises an error when the '#{option_name}' does not exist", skip_before: true, skip_after: true do allow(Vagrant::Util::Subprocess).to receive(:execute) .and_return( Vagrant::Util::Subprocess::Result.new(0, "", "")) config.playbook = existing_file config.send(option_name + '=', STUBBED_INVALID_PATH) config.finalize! ensure_that_config_is_valid expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsibleError, "`#{option_name}` does not exist on the host: #{STUBBED_INVALID_PATH}") end end end describe 'when ansible-playbook fails' do it "raises an error", skip_before: true, skip_after: true do config.finalize! ensure_that_config_is_valid allow(subject).to receive(:check_path) allow(Vagrant::Util::Subprocess).to receive(:execute) .and_return(Vagrant::Util::Subprocess::Result.new(1, "", "")) expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsibleCommandFailed) end end describe "with default options" do it_should_check_ansible_version it_should_set_arguments_and_environment_variables it_should_create_and_use_generated_inventory it "does not add any group section to the generated inventory" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { inventory_content = File.read(generated_inventory_file) expect(inventory_content).to_not match(/^\s*\[^\\+\]\s*$/) }.and_return(default_execute_result) end it "doesn't show the ansible-playbook command" do expect(machine.env.ui).to_not receive(:detail).with(/ansible-playbook/) end end describe "deprecated 'sudo' options are aliases for equivalent 'become' options" do before do # Filter the deprecation notices allow($stdout).to receive(:puts) config.sudo = true config.sudo_user = 'deployer' config.ask_sudo_pass = true end it_should_set_optional_arguments({"sudo" => "--sudo", "sudo_user" => "--sudo-user=deployer", "ask_sudo_pass" => "--ask-sudo-pass", "become" => "--sudo", "become_user" => "--sudo-user=deployer", "ask_become_pass" => "--ask-sudo-pass"}) end context "with compatibility_mode 'auto'" do before do config.compatibility_mode = VagrantPlugins::Ansible::COMPATIBILITY_MODE_AUTO end valid_versions = { "0.6": VagrantPlugins::Ansible::COMPATIBILITY_MODE_V1_8, "1.9.4": VagrantPlugins::Ansible::COMPATIBILITY_MODE_V1_8, "2.5.0.0-rc1": VagrantPlugins::Ansible::COMPATIBILITY_MODE_V2_0, "2.x.y.z": VagrantPlugins::Ansible::COMPATIBILITY_MODE_V2_0, "4.3.2.1": VagrantPlugins::Ansible::COMPATIBILITY_MODE_V2_0, } valid_versions.each_pair do |ansible_version, mode| describe "and ansible version #{ansible_version}" do before do allow(subject).to receive(:gather_ansible_version).and_return("ansible #{ansible_version}\n...\n") end it "detects the compatibility mode #{mode}" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| expect(config.compatibility_mode).to eq(mode) }.and_return(default_execute_result) end it "warns about compatibility mode auto-detection being used" do expect(machine.env.ui).to receive(:warn).with( I18n.t("vagrant.provisioners.ansible.compatibility_mode_warning", compatibility_mode: mode, ansible_version: ansible_version) + "\n") end end end invalid_versions = [ "ansible devel", "anything 1.2", "2.9.2.1", ] invalid_versions.each do |unknown_ansible_version| describe "and `ansible --version` returning '#{unknown_ansible_version}'" do before do allow(subject).to receive(:gather_ansible_version).and_return(unknown_ansible_version) end it "applies the safest compatibility mode ('#{VagrantPlugins::Ansible::SAFE_COMPATIBILITY_MODE}')" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| expect(config.compatibility_mode).to eq(VagrantPlugins::Ansible::SAFE_COMPATIBILITY_MODE) }.and_return(default_execute_result) end it "warns about not being able to detect the best compatibility mode" do expect(machine.env.ui).to receive(:warn).with( I18n.t("vagrant.provisioners.ansible.compatibility_mode_not_detected", compatibility_mode: VagrantPlugins::Ansible::SAFE_COMPATIBILITY_MODE, gathered_version: unknown_ansible_version) + "\n") end end end end context "with compatibility_mode '#{VagrantPlugins::Ansible::COMPATIBILITY_MODE_V1_8}'" do before do config.compatibility_mode = VagrantPlugins::Ansible::COMPATIBILITY_MODE_V1_8 end it_should_check_ansible_version it_should_create_and_use_generated_inventory it "doesn't warn about compatibility mode auto-detection" do expect(machine.env.ui).to_not receive(:warn) end end context "with compatibility_mode '#{VagrantPlugins::Ansible::COMPATIBILITY_MODE_V2_0}'" do before do config.compatibility_mode = VagrantPlugins::Ansible::COMPATIBILITY_MODE_V2_0 allow(subject).to receive(:gather_ansible_version).and_return("ansible 2.3.0.0\n...\n") end it_should_create_and_use_generated_inventory it "doesn't warn about compatibility mode auto-detection" do expect(machine.env.ui).to_not receive(:warn) end describe "and an incompatible ansible version" do before do allow(subject).to receive(:gather_ansible_version).and_return("ansible 1.9.3\n...\n") end it "raises a compatibility conflict error", skip_before: false, skip_after: true do ensure_that_config_is_valid expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsibleCompatibilityModeConflict) end end describe "deprecated 'sudo' options are aliases for equivalent 'become' options" do before do # Filter the deprecation notices allow($stdout).to receive(:puts) config.sudo = true config.sudo_user = 'deployer' config.ask_sudo_pass = true end it_should_set_optional_arguments({"sudo" => "--become", "sudo_user" => "--become-user=deployer", "ask_sudo_pass" => "--ask-become-pass", "become" => "--become", "become_user" => "--become-user=deployer", "ask_become_pass" => "--ask-become-pass"}) end end describe "with playbook_command option" do before do config.playbook_command = "custom-ansible-playbook" # set the compatibility mode to ensure that only ansible-playbook is excuted config.compatibility_mode = VagrantPlugins::Ansible::COMPATIBILITY_MODE_V1_8 end it "uses custom playbook_command to run playbooks" do expect(Vagrant::Util::Subprocess).to receive(:execute) .with("custom-ansible-playbook", any_args) .and_return(default_execute_result) end end describe "with host_vars option" do it_should_create_and_use_generated_inventory it "adds host variables (given in Hash format) to the generated inventory" do config.host_vars = { machine1: { "http_port" => 80, "comments" => "'some text with spaces and quotes'", "description" => "text with spaces but no quotes", } } expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { inventory_content = File.read(generated_inventory_file) expect(inventory_content).to match("^" + Regexp.quote(machine.name) + ".+http_port=80 comments='some text with spaces and quotes' description='text with spaces but no quotes'") }.and_return(default_execute_result) end it "adds host variables (given in Array format) to the generated inventory" do config.host_vars = { machine1: ["http_port=80", "maxRequestsPerChild=808"] } expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { inventory_content = File.read(generated_inventory_file) expect(inventory_content).to match("^" + Regexp.quote(machine.name) + ".+http_port=80 maxRequestsPerChild=808") }.and_return(default_execute_result) end it "adds host variables (given in String format) to the generated inventory " do config.host_vars = { :machine1 => "http_port=80 maxRequestsPerChild=808" } expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { inventory_content = File.read(generated_inventory_file) expect(inventory_content).to match("^" + Regexp.quote(machine.name) + ".+http_port=80 maxRequestsPerChild=808") }.and_return(default_execute_result) end it "retrieves the host variables by machine name, also in String format" do config.host_vars = { "machine1" => "http_port=80 maxRequestsPerChild=808" } expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { inventory_content = File.read(generated_inventory_file) expect(inventory_content).to match("^" + Regexp.quote(machine.name) + ".+http_port=80 maxRequestsPerChild=808") }.and_return(default_execute_result) end end describe "with groups option" do it_should_create_and_use_generated_inventory it "adds group sections to the generated inventory" do config.groups = { "group1" => "machine1", "group1:children" => 'bar group3', "group2" => [iso_env.machine_names[1]], "group3" => ["unknown", "#{machine.name}"], "group4" => ["machine[1:2]", "machine[a:f]"], "group6" => [machine.name], "bar" => ["#{machine.name}", "group3"], "bar:children" => ["group1", "group2", "group3", "group5"], } expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| inventory_content = File.read(generated_inventory_file) # Accept String instead of Array for group member list expect(inventory_content).to include("[group1]\nmachine1\n\n") expect(inventory_content).to include("[group1:children]\nbar\ngroup3\n\n") # Skip "lost" machines expect(inventory_content).to include("[group2]\n\n") # Skip "unknown" machines expect(inventory_content).to include("[group3]\n#{machine.name}\n\n") # Accept Symbol datatype for group names expect(inventory_content).to include("[group6]\n#{machine.name}\n\n") # Accept host range patterns expect(inventory_content).to include("[group4]\nmachine[1:2]\nmachine[a:f]\n") # Don't mix group names and host names expect(inventory_content).to include("[bar]\n#{machine.name}\n\n") # A group of groups only includes declared groups expect(inventory_content).not_to include("group5") expect(inventory_content).to match(Regexp.quote("[bar:children]\ngroup1\ngroup2\ngroup3\n") + "$") }.and_return(default_execute_result) end it "adds group vars to the generated inventory" do config.groups = { "group1" => [machine.name], "group2" => [machine.name], "group3" => [machine.name], "group1:vars" => {"hashvar1" => "hashvalue1", "hashvar2" => "hashvalue2"}, "group2:vars" => ["arrayvar1=arrayvalue1", "arrayvar2=arrayvalue2"], "group3:vars" => "stringvar1=stringvalue1 stringvar2=stringvalue2", } expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| inventory_content = File.read(generated_inventory_file) # Hash syntax expect(inventory_content).to include("[group1:vars]\nhashvar1=hashvalue1\nhashvar2=hashvalue2\n") # Array syntax expect(inventory_content).to include("[group2:vars]\narrayvar1=arrayvalue1\narrayvar2=arrayvalue2\n") # Single string syntax expect(inventory_content).to include("[group3:vars]\nstringvar1=stringvalue1\nstringvar2=stringvalue2\n") }.and_return(default_execute_result) end it "adds 'all:vars' section to the generated inventory" do config.groups = { "all:vars" => { "var1" => "value1", "var2" => "value2" } } expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| inventory_content = File.read(generated_inventory_file) expect(inventory_content).to include("[all:vars]\nvar1=value1\nvar2=value2\n") }.and_return(default_execute_result) end end describe "with host_key_checking option enabled" do before do config.host_key_checking = true end it_should_set_arguments_and_environment_variables 5, 4, true end describe "with boolean (flag) options disabled" do before do config.become = false config.ask_become_pass = false config.ask_vault_pass = false config.become_user = 'root' end it_should_set_arguments_and_environment_variables 6 it_should_set_optional_arguments({ "become_user" => "--sudo-user=root" }) it "it does not set boolean flag when corresponding option is set to false" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| expect(args.index("--sudo")).to be_nil expect(args.index("--ask-sudo-pass")).to be_nil expect(args.index("--ask-vault-pass")).to be_nil }.and_return(default_execute_result) end end describe "with raw_arguments option" do before do config.become = false config.force_remote_user = false config.skip_tags = %w(foo bar) config.limit = "all" config.raw_arguments = ["--connection=paramiko", "--skip-tags=ignored", "--module-path=/other/modules", "--sudo", "-l localhost", "--limit=foo", "--limit=bar", "--inventory-file=/forget/it/my/friend", "--user=lion", "--new-arg=yeah"] end it_should_set_arguments_and_environment_variables 17, 4, false, "paramiko" it "sets all raw arguments" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| config.raw_arguments.each do |raw_arg| expect(args).to include(raw_arg) end }.and_return(default_execute_result) end it "sets raw arguments after arguments related to supported options" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| expect(args.index("--user=lion")).to be > args.index("--user=testuser") expect(args.index("--inventory-file=/forget/it/my/friend")).to be > args.index("--inventory-file=#{generated_inventory_dir}") expect(args.index("--limit=bar")).to be > args.index("--limit=all") expect(args.index("--skip-tags=ignored")).to be > args.index("--skip-tags=foo,bar") }.and_return(default_execute_result) end it "sets boolean flag (e.g. --sudo) defined in raw_arguments, even if corresponding option is set to false" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| expect(args).to include('--sudo') }.and_return(default_execute_result) end end describe "with limit option" do before do config.limit = %w(foo !bar) end it_should_set_arguments_and_environment_variables end context "with force_remote_user option disabled" do before do config.force_remote_user = false end it_should_create_and_use_generated_inventory false # i.e. without setting ansible_ssh_user in inventory it_should_set_arguments_and_environment_variables 6 it "uses a --user argument to set a default remote user" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| expect(args).not_to include("--extra-vars=ansible_ssh_user='#{machine.ssh_info[:username]}'") expect(args).to include("--user=#{machine.ssh_info[:username]}") }.and_return(default_execute_result) end end context "with winrm communicator" do let(:iso_winrm_env) do env = isolated_environment env.vagrantfile <<-VF Vagrant.configure("2") do |config| config.winrm.username = 'winner' config.winrm.password = 'winword' config.winrm.transport = :ssl config.vm.define :machine1 do |machine| machine.vm.box = "winbox" machine.vm.communicator = :winrm end end VF env.create_vagrant_env end let(:machine) { iso_winrm_env.machine(iso_winrm_env.machine_names[0], :dummy) } it_should_set_arguments_and_environment_variables it "generates an inventory with winrm connection settings" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| expect(config.inventory_path).to be_nil expect(File.exists?(generated_inventory_file)).to be(true) inventory_content = File.read(generated_inventory_file) expect(inventory_content).to include("machine1 ansible_connection=winrm ansible_ssh_host=127.0.0.1 ansible_ssh_port=55986 ansible_ssh_user='winner' ansible_ssh_pass='winword'\n") }.and_return(default_execute_result) end describe "with force_remote_user option disabled" do before do config.force_remote_user = false end it "doesn't set the ansible remote user in inventory and use '--user' argument with the vagrant ssh username" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| inventory_content = File.read(generated_inventory_file) expect(inventory_content).to include("machine1 ansible_connection=winrm ansible_ssh_host=127.0.0.1 ansible_ssh_port=55986 ansible_ssh_pass='winword'\n") expect(args).to include("--user=testuser") }.and_return(default_execute_result) end end end describe "with inventory_path option" do before do config.inventory_path = existing_file end it_should_set_arguments_and_environment_variables 6 it "does not generate the inventory and uses given inventory path instead" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| expect(args).to include("--inventory-file=#{existing_file}") expect(args).not_to include("--inventory-file=#{generated_inventory_file}") expect(File.exists?(generated_inventory_file)).to be(false) }.and_return(default_execute_result) end it "uses an --extra-vars argument to force ansible_ssh_user parameter" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| expect(args).not_to include("--user=#{machine.ssh_info[:username]}") expect(args).to include("--extra-vars=ansible_ssh_user='#{machine.ssh_info[:username]}'") }.and_return(default_execute_result) end describe "with force_remote_user option disabled" do before do config.force_remote_user = false end it "uses a --user argument to set a default remote user" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| expect(args).not_to include("--extra-vars=ansible_ssh_user='#{machine.ssh_info[:username]}'") expect(args).to include("--user=#{machine.ssh_info[:username]}") }.and_return(default_execute_result) end end end context "with config_file option defined" do before do config.config_file = existing_file end it "sets ANSIBLE_CONFIG environment variable" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| cmd_opts = args.last expect(cmd_opts[:env]).to include("ANSIBLE_CONFIG") expect(cmd_opts[:env]['ANSIBLE_CONFIG']).to eql(existing_file) }.and_return(default_execute_result) end end describe "with ask_vault_pass option" do before do config.ask_vault_pass = true end it_should_set_arguments_and_environment_variables 6 it "should ask the vault password" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| expect(args).to include("--ask-vault-pass") }.and_return(default_execute_result) end end describe "with vault_password_file option" do before do config.vault_password_file = existing_file end it_should_set_arguments_and_environment_variables 6 it "uses the given vault password file" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| expect(args).to include("--vault-password-file=#{existing_file}") }.and_return(default_execute_result) end end describe "with raw_ssh_args" do before do config.raw_ssh_args = ['-o ControlMaster=no', '-o ForwardAgent=no'] end it_should_set_arguments_and_environment_variables it_should_explicitly_enable_ansible_ssh_control_persist_defaults it "passes custom SSH options via ANSIBLE_SSH_ARGS with the highest priority" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| cmd_opts = args.last raw_opt_index = cmd_opts[:env]['ANSIBLE_SSH_ARGS'].index("-o ControlMaster=no") default_opt_index = cmd_opts[:env]['ANSIBLE_SSH_ARGS'].index("-o ControlMaster=auto") expect(raw_opt_index).to be < default_opt_index }.and_return(default_execute_result) end describe "and with ssh forwarding enabled" do before do ssh_info[:forward_agent] = true end it "sets '-o ForwardAgent=yes' via ANSIBLE_SSH_ARGS with higher priority than raw_ssh_args values" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| cmd_opts = args.last forwardAgentYes = cmd_opts[:env]['ANSIBLE_SSH_ARGS'].index("-o ForwardAgent=yes") forwardAgentNo = cmd_opts[:env]['ANSIBLE_SSH_ARGS'].index("-o ForwardAgent=no") expect(forwardAgentYes).to be < forwardAgentNo }.and_return(default_execute_result) end end end describe "with multiple SSH identities" do before do ssh_info[:private_key_path] = ['/path/to/my/key', '/an/other/identity', '/yet/an/other/key'] end it_should_set_arguments_and_environment_variables it_should_explicitly_enable_ansible_ssh_control_persist_defaults it "passes additional Identity Files via ANSIBLE_SSH_ARGS" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| cmd_opts = args.last expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o IdentityFile=/an/other/identity") expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o IdentityFile=/yet/an/other/key") }.and_return(default_execute_result) end end describe "with an identity file containing `%`" do before do ssh_info[:private_key_path] = ['/foo%bar/key', '/bar%%buz/key'] end it "replaces `%` with `%%`" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| cmd_opts = args.last expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o IdentityFile=/foo%%bar/key") expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o IdentityFile=/bar%%%%buz/key") }.and_return(default_execute_result) end end describe "with ssh forwarding enabled" do before do ssh_info[:forward_agent] = true end it_should_set_arguments_and_environment_variables it_should_explicitly_enable_ansible_ssh_control_persist_defaults it "enables SSH-Forwarding via ANSIBLE_SSH_ARGS" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| cmd_opts = args.last expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o ForwardAgent=yes") }.and_return(default_execute_result) end end describe "with an ssh proxy command configured" do before do ssh_info[:proxy_command] = "ssh -W %h:%p -q user@remote_libvirt_host" end it "sets '-o ProxyCommand' via ANSIBLE_SSH_ARGS" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| cmd_opts = args.last expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o ProxyCommand='ssh -W %h:%p -q user@remote_libvirt_host'") }.and_return(default_execute_result) end end context "with verbose option defined" do %w(vv vvvv).each do |verbose_option| describe "with a value of '#{verbose_option}'" do before do config.verbose = verbose_option end it_should_set_arguments_and_environment_variables 6 it_should_set_optional_arguments({ "verbose" => "-#{verbose_option}" }) it "shows the ansible-playbook command and set verbosity to '-#{verbose_option}' level" do expect(machine.env.ui).to receive(:detail) .with("PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --connection=ssh --timeout=30 --limit=\"machine1\" --inventory-file=#{generated_inventory_dir} -#{verbose_option} playbook.yml") end end describe "with a value of '-#{verbose_option}'" do before do config.verbose = "-#{verbose_option}" end it_should_set_arguments_and_environment_variables 6 it_should_set_optional_arguments({ "verbose" => "-#{verbose_option}" }) it "shows the ansible-playbook command and set verbosity to '-#{verbose_option}' level" do expect(machine.env.ui).to receive(:detail) .with("PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --connection=ssh --timeout=30 --limit=\"machine1\" --inventory-file=#{generated_inventory_dir} -#{verbose_option} playbook.yml") end end end describe "with an invalid string" do before do config.verbose = "wrong" end it_should_set_arguments_and_environment_variables 6 it_should_set_optional_arguments({ "verbose" => "-v" }) it "shows the ansible-playbook command and set verbosity to '-v' level" do expect(machine.env.ui).to receive(:detail) .with("PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --connection=ssh --timeout=30 --limit=\"machine1\" --inventory-file=#{generated_inventory_dir} -v playbook.yml") end end describe "with an empty string" do before do config.verbose = "" end it_should_set_arguments_and_environment_variables it "doesn't show the ansible-playbook command" do expect(machine.env.ui).to_not receive(:detail).with(/ansible-playbook/) end end end describe "without colorized output" do before do allow(machine.env).to receive(:ui).and_return(Vagrant::UI::Basic.new) allow(machine.env.ui).to receive(:warn).and_return("") # hide the breaking change warning end it "disables ansible-playbook colored output" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| cmd_opts = args.last expect(cmd_opts[:env]).to_not include("ANSIBLE_FORCE_COLOR") expect(cmd_opts[:env]['ANSIBLE_NOCOLOR']).to eql("true") }.and_return(default_execute_result) end end context "with version option set" do before do config.version = "2.3.4.5" end describe "and the installed ansible version is correct" do before do allow(subject).to receive(:gather_ansible_version).and_return("ansible #{config.version}\n...\n") end it "executes ansible-playbook command" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args).and_return(default_execute_result) end end describe "and there is an ansible version mismatch" do before do allow(subject).to receive(:gather_ansible_version).and_return("ansible 1.9.6\n...\n") end it "raises an error about the ansible version mismatch", skip_before: false, skip_after: true do ensure_that_config_is_valid expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsibleVersionMismatch) end end describe "and the installed ansible version cannot be detected" do before do allow(subject).to receive(:gather_ansible_version).and_return("") end it "skips the ansible version check and executes ansible-playbook command" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args).and_return(default_execute_result) end end describe "with special value: 'latest'" do before do config.version = :latest allow(subject).to receive(:gather_ansible_version).and_return("ansible 2.2.0.1\n...\n") end it "skips the ansible version check and executes ansible-playbook command" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args).and_return(default_execute_result) end end end describe "with galaxy support" do before do config.galaxy_role_file = existing_file end it "raises an error when ansible-galaxy command fails", skip_before: true, skip_after: true do config.finalize! ensure_that_config_is_valid allow(subject).to receive(:check_path) allow(Vagrant::Util::Subprocess).to receive(:execute) .and_return(Vagrant::Util::Subprocess::Result.new(1, "", "")) expect {subject.provision}.to raise_error(VagrantPlugins::Ansible::Errors::AnsibleCommandFailed) end it "execute three commands: ansible --version, ansible-galaxy, and ansible-playbook" do expect(Vagrant::Util::Subprocess).to receive(:execute) .once .with('ansible', '--version', { :notify => [:stdout, :stderr] }) .and_return(default_execute_result) expect(Vagrant::Util::Subprocess).to receive(:execute) .once .with('ansible-galaxy', any_args) .and_return(default_execute_result) expect(Vagrant::Util::Subprocess).to receive(:execute) .once .with('ansible-playbook', any_args) .and_return(default_execute_result) end it "doesn't show the ansible-galaxy command" do expect(machine.env.ui).to_not receive(:detail).with(/ansible-galaxy/) end describe "with verbose option enabled" do before do config.galaxy_roles_path = "/tmp/roles" config.verbose = true end it "shows the ansible-galaxy command in use" do expect(machine.env.ui).to receive(:detail) .with(%Q(ansible-galaxy install --role-file='#{existing_file}' --roles-path='/tmp/roles' --force)) end end end context "with galaxy_roles_path option defined" do before do config.galaxy_roles_path = "my-roles" end it "sets ANSIBLE_ROLES_PATH with corresponding absolute path" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| cmd_opts = args.last expect(cmd_opts[:env]).to include("ANSIBLE_ROLES_PATH") expect(cmd_opts[:env]['ANSIBLE_ROLES_PATH']).to eql(File.join(machine.env.root_path, "my-roles")) }.and_return(default_execute_result) end end context "with extra_vars option defined" do describe "with a hash value" do before do config.extra_vars = { var1: %Q(string with 'apo$trophe$', \\, " and =), var2: { x: 42 } } end it_should_set_optional_arguments({ "extra_vars" => "--extra-vars={\"var1\":\"string with 'apo$trophe$', \\\\, \\\" and =\",\"var2\":{\"x\":42}}" }) end describe "with a string value referring to file path (with the '@' prefix)" do before do config.extra_vars = "@#{existing_file}" end it_should_set_optional_arguments({ "extra_vars" => "--extra-vars=@#{File.expand_path(__FILE__)}" }) end end # The Vagrant Ansible provisioner does not validate the coherency of # argument combinations, and lets ansible-playbook complain. describe "with a maximum of options" do before do # vagrant general options ssh_info[:forward_agent] = true ssh_info[:private_key_path] = ['/my/key1', '/my/key2'] # command line arguments config.galaxy_roles_path = "/up/to the stars" config.extra_vars = { var1: %Q(string with 'apo$trophe$', \\, " and =), var2: { x: 42 } } config.become = true config.become_user = 'deployer' config.verbose = "vvv" config.ask_become_pass = true config.ask_vault_pass = true config.vault_password_file = existing_file config.tags = %w(db www) config.skip_tags = %w(foo bar) config.limit = 'machine*:&vagrant:!that_one' config.start_at_task = "joe's awesome task" config.raw_arguments = ["--why-not", "--su-user=foot", "--ask-su-pass", "--limit=all", "--private-key=./myself.key", "--extra-vars='{\"var3\":\"foo\"}'"] # environment variables config.config_file = existing_file config.host_key_checking = true config.raw_ssh_args = ['-o ControlMaster=no'] end it_should_set_arguments_and_environment_variables 21, 6, true it_should_explicitly_enable_ansible_ssh_control_persist_defaults it_should_set_optional_arguments({ "extra_vars" => "--extra-vars={\"var1\":\"string with 'apo$trophe$', \\\\, \\\" and =\",\"var2\":{\"x\":42}}", "become" => "--sudo", "become_user" => "--sudo-user=deployer", "verbose" => "-vvv", "ask_become_pass" => "--ask-sudo-pass", "ask_vault_pass" => "--ask-vault-pass", "vault_password_file" => "--vault-password-file=#{File.expand_path(__FILE__)}", "tags" => "--tags=db,www", "skip_tags" => "--skip-tags=foo,bar", "limit" => "--limit=machine*:&vagrant:!that_one", "start_at_task" => "--start-at-task=joe's awesome task", }) it "also includes given raw arguments" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| expect(args).to include("--why-not") expect(args).to include("--su-user=foot") expect(args).to include("--ask-su-pass") expect(args).to include("--limit=all") expect(args).to include("--private-key=./myself.key") }.and_return(default_execute_result) end it "shows the ansible-playbook command, with additional quotes when required" do expect(machine.env.ui).to receive(:detail) .with(%Q(PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true ANSIBLE_ROLES_PATH='/up/to the stars' ANSIBLE_CONFIG='#{existing_file}' ANSIBLE_HOST_KEY_CHECKING=true ANSIBLE_SSH_ARGS='-o IdentitiesOnly=yes -o IdentityFile=/my/key1 -o IdentityFile=/my/key2 -o ForwardAgent=yes -o ControlMaster=no -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --connection=ssh --timeout=30 --ask-sudo-pass --ask-vault-pass --limit="machine*:&vagrant:!that_one" --inventory-file=#{generated_inventory_dir} --extra-vars=\\{\\"var1\\":\\"string\\ with\\ \\'apo\\$trophe\\$\\',\\ \\\\\\\\,\\ \\\\\\"\\ and\\ \\=\\",\\"var2\\":\\{\\"x\\":42\\}\\} --sudo --sudo-user=deployer -vvv --vault-password-file=#{existing_file} --tags=db,www --skip-tags=foo,bar --start-at-task="joe's awesome task" --why-not --su-user=foot --ask-su-pass --limit=all --private-key=./myself.key --extra-vars='{\"var3\":\"foo\"}' playbook.yml)) end end # # Special cases related to the VM provider context # context "with Docker provider on a non-Linux host" do let(:fake_host_ssh_info) {{ private_key_path: ['/path/to/docker/host/key'], username: 'boot9docker', host: '127.0.0.1', port: 2299 }} let(:fake_host_vm) { double("host_vm").tap do |h| allow(h).to receive(:ssh_info).and_return(fake_host_ssh_info) end } before do allow(machine).to receive(:provider_name).and_return(:docker) allow(machine.provider).to receive(:host_vm?).and_return(true) allow(machine.provider).to receive(:host_vm).and_return(fake_host_vm) end it "uses an SSH ProxyCommand to reach the VM" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| cmd_opts = args.last expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to include("-o ProxyCommand='ssh boot9docker@127.0.0.1 -p 2299 -i /path/to/docker/host/key -o Compression=yes -o ConnectTimeout=5 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no exec nc %h %p 2>/dev/null'") }.and_return(default_execute_result) end end # # Special cases related to the Vagrant Host operating system in use # context "on a Windows host" do before do allow(Vagrant::Util::Platform).to receive(:windows?).and_return(true) allow(machine.ui).to receive(:warn) # Set the compatibility mode to only get the Windows warning config.compatibility_mode = VagrantPlugins::Ansible::COMPATIBILITY_MODE_V1_8 end it "warns that Windows is not officially supported for the Ansible control machine" do expect(machine.env.ui).to receive(:warn) .with(I18n.t("vagrant.provisioners.ansible.windows_not_supported_for_control_machine") + "\n") end end context "on a Solaris-like host" do before do allow(Vagrant::Util::Platform).to receive(:solaris?).and_return(true) end it "does not set IdentitiesOnly=yes in ANSIBLE_SSH_ARGS" do expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| cmd_opts = args.last expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to_not include("-o IdentitiesOnly=yes") }.and_return(default_execute_result) end describe "and with host_key_checking option enabled" do it "does not set ANSIBLE_SSH_ARGS environment variable" do config.host_key_checking = true expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| cmd_opts = args.last expect(cmd_opts[:env]).to_not include('ANSIBLE_SSH_ARGS') }.and_return(Vagrant::Util::Subprocess::Result.new(0, "", "")) end end end describe 'with config.ssh.keys_only = false' do it 'does not set IdentitiesOnly=yes in ANSIBLE_SSH_ARGS' do ssh_info[:keys_only] = false expect(Vagrant::Util::Subprocess).to receive(:execute).with('ansible-playbook', any_args) { |*args| cmd_opts = args.last expect(cmd_opts[:env]['ANSIBLE_SSH_ARGS']).to_not include("-o IdentitiesOnly=yes") }.and_return(default_execute_result) end end end end vagrant-2.0.2/test/unit/plugins/provisioners/chef/000077500000000000000000000000001323370221500222415ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/provisioners/chef/command_builder_test.rb000066400000000000000000000117601323370221500267560ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/provisioners/chef/command_builder") describe VagrantPlugins::Chef::CommandBuilder do let(:machine) { double("machine") } let(:chef_config) { double("chef_config") } before(:each) do allow(chef_config).to receive(:install).and_return(true) allow(chef_config).to receive(:version).and_return("12.0.0") allow(chef_config).to receive(:provisioning_path).and_return("/tmp/vagrant-chef-1") allow(chef_config).to receive(:arguments).and_return(nil) allow(chef_config).to receive(:binary_env).and_return(nil) allow(chef_config).to receive(:binary_path).and_return(nil) allow(chef_config).to receive(:binary_env).and_return(nil) allow(chef_config).to receive(:log_level).and_return(:info) end describe ".initialize" do it "raises an error when chef type is not client or solo" do expect { VagrantPlugins::Chef::CommandBuilder.new(:client_bad, chef_config) }. to raise_error(RuntimeError) end it "does not raise an error for :client" do expect { VagrantPlugins::Chef::CommandBuilder.new(:client, chef_config) }.to_not raise_error end it "does not raise an error for :solo" do expect { VagrantPlugins::Chef::CommandBuilder.new(:solo, chef_config) }.to_not raise_error end end describe "#command" do describe "windows" do subject do VagrantPlugins::Chef::CommandBuilder.new(:client, chef_config, windows: true) end it "executes the chef-client in PATH by default" do expect(subject.command).to match(/^chef-client/) end it "executes the chef-client using full path if binary_path is specified" do allow(chef_config).to receive(:binary_path).and_return( "c:\\opscode\\chef\\bin\\chef-client") expect(subject.command).to match(/^c:\\opscode\\chef\\bin\\chef-client\\chef-client/) end it "builds a guest friendly client.rb path" do expect(subject.command).to include( '--config c:\\tmp\\vagrant-chef-1\\client.rb') end it "builds a guest friendly solo.json path" do expect(subject.command).to include( '--json-attributes c:\\tmp\\vagrant-chef-1\\dna.json') end it "includes Chef arguments if specified" do allow(chef_config).to receive(:arguments).and_return("bacon pants") expect(subject.command).to include( " bacon pants") end it "includes --no-color if UI is not colored" do expect(subject.command).to include( " --no-color") end it "includes --force-formatter if Chef > 10" do expect(subject.command).to include( " --force-formatter") end it "does not include --force-formatter if Chef < 11" do allow(chef_config).to receive(:version).and_return("10.0") expect(subject.command).to_not include( " --force-formatter") end it "does not include --force-formatter if we did not install Chef" do allow(chef_config).to receive(:install).and_return(false) expect(subject.command).to_not include( " --force-formatter") end end describe "linux" do subject do VagrantPlugins::Chef::CommandBuilder.new(:client, chef_config, windows: false) end it "executes the chef-client in PATH by default" do expect(subject.command).to match(/^chef-client/) end it "executes the chef-client using full path if binary_path is specified" do allow(chef_config).to receive(:binary_path).and_return( "/opt/chef/chef-client") expect(subject.command).to match(/^\/opt\/chef\/chef-client/) end it "builds a guest friendly client.rb path" do expect(subject.command).to include( "--config /tmp/vagrant-chef-1/client.rb") end it "builds a guest friendly solo.json path" do expect(subject.command).to include( "--json-attributes /tmp/vagrant-chef-1/dna.json") end it "includes Chef arguments if specified" do allow(chef_config).to receive(:arguments).and_return("bacon") expect(subject.command).to include( " bacon") end it "includes --no-color if UI is not colored" do expect(subject.command).to include( " --no-color") end it "includes environment variables if specified" do allow(chef_config).to receive(:binary_env).and_return("ENVVAR=VAL") expect(subject.command).to match(/^ENVVAR=VAL /) end it "does not include --force-formatter if Chef < 11" do allow(chef_config).to receive(:version).and_return("10.0") expect(subject.command).to_not include( " --force-formatter") end it "does not include --force-formatter if we did not install Chef" do allow(chef_config).to receive(:install).and_return(false) expect(subject.command).to_not include( " --force-formatter") end end end end vagrant-2.0.2/test/unit/plugins/provisioners/chef/config/000077500000000000000000000000001323370221500235065ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/provisioners/chef/config/base_runner_test.rb000066400000000000000000000136361323370221500274060ustar00rootroot00000000000000require_relative "../../../../base" require Vagrant.source_root.join("plugins/provisioners/chef/config/base_runner") describe VagrantPlugins::Chef::Config::BaseRunner do include_context "unit" subject { described_class.new } let(:machine) { double("machine") } describe "#arguments" do it "defaults to nil" do subject.finalize! expect(subject.arguments).to be(nil) end end describe "#attempts" do it "defaults to 1" do subject.finalize! expect(subject.attempts).to eq(1) end end describe "#custom_config_path" do it "defaults to nil" do subject.finalize! expect(subject.custom_config_path).to be(nil) end end describe "#environment" do it "defaults to nil" do subject.finalize! expect(subject.environment).to be(nil) end end describe "#encrypted_data_bag_secret_key_path" do it "defaults to nil" do subject.finalize! expect(subject.encrypted_data_bag_secret_key_path).to be(nil) end end describe "#formatter" do it "defaults to nil" do subject.finalize! expect(subject.formatter).to be(nil) end end describe "#http_proxy" do it "defaults to nil" do subject.finalize! expect(subject.http_proxy).to be(nil) end end describe "#http_proxy_user" do it "defaults to nil" do subject.finalize! expect(subject.http_proxy_user).to be(nil) end end describe "#http_proxy_pass" do it "defaults to nil" do subject.finalize! expect(subject.http_proxy_pass).to be(nil) end end describe "#https_proxy" do it "defaults to nil" do subject.finalize! expect(subject.https_proxy).to be(nil) end end describe "#https_proxy_user" do it "defaults to nil" do subject.finalize! expect(subject.https_proxy_user).to be(nil) end end describe "#https_proxy_pass" do it "defaults to nil" do subject.finalize! expect(subject.https_proxy_pass).to be(nil) end end describe "#log_level" do it "defaults to :info" do subject.finalize! expect(subject.log_level).to be(:info) end it "is converted to a symbol" do subject.log_level = "foo" subject.finalize! expect(subject.log_level).to eq(:foo) end end describe "#no_proxy" do it "defaults to nil" do subject.finalize! expect(subject.no_proxy).to be(nil) end end describe "#node_name" do it "defaults to nil" do subject.finalize! expect(subject.node_name).to be(nil) end end describe "#provisioning_path" do it "defaults to nil" do subject.finalize! expect(subject.provisioning_path).to be(nil) end end describe "#file_backup_path" do it "defaults to nil" do subject.finalize! expect(subject.file_backup_path).to be(nil) end end describe "#file_cache_path" do it "defaults to nil" do subject.finalize! expect(subject.file_cache_path).to be(nil) end end describe "#verbose_logging" do it "defaults to false" do subject.finalize! expect(subject.verbose_logging).to be(false) end end describe "#enable_reporting" do it "defaults to true" do subject.finalize! expect(subject.enable_reporting).to be(true) end end describe "#run_list" do it "defaults to an empty array" do subject.finalize! expect(subject.run_list).to be_a(Array) expect(subject.run_list).to be_empty end end describe "#json" do it "defaults to an empty hash" do subject.finalize! expect(subject.json).to be_a(Hash) expect(subject.json).to be_empty end end describe "#add_recipe" do context "when the prefix is given" do it "adds the value to the run_list" do subject.add_recipe("recipe[foo::bar]") expect(subject.run_list).to eq %w(recipe[foo::bar]) end end context "when the prefix is not given" do it "adds the prefixed value to the run_list" do subject.add_recipe("foo::bar") expect(subject.run_list).to eq %w(recipe[foo::bar]) end end end describe "#add_role" do context "when the prefix is given" do it "adds the value to the run_list" do subject.add_role("role[foo]") expect(subject.run_list).to eq %w(role[foo]) end end context "when the prefix is not given" do it "adds the prefixed value to the run_list" do subject.add_role("foo") expect(subject.run_list).to eq %w(role[foo]) end end end describe "#validate_base" do context "when #custom_config_path does not exist" do let(:path) do next "/path/to/file" if !Vagrant::Util::Platform.windows? "C:/path/to/file" end before do allow(File).to receive(:file?) .with(path) .and_return(false) allow(machine).to receive(:env) .and_return(double("env", root_path: "", )) end it "returns an error" do subject.custom_config_path = path subject.finalize! expect(subject.validate_base(machine)) .to eq ['Path specified for "custom_config_path" does not exist.'] end end end describe "#merge" do it "merges the json hash" do a = described_class.new.tap do |i| i.json = { "foo" => "bar" } end b = described_class.new.tap do |i| i.json = { "zip" => "zap" } end result = a.merge(b) expect(result.json).to eq( "foo" => "bar", "zip" => "zap", ) end it "appends the run_list array" do a = described_class.new.tap do |i| i.run_list = ["recipe[foo::bar]"] end b = described_class.new.tap do |i| i.run_list = ["recipe[zip::zap]"] end result = a.merge(b) expect(result.run_list).to eq %w( recipe[foo::bar] recipe[zip::zap] ) end end end vagrant-2.0.2/test/unit/plugins/provisioners/chef/config/base_test.rb000066400000000000000000000043271323370221500260120ustar00rootroot00000000000000require_relative "../../../../base" require Vagrant.source_root.join("plugins/provisioners/chef/config/base") describe VagrantPlugins::Chef::Config::Base do include_context "unit" subject { described_class.new } let(:machine) { double("machine") } describe "#binary_path" do it "defaults to nil" do subject.finalize! expect(subject.binary_path).to be(nil) end end describe "#binary_env" do it "defaults to nil" do subject.finalize! expect(subject.binary_env).to be(nil) end end describe "#product" do it "defaults to \"chef\"" do subject.finalize! expect(subject.product).to eq("chef") end end describe "#install" do it "defaults to true" do subject.finalize! expect(subject.install).to be(true) end it "is converted to a symbol" do subject.install = "force" subject.finalize! expect(subject.install).to eq(:force) end end describe "#log_level" do it "defaults to :info" do subject.finalize! expect(subject.log_level).to be(:info) end it "is converted to a symbol" do subject.log_level = "foo" subject.finalize! expect(subject.log_level).to eq(:foo) end end describe "#channel" do it "defaults to \"stable\"" do subject.finalize! expect(subject.channel).to eq("stable") end end describe "#version" do it "defaults to :latest" do subject.finalize! expect(subject.version).to eq(:latest) end it "converts the string 'latest' to a symbol" do subject.version = "latest" subject.finalize! expect(subject.version).to eq(:latest) end end describe "#installer_download_path" do it "defaults to nil" do subject.finalize! expect(subject.installer_download_path).to be(nil) end end describe "#omnibus_url" do it "defaults to https://omnitruck.chef.io" do subject.finalize! expect(subject.omnibus_url).to eq("https://omnitruck.chef.io") end it "makes use of the configured url" do subject.omnibus_url = "https://omnitruck.example.com" subject.finalize! expect(subject.omnibus_url).to eq("https://omnitruck.example.com") end end end vagrant-2.0.2/test/unit/plugins/provisioners/chef/config/chef_apply_test.rb000066400000000000000000000041011323370221500272000ustar00rootroot00000000000000require_relative "../../../../base" require Vagrant.source_root.join("plugins/provisioners/chef/config/chef_apply") describe VagrantPlugins::Chef::Config::ChefApply do include_context "unit" subject { described_class.new } let(:machine) { double("machine") } def chef_error(key, options = {}) I18n.t("vagrant.provisioners.chef.#{key}", options) end describe "#recipe" do it "defaults to nil" do subject.finalize! expect(subject.recipe).to be(nil) end end describe "#upload_path" do it "defaults to /tmp/vagrant-chef-apply.rb" do subject.finalize! expect(subject.upload_path).to eq("/tmp/vagrant-chef-apply") end end describe "#validate" do before do allow(machine).to receive(:env) .and_return(double("env", root_path: "", )) subject.recipe = <<-EOH package "foo" EOH end let(:result) { subject.validate(machine) } let(:errors) { result["chef apply provisioner"] } context "when the recipe is nil" do it "returns an error" do subject.recipe = nil subject.finalize! expect(errors).to include chef_error("recipe_empty") end end context "when the recipe is empty" do it "returns an error" do subject.recipe = " " subject.finalize! expect(errors).to include chef_error("recipe_empty") end end context "when the log_level is an empty array" do it "returns an error" do subject.log_level = " " subject.finalize! expect(errors).to include chef_error("log_level_empty") end end context "when the upload_path is nil" do it "returns an error" do subject.upload_path = nil subject.finalize! expect(errors).to include chef_error("upload_path_empty") end end context "when the upload_path is an empty array" do it "returns an error" do subject.upload_path = " " subject.finalize! expect(errors).to include chef_error("upload_path_empty") end end end end vagrant-2.0.2/test/unit/plugins/provisioners/chef/config/chef_client_test.rb000066400000000000000000000050271323370221500273410ustar00rootroot00000000000000require_relative "../../../../base" require Vagrant.source_root.join("plugins/provisioners/chef/config/chef_client") describe VagrantPlugins::Chef::Config::ChefClient do include_context "unit" subject { described_class.new } let(:machine) { double("machine") } describe "#chef_server_url" do it "defaults to nil" do subject.finalize! expect(subject.chef_server_url).to be(nil) end end describe "#client_key_path" do it "defaults to nil" do subject.finalize! expect(subject.client_key_path).to be(nil) end end describe "#delete_client" do it "defaults to false" do subject.finalize! expect(subject.delete_client).to be(false) end end describe "#delete_node" do it "defaults to false" do subject.finalize! expect(subject.delete_node).to be(false) end end describe "#validation_key_path" do it "defaults to nil" do subject.finalize! expect(subject.validation_key_path).to be(nil) end end describe "#validation_client_name" do it "defaults to chef-validator" do subject.finalize! expect(subject.validation_client_name).to eq("chef-validator") end end describe "#validate" do before do allow(machine).to receive(:env) .and_return(double("env", root_path: "", )) subject.chef_server_url = "https://example.com" subject.validation_key_path = "/path/to/key.pem" end let(:result) { subject.validate(machine) } let(:errors) { result["chef client provisioner"] } context "when the chef_server_url is nil" do it "returns an error" do subject.chef_server_url = nil subject.finalize! expect(errors).to eq([I18n.t("vagrant.config.chef.server_url_empty")]) end end context "when the chef_server_url is blank" do it "returns an error" do subject.chef_server_url = " " subject.finalize! expect(errors).to eq([I18n.t("vagrant.config.chef.server_url_empty")]) end end context "when the validation_key_path is nil" do it "returns an error" do subject.validation_key_path = nil subject.finalize! expect(errors).to eq([I18n.t("vagrant.config.chef.validation_key_path")]) end end context "when the validation_key_path is blank" do it "returns an error" do subject.validation_key_path = " " subject.finalize! expect(errors).to eq([I18n.t("vagrant.config.chef.validation_key_path")]) end end end end vagrant-2.0.2/test/unit/plugins/provisioners/chef/config/chef_solo_test.rb000066400000000000000000000103221323370221500270310ustar00rootroot00000000000000require_relative "../../../../base" require Vagrant.source_root.join("plugins/provisioners/chef/config/chef_solo") describe VagrantPlugins::Chef::Config::ChefSolo do include_context "unit" subject { described_class.new } let(:machine) { double("machine") } describe "#cookbooks_path" do it "defaults to something" do subject.finalize! expect(subject.cookbooks_path).to eq([ [:host, "cookbooks"], [:vm, "cookbooks"], ]) end end describe "#data_bags_path" do it "defaults to an empty array" do subject.finalize! expect(subject.data_bags_path).to be_a(Array) expect(subject.data_bags_path).to be_empty end end describe "#environments_path" do it "defaults to an empty array" do subject.finalize! expect(subject.environments_path).to be_a(Array) expect(subject.environments_path).to be_empty end it "merges deeply nested paths" do subject.environments_path = ["/foo", "/bar", ["/zip"]] subject.finalize! expect(subject.environments_path) .to eq([:host, :host, :host].zip %w(/foo /bar /zip)) end end describe "#recipe_url" do it "defaults to nil" do subject.finalize! expect(subject.recipe_url).to be(nil) end end describe "#roles_path" do it "defaults to an empty array" do subject.finalize! expect(subject.roles_path).to be_a(Array) expect(subject.roles_path).to be_empty end end describe "#nodes_path" do it "defaults to an empty array" do subject.finalize! expect(subject.nodes_path).to be_a(Array) expect(subject.nodes_path).to be_empty end end describe "#synced_folder_type" do it "defaults to nil" do subject.finalize! expect(subject.synced_folder_type).to be(nil) end end describe "#validate" do before do allow(machine).to receive(:env) .and_return(double("env", root_path: "", )) subject.cookbooks_path = ["/cookbooks", "/more/cookbooks"] end let(:result) { subject.validate(machine) } let(:errors) { result["chef solo provisioner"] } context "when the cookbooks_path is nil" do it "returns an error" do subject.cookbooks_path = nil subject.finalize! expect(errors).to eq [I18n.t("vagrant.config.chef.cookbooks_path_empty")] end end context "when the cookbooks_path is an empty array" do it "returns an error" do subject.cookbooks_path = [] subject.finalize! expect(errors).to eq [I18n.t("vagrant.config.chef.cookbooks_path_empty")] end end context "when the cookbooks_path is an array with nil" do it "returns an error" do subject.cookbooks_path = [nil, nil] subject.finalize! expect(errors).to eq [I18n.t("vagrant.config.chef.cookbooks_path_empty")] end end context "when environments is given" do before do subject.environment = "production" end context "when the environments_path is nil" do it "returns an error" do subject.environments_path = nil subject.finalize! expect(errors).to eq [I18n.t("vagrant.config.chef.environment_path_required")] end end context "when the environments_path is an empty array" do it "returns an error" do subject.environments_path = [] subject.finalize! expect(errors).to eq [I18n.t("vagrant.config.chef.environment_path_required")] end end context "when the environments_path is an array with nil" do it "returns an error" do subject.environments_path = [nil, nil] subject.finalize! expect(errors).to eq [I18n.t("vagrant.config.chef.environment_path_required")] end end context "when the environments_path does not exist" do it "returns an error" do env_path = "/path/to/environments/that/will/never/exist" subject.environments_path = env_path subject.finalize! expect(errors).to eq [ I18n.t("vagrant.config.chef.environment_path_missing", path: env_path ) ] end end end end end vagrant-2.0.2/test/unit/plugins/provisioners/chef/config/chef_zero_test.rb000066400000000000000000000122311323370221500270350ustar00rootroot00000000000000require_relative "../../../../base" require Vagrant.source_root.join("plugins/provisioners/chef/config/chef_zero") describe VagrantPlugins::Chef::Config::ChefZero do include_context "unit" subject { described_class.new } let(:machine) { double("machine") } describe "#cookbooks_path" do it "defaults to something" do subject.finalize! expect(subject.cookbooks_path).to eq([ [:host, "cookbooks"], [:vm, "cookbooks"], ]) end end describe "#data_bags_path" do it "defaults to an empty array" do subject.finalize! expect(subject.data_bags_path).to be_a(Array) expect(subject.data_bags_path).to be_empty end end describe "#environments_path" do it "defaults to an empty array" do subject.finalize! expect(subject.environments_path).to be_a(Array) expect(subject.environments_path).to be_empty end it "merges deeply nested paths" do subject.environments_path = ["/foo", "/bar", ["/zip"]] subject.finalize! expect(subject.environments_path) .to eq([:host, :host, :host].zip %w(/foo /bar /zip)) end end describe "#roles_path" do it "defaults to an empty array" do subject.finalize! expect(subject.roles_path).to be_a(Array) expect(subject.roles_path).to be_empty end end describe "#nodes_path" do it "defaults to an empty array" do subject.finalize! expect(subject.nodes_path).to be_a(Array) expect(subject.nodes_path).to be_empty end end describe "#synced_folder_type" do it "defaults to nil" do subject.finalize! expect(subject.synced_folder_type).to be(nil) end end describe "#validate" do before do allow(machine).to receive(:env) .and_return(double("env", root_path: "", )) subject.cookbooks_path = ["/cookbooks", "/more/cookbooks"] end let(:result) { subject.validate(machine) } let(:errors) { result["chef zero provisioner"] } context "when the cookbooks_path is nil" do it "returns an error" do subject.cookbooks_path = nil subject.finalize! expect(errors).to include(I18n.t("vagrant.config.chef.cookbooks_path_empty")) end end context "when the cookbooks_path is an empty array" do it "returns an error" do subject.cookbooks_path = [] subject.finalize! expect(errors).to include(I18n.t("vagrant.config.chef.cookbooks_path_empty")) end end context "when the cookbooks_path is an array with nil" do it "returns an error" do subject.cookbooks_path = [nil, nil] subject.finalize! expect(errors).to include(I18n.t("vagrant.config.chef.cookbooks_path_empty")) end end context "when the nodes_path is nil" do it "returns an error" do subject.nodes_path = nil subject.finalize! expect(errors).to include(I18n.t("vagrant.config.chef.nodes_path_empty")) end end context "when an element of nodes_path does not exist on disk" do it "returns an error" do nodes_path = ["/path/to/nodes/that/will/never/exist"] subject.nodes_path = nodes_path subject.finalize! expect(errors).to include(I18n.t("vagrant.config.chef.nodes_path_missing", path: nodes_path )) end end context "when the nodes_path is an empty array" do it "returns an error" do subject.nodes_path = [] subject.finalize! expect(errors).to include(I18n.t("vagrant.config.chef.nodes_path_empty")) end end context "when the nodes_path is an array with nil" do it "returns an error" do subject.nodes_path = [nil, nil] subject.finalize! expect(errors).to include(I18n.t("vagrant.config.chef.nodes_path_empty")) end end context "when environments is given" do before do subject.environment = "production" end context "when the environments_path is nil" do it "returns an error" do subject.environments_path = nil subject.finalize! expect(errors).to include(I18n.t("vagrant.config.chef.environment_path_required")) end end context "when the environments_path is an empty array" do it "returns an error" do subject.environments_path = [] subject.finalize! expect(errors).to include(I18n.t("vagrant.config.chef.environment_path_required")) end end context "when the environments_path is an array with nil" do it "returns an error" do subject.environments_path = [nil, nil] subject.finalize! expect(errors).to include(I18n.t("vagrant.config.chef.environment_path_required")) end end context "when the environments_path does not exist" do it "returns an error" do env_path = "/path/to/environments/that/will/never/exist" subject.environments_path = env_path subject.finalize! expect(errors).to include(I18n.t("vagrant.config.chef.environment_path_missing", path: env_path, )) end end end end end vagrant-2.0.2/test/unit/plugins/provisioners/chef/omnibus_test.rb000066400000000000000000000040211323370221500252760ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/provisioners/chef/omnibus") describe VagrantPlugins::Chef::Omnibus do describe "#sh_command" do it "includes the project name" do command = described_class.sh_command("chef", nil, "stable", "https://omnitruck.chef.io") expect(command).to include %|-P "chef"| end it "includes the channel" do command = described_class.sh_command("chef", nil, "stable", "https://omnitruck.chef.io") expect(command).to include %|-c "stable"| end it "includes the version" do command = described_class.sh_command("chef", "1.2.3", "stable", "https://omnitruck.chef.io") expect(command).to include %|-v "1.2.3"| end it "includes the Omnibus installation URL" do command = described_class.sh_command("chef", "1.2.3", "stable", "https://omnitruck.chef.io") expect(command).to include %|https://omnitruck.chef.io/install.sh| end it "includes the download path" do command = described_class.sh_command("chef", "1.2.3", "stable", "https://omnitruck.chef.io", download_path: "/some/path", ) expect(command).to include %|-d "/some/path"| end end describe "#ps_command" do it "includes the project name" do command = described_class.ps_command("chef", nil, "stable", "https://omnitruck.chef.io") expect(command).to include %|-project 'chef'| end it "includes the channel" do command = described_class.ps_command("chef", nil, "stable", "https://omnitruck.chef.io") expect(command).to include %|-channel 'stable'| end it "includes the version" do command = described_class.ps_command("chef", "1.2.3", "stable", "https://omnitruck.chef.io") expect(command).to include %|-version '1.2.3'| end it "includes the Omnibus installation URL" do command = described_class.ps_command("chef", "1.2.3", "stable", "https://omnitruck.chef.io") expect(command).to include %|https://omnitruck.chef.io/install.ps1| end end end vagrant-2.0.2/test/unit/plugins/provisioners/chef/provisioner/000077500000000000000000000000001323370221500246205ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/provisioners/chef/provisioner/base_test.rb000066400000000000000000000057111323370221500271220ustar00rootroot00000000000000require_relative "../../../../base" require Vagrant.source_root.join("plugins/provisioners/chef/provisioner/base") describe VagrantPlugins::Chef::Provisioner::Base 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(:config) { double("config") } subject { described_class.new(machine, config) } before do allow(config).to receive(:node_name) allow(config).to receive(:node_name=) end describe "#node_name" do let(:env) { double("env") } let(:root_path) { "/my/root" } before do allow(machine).to receive(:env).and_return(env) allow(env).to receive(:root_path).and_return(root_path) end it "defaults to node_name if given" do config = OpenStruct.new(node_name: "name") instance = described_class.new(machine, config) expect(instance.config.node_name).to eq("name") end it "defaults to hostname if given" do machine.config.vm.hostname = "by.hostname" instance = described_class.new(machine, OpenStruct.new) expect(instance.config.node_name).to eq("by.hostname") end it "generates a random name if no hostname or node_name is given" do config = OpenStruct.new(node_name: nil) machine.config.vm.hostname = nil instance = described_class.new(machine, OpenStruct.new) expect(instance.config.node_name).to match(/vagrant\-.+/) end end describe "#encrypted_data_bag_secret_key_path" do let(:env) { double("env") } let(:root_path) { "/my/root" } before do allow(machine).to receive(:env).and_return(env) allow(env).to receive(:root_path).and_return(root_path) end it "returns absolute path as is" do expect(config).to receive(:encrypted_data_bag_secret_key_path). and_return("/foo/bar") expect(subject.encrypted_data_bag_secret_key_path).to eq "/foo/bar" end it "returns relative path joined to root_path" do expect(config).to receive(:encrypted_data_bag_secret_key_path). and_return("secret") expect(subject.encrypted_data_bag_secret_key_path).to eq "/my/root/secret" end end describe "#guest_encrypted_data_bag_secret_key_path" do it "returns nil if host path is not configured" do allow(config).to receive(:encrypted_data_bag_secret_key_path).and_return(nil) allow(config).to receive(:provisioning_path).and_return("/tmp/foo") expect(subject.guest_encrypted_data_bag_secret_key_path).to be_nil end it "returns path under config.provisioning_path" do allow(config).to receive(:encrypted_data_bag_secret_key_path).and_return("secret") allow(config).to receive(:provisioning_path).and_return("/tmp/foo") expect(File.dirname(subject.guest_encrypted_data_bag_secret_key_path)). to eq "/tmp/foo" end end end vagrant-2.0.2/test/unit/plugins/provisioners/docker/000077500000000000000000000000001323370221500226035ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/provisioners/docker/config_test.rb000066400000000000000000000104411323370221500254340ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/provisioners/docker/config") require Vagrant.source_root.join("plugins/provisioners/docker/provisioner") require Vagrant.source_root.join("plugins/kernel_v2/config/vm") describe VagrantPlugins::DockerProvisioner::Config do subject { described_class.new } describe "#build_image" do it "stores them" do subject.build_image("foo") subject.build_image("bar", foo: :bar) subject.finalize! expect(subject.build_images.length).to eql(2) expect(subject.build_images[0]).to eql(["foo", {}]) expect(subject.build_images[1]).to eql(["bar", { foo: :bar }]) end end describe "#images" do it "stores them in a set" do subject.images = ["1", "1", "2"] subject.finalize! expect(subject.images.to_a.sort).to eql(["1", "2"]) end it "overrides previously set images" do subject.images = ["3"] subject.images = ["1", "1", "2"] subject.finalize! expect(subject.images.to_a.sort).to eql(["1", "2"]) end end describe "#merge" do it "has all images to pull" do subject.pull_images("1") other = described_class.new other.pull_images("2", "3") result = subject.merge(other) expect(result.images.to_a.sort).to eq( ["1", "2", "3"]) end it "has all the containers to run" do subject.run("foo", image: "bar", daemonize: false) subject.run("bar") other = described_class.new other.run("foo", image: "foo") result = subject.merge(other) result.finalize! cs = result.containers expect(cs.length).to eq(2) expect(cs["foo"]).to eq({ auto_assign_name: true, image: "foo", daemonize: false, restart: "always", }) expect(cs["bar"]).to eq({ auto_assign_name: true, image: "bar", daemonize: true, restart: "always", }) end it "has all the containers to build" do subject.build_image("foo") other = described_class.new other.build_image("bar") result = subject.merge(other) result.finalize! images = result.build_images expect(images.length).to eq(2) expect(images[0]).to eq(["foo", {}]) expect(images[1]).to eq(["bar", {}]) end end describe "#pull_images" do it "adds images to the list of images to build" do subject.pull_images("1") subject.pull_images("2", "3") subject.finalize! expect(subject.images.to_a.sort).to eql(["1", "2", "3"]) end end describe "#run" do it "runs the given image" do subject.run("foo") subject.finalize! expect(subject.containers).to eql({ "foo" => { auto_assign_name: true, daemonize: true, image: "foo", restart: "always", } }) end it "can not auto assign name" do subject.run("foo", auto_assign_name: false) subject.finalize! expect(subject.containers).to eql({ "foo" => { auto_assign_name: false, daemonize: true, image: "foo", restart: "always", } }) end it "can not daemonize" do subject.run("foo", daemonize: false) subject.finalize! expect(subject.containers).to eql({ "foo" => { auto_assign_name: true, daemonize: false, image: "foo", restart: "always", } }) end end describe "#post_install_provision" do it "raises an error if 'docker' provisioner was provided" do expect {subject.post_install_provision("myprov", :type=>"docker", :inline=>"echo 'hello'")} .to raise_error(VagrantPlugins::DockerProvisioner::DockerError) end it "setups a basic provisioner" do prov = double() mock_provisioner = "mock" mock_provisioners = [mock_provisioner] allow(VagrantPlugins::Kernel_V2::VMConfig).to receive(:new). and_return(prov) allow(prov).to receive(:provision).and_return(mock_provisioners) allow(prov).to receive(:provisioners).and_return(mock_provisioners) subject.post_install_provision("myprov", :inline=>"echo 'hello'") expect(subject.post_install_provisioner).to eq(mock_provisioner) end end end vagrant-2.0.2/test/unit/plugins/provisioners/docker/provisioner_test.rb000066400000000000000000000056301323370221500265520ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/provisioners/docker/provisioner") describe VagrantPlugins::DockerProvisioner::Provisioner do include_context "unit" subject { described_class.new(machine, config, installer, client) } 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(:config) { double("config") } let(:communicator) { double("comm") } let(:guest) { double("guest") } let(:client) { double("client") } let(:installer) { double("installer") } let(:hook) { double("hook") } before do allow(machine).to receive(:communicate).and_return(communicator) allow(machine).to receive(:guest).and_return(guest) allow(communicator).to receive(:execute).and_return(true) allow(communicator).to receive(:upload).and_return(true) allow(guest).to receive(:capability?).and_return(false) allow(guest).to receive(:capability).and_return(false) allow(client).to receive(:start_service).and_return(true) allow(client).to receive(:daemon_running?).and_return(true) allow(config).to receive(:images).and_return(Set.new) allow(config).to receive(:build_images).and_return(Set.new) allow(config).to receive(:containers).and_return(Hash.new) end describe "#provision" do let(:provisioner) do prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("spec-test", :shell) prov.config = {} prov end it "invokes a post_install_provisioner if defined and docker is installed" do allow(installer).to receive(:ensure_installed).and_return(true) allow(config).to receive(:post_install_provisioner).and_return(provisioner) allow(machine).to receive(:env).and_return(iso_env) allow(machine.env).to receive(:hook).and_return(true) expect(machine.env).to receive(:hook).with(:run_provisioner, anything) subject.provision() end it "does not invoke post_install_provisioner if not defined" do allow(installer).to receive(:ensure_installed).and_return(true) allow(config).to receive(:post_install_provisioner).and_return(nil) allow(machine).to receive(:env).and_return(iso_env) allow(machine.env).to receive(:hook).and_return(true) expect(machine.env).not_to receive(:hook).with(:run_provisioner, anything) subject.provision() end it "raises an error if docker daemon isn't running" do allow(installer).to receive(:ensure_installed).and_return(false) allow(client).to receive(:start_service).and_return(false) allow(client).to receive(:daemon_running?).and_return(false) expect { subject.provision() }. to raise_error(VagrantPlugins::DockerProvisioner::DockerError) end end end vagrant-2.0.2/test/unit/plugins/provisioners/file/000077500000000000000000000000001323370221500222535ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/provisioners/file/config_test.rb000066400000000000000000000041771323370221500251150ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/provisioners/file/config") describe VagrantPlugins::FileUpload::Config do include_context "unit" subject { described_class.new } let(:env) do iso_env = isolated_environment iso_env.vagrantfile("") iso_env.create_vagrant_env end let(:machine) { double("machine", env: env) } describe "#validate" do it "returns an error if destination is not specified" do existing_file = File.expand_path(__FILE__) subject.source = existing_file subject.finalize! result = subject.validate(machine) expect(result["File provisioner"]).to eql([ I18n.t("vagrant.provisioners.file.no_dest_file") ]) end it "returns an error if source is not specified" do subject.destination = "/tmp/foo" subject.finalize! result = subject.validate(machine) expect(result["File provisioner"]).to eql([ I18n.t("vagrant.provisioners.file.no_source_file") ]) end it "returns an error if source file does not exist" do non_existing_file = "/this/does/not/exist" subject.source = non_existing_file subject.destination = "/tmp/foo" subject.finalize! result = subject.validate(machine) expect(result["File provisioner"]).to eql([ I18n.t("vagrant.provisioners.file.path_invalid", path: File.expand_path(non_existing_file)) ]) end it "passes with absolute source path" do existing_absolute_path = File.expand_path(__FILE__) subject.source = existing_absolute_path subject.destination = "/tmp/foo" subject.finalize! result = subject.validate(machine) expect(result["File provisioner"]).to eql([]) end it "passes with relative source path" do path = env.root_path.join("foo") path.open("w+") { |f| f.write("hello") } existing_relative_path = "foo" subject.source = existing_relative_path subject.destination = "/tmp/foo" subject.finalize! result = subject.validate(machine) expect(result["File provisioner"]).to eql([]) end end end vagrant-2.0.2/test/unit/plugins/provisioners/file/provisioner_test.rb000066400000000000000000000105011323370221500262130ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/provisioners/file/provisioner") describe VagrantPlugins::FileUpload::Provisioner do include_context "unit" subject { described_class.new(machine, config) } 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(:config) { double("config") } let(:communicator) { double("comm") } let(:guest) { double("guest") } before do allow(machine).to receive(:communicate).and_return(communicator) allow(machine).to receive(:guest).and_return(guest) allow(communicator).to receive(:execute).and_return(true) allow(communicator).to receive(:upload).and_return(true) allow(guest).to receive(:capability?).and_return(false) end describe "#provision" do it "creates the destination directory" do 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 it "creates the destination directory with a space" do 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 it "creates the destination directory above file" do 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 it "uploads the file" do allow(config).to receive(:source).and_return("/source") allow(config).to receive(:destination).and_return("/foo/bar") expect(communicator).to receive(:upload).with("/source", "/foo/bar") subject.provision end it "expands the source file path" do allow(config).to receive(:source).and_return("source") allow(config).to receive(:destination).and_return("/foo/bar") expect(communicator).to receive(:upload).with( File.expand_path("source"), "/foo/bar") subject.provision end it "expands the destination file path if capable" do allow(config).to receive(:source).and_return("/source") allow(config).to receive(:destination).and_return("$HOME/foo") expect(guest).to receive(:capability?). with(:shell_expand_guest_path).and_return(true) expect(guest).to receive(:capability). with(:shell_expand_guest_path, "$HOME/foo").and_return("/home/foo") expect(communicator).to receive(:upload).with("/source", "/home/foo") subject.provision end it "appends a '/.' if the destination doesnt end with a file separator" do allow(config).to receive(:source).and_return("/source") allow(config).to receive(:destination).and_return("/foo/bar") allow(File).to receive(:directory?).with("/source").and_return(true) 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") expect(communicator).to receive(:upload).with("/source/.", "/foo/bar") 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") 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") expect(communicator).to receive(:upload) .with(files, "/foo/bar") subject.provision end end end vagrant-2.0.2/test/unit/plugins/provisioners/puppet/000077500000000000000000000000001323370221500226515ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/provisioners/puppet/provisioner/000077500000000000000000000000001323370221500252305ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/provisioners/puppet/provisioner/puppet_test.rb000066400000000000000000000034751323370221500301420ustar00rootroot00000000000000require File.expand_path("../../../../../base", __FILE__) require Vagrant.source_root.join("plugins/provisioners/puppet/provisioner/puppet") describe VagrantPlugins::Puppet::Provisioner::Puppet 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(:config) { double("config") } let(:facts) { [] } let(:communicator) { double("comm") } let(:guest) { double("guest") } let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } let(:module_paths) { ["etc/puppet/modules"] } # make this something real subject { described_class.new(machine, config) } before do allow(machine).to receive(:communicate).and_return(comm) end describe "#run_puppet_apply" do let(:options) { double("options") } let(:binary_path) { "/opt/puppetlabs/bin" } let(:manifest_file) { "default.pp" } it "runs puppet on a manifest" do allow(config).to receive(:options).and_return(options) allow(config).to receive(:environment_path).and_return(false) allow(config).to receive(:facter).and_return(facts) allow(config).to receive(:binary_path).and_return(binary_path) allow(config).to receive(:environment_variables).and_return(nil) allow(config).to receive(:working_directory).and_return(false) allow(config).to receive(:manifest_file).and_return(manifest_file) allow_message_expectations_on_nil allow(@module_paths).to receive(:map) { module_paths } allow(@module_paths).to receive(:empty?).and_return(true) expect(machine).to receive(:communicate).and_return(comm) subject.run_puppet_apply() end end end vagrant-2.0.2/test/unit/plugins/provisioners/salt/000077500000000000000000000000001323370221500222775ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/provisioners/salt/config_test.rb000066400000000000000000000103021323370221500251240ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/provisioners/salt/config") describe VagrantPlugins::Salt::Config do include_context "unit" subject { described_class.new } 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) } describe "validate" do let(:error_key) { "salt provisioner" } it "passes by default" do subject.finalize! result = subject.validate(machine) expect(result[error_key]).to be_empty end context "minion_config" do it "fails if minion_config is set and missing" do subject.minion_config = "/nope/nope/i/dont/exist" subject.finalize! result = subject.validate(machine) expect(result[error_key]).to_not be_empty end it "is valid if is set and not missing" do subject.minion_config = File.expand_path(__FILE__) subject.finalize! result = subject.validate(machine) expect(result[error_key]).to be_empty end end context "master_config" do it "fails if master_config is set and missing" do subject.master_config = "/ceci/nest/pas/une/path" subject.finalize! result = subject.validate(machine) expect(result[error_key]).to_not be_empty end it "is valid if is set and not missing" do subject.master_config = File.expand_path(__FILE__) subject.finalize! result = subject.validate(machine) expect(result[error_key]).to be_empty end end context "grains_config" do it "fails if grains_config is set and missing" do subject.grains_config = "/nope/still/not/here" subject.finalize! result = subject.validate(machine) expect(result[error_key]).to_not be_empty end it "is valid if is set and not missing" do subject.grains_config = File.expand_path(__FILE__) subject.finalize! result = subject.validate(machine) expect(result[error_key]).to be_empty end end context "salt_call_args" do it "fails if salt_call_args is not an array" do subject.salt_call_args = "--flags" subject.finalize! result = subject.validate(machine) expect(result[error_key]).to_not be_empty end it "is valid if is set and not missing" do subject.salt_call_args = ["--flags"] subject.finalize! result = subject.validate(machine) expect(result[error_key]).to be_empty end end context "salt_args" do it "fails if not an array" do subject.salt_args = "--flags" subject.finalize! result = subject.validate(machine) expect(result[error_key]).to_not be_empty end it "is valid if is set and not missing" do subject.salt_args = ["--flags"] subject.finalize! result = subject.validate(machine) expect(result[error_key]).to be_empty end end context "python_version" do it "is valid if is set and not missing" do subject.python_version = "2" subject.finalize! result = subject.validate(machine) expect(result[error_key]).to be_empty end it "can be a string" do subject.python_version = "2" subject.finalize! result = subject.validate(machine) expect(result[error_key]).to be_empty end it "can be an integer" do subject.python_version = 2 subject.finalize! result = subject.validate(machine) expect(result[error_key]).to be_empty end it "is not a number that is not an integer" do subject.python_version = 2.7 subject.finalize! result = subject.validate(machine) expect(result[error_key]).to_not be_empty end it "is not a string that does not parse to an integer" do subject.python_version = '2.7' subject.finalize! result = subject.validate(machine) expect(result[error_key]).to_not be_empty end end end end vagrant-2.0.2/test/unit/plugins/provisioners/salt/provisioner_test.rb000066400000000000000000000143271323370221500262510ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/provisioners/salt/provisioner") describe VagrantPlugins::Salt::Provisioner do include_context "unit" subject { described_class.new(machine, config) } 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(:config) { double("config") } let(:communicator) { double("comm") } let(:guest) { double("guest") } before do allow(machine).to receive(:communicate).and_return(communicator) allow(machine).to receive(:guest).and_return(guest) allow(communicator).to receive(:execute).and_return(true) allow(communicator).to receive(:upload).and_return(true) allow(guest).to receive(:capability?).and_return(false) end describe "#provision" do context "minion" do it "does not add linux-only bootstrap flags when on windows" do additional_windows_options = "-only -these options -should -remain" allow(config).to receive(:seed_master).and_return(true) allow(config).to receive(:install_master).and_return(true) allow(config).to receive(:install_syndic).and_return(true) allow(config).to receive(:no_minion).and_return(true) allow(config).to receive(:install_type).and_return('stable') allow(config).to receive(:install_args).and_return('develop') allow(config).to receive(:verbose).and_return(true) allow(machine.config.vm).to receive(:communicator).and_return(:winrm) allow(config).to receive(:bootstrap_options).and_return(additional_windows_options) result = subject.bootstrap_options(true, true, "C:\\salttmp") expect(result.strip).to eq(additional_windows_options) end end end describe "#call_highstate" do context "master" do it "passes along extra cli flags" do allow(config).to receive(:run_highstate).and_return(true) allow(config).to receive(:verbose).and_return(true) allow(config).to receive(:masterless?).and_return(false) allow(config).to receive(:masterless).and_return(false) allow(config).to receive(:minion_id).and_return(nil) allow(config).to receive(:log_level).and_return(nil) allow(config).to receive(:colorize).and_return(false) allow(config).to receive(:pillar_data).and_return([]) allow(config).to receive(:install_master).and_return(true) allow(config).to receive(:salt_args).and_return(["--async"]) allow(machine.communicate).to receive(:sudo) allow(machine.config.vm).to receive(:communicator).and_return(:notwinrm) expect(machine.communicate).to receive(:sudo).with("salt '*' state.highstate --verbose --log-level=debug --no-color --async", {:error_key=>:ssh_bad_exit_status_muted}) subject.call_highstate() end it "has no additional cli flags if not included" do allow(config).to receive(:run_highstate).and_return(true) allow(config).to receive(:verbose).and_return(true) allow(config).to receive(:masterless?).and_return(false) allow(config).to receive(:masterless).and_return(false) allow(config).to receive(:minion_id).and_return(nil) allow(config).to receive(:log_level).and_return(nil) allow(config).to receive(:colorize).and_return(false) allow(config).to receive(:pillar_data).and_return([]) allow(config).to receive(:install_master).and_return(true) allow(config).to receive(:salt_args).and_return(nil) allow(machine.communicate).to receive(:sudo) allow(machine.config.vm).to receive(:communicator).and_return(:notwinrm) expect(machine.communicate).to receive(:sudo).with("salt '*' state.highstate --verbose --log-level=debug --no-color ", {:error_key=>:ssh_bad_exit_status_muted}) subject.call_highstate() end end context "with masterless" do it "passes along extra cli flags" do allow(config).to receive(:run_highstate).and_return(true) allow(config).to receive(:verbose).and_return(true) allow(config).to receive(:masterless?).and_return(true) allow(config).to receive(:masterless).and_return(true) allow(config).to receive(:minion_id).and_return(nil) allow(config).to receive(:log_level).and_return(nil) allow(config).to receive(:colorize).and_return(false) allow(config).to receive(:pillar_data).and_return([]) allow(config).to receive(:salt_args).and_return(["--async"]) allow(config).to receive(:salt_call_args).and_return(["--output-dif"]) allow(machine.communicate).to receive(:sudo) allow(machine.config.vm).to receive(:communicator).and_return(:notwinrm) allow(config).to receive(:install_master).and_return(false) expect(machine.communicate).to receive(:sudo).with("salt-call state.highstate --retcode-passthrough --local --log-level=debug --no-color --output-dif", {:error_key=>:ssh_bad_exit_status_muted}) subject.call_highstate() end it "has no additional cli flags if not included" do allow(config).to receive(:run_highstate).and_return(true) allow(config).to receive(:verbose).and_return(true) allow(config).to receive(:masterless?).and_return(true) allow(config).to receive(:masterless).and_return(true) allow(config).to receive(:minion_id).and_return(nil) allow(config).to receive(:log_level).and_return(nil) allow(config).to receive(:colorize).and_return(false) allow(config).to receive(:pillar_data).and_return([]) allow(config).to receive(:salt_call_args).and_return(nil) allow(config).to receive(:salt_args).and_return(nil) allow(machine.communicate).to receive(:sudo) allow(machine.config.vm).to receive(:communicator).and_return(:notwinrm) allow(config).to receive(:install_master).and_return(false) expect(machine.communicate).to receive(:sudo).with("salt-call state.highstate --retcode-passthrough --local --log-level=debug --no-color ", {:error_key=>:ssh_bad_exit_status_muted}) subject.call_highstate() end end end end vagrant-2.0.2/test/unit/plugins/provisioners/shell/000077500000000000000000000000001323370221500224435ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/provisioners/shell/config_test.rb000066400000000000000000000101171323370221500252740ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe "VagrantPlugins::Shell::Config" do let(:described_class) do VagrantPlugins::Shell::Plugin.components.configs[:provisioner][:shell] end let(:machine) { double('machine', env: Vagrant::Environment.new) } let(:file_that_exists) { File.expand_path(__FILE__) } subject { described_class.new } describe "validate" do it "passes with no args" do subject.path = file_that_exists subject.finalize! result = subject.validate(machine) expect(result["shell provisioner"]).to eq([]) end it "passes with string args" do subject.path = file_that_exists subject.args = "a string" subject.finalize! result = subject.validate(machine) expect(result["shell provisioner"]).to eq([]) end it "passes with integer args" do subject.path = file_that_exists subject.args = 1 subject.finalize! result = subject.validate(machine) expect(result["shell provisioner"]).to eq([]) end it "passes with array args" do subject.path = file_that_exists subject.args = ["an", "array"] subject.finalize! result = subject.validate(machine) expect(result["shell provisioner"]).to eq([]) end it "returns an error if args is neither a string nor an array" do neither_array_nor_string = Object.new subject.path = file_that_exists subject.args = neither_array_nor_string subject.finalize! result = subject.validate(machine) expect(result["shell provisioner"]).to eq([ I18n.t("vagrant.provisioners.shell.args_bad_type") ]) end it "handles scalar array args" do subject.path = file_that_exists subject.args = ["string", 1, 2] subject.finalize! result = subject.validate(machine) expect(result["shell provisioner"]).to eq([]) end it "returns an error if args is an array with non-scalar types" do subject.path = file_that_exists subject.args = [[1]] subject.finalize! result = subject.validate(machine) expect(result["shell provisioner"]).to eq([ I18n.t("vagrant.provisioners.shell.args_bad_type") ]) end it "returns an error if elevated_interactive is true but privileged is false" do subject.path = file_that_exists subject.powershell_elevated_interactive = true subject.privileged = false subject.finalize! result = subject.validate(machine) expect(result["shell provisioner"]).to eq([ I18n.t("vagrant.provisioners.shell.interactive_not_elevated") ]) end it "returns an error if the environment is not a hash" do subject.env = "foo" subject.finalize! result = subject.validate(machine) expect(result["shell provisioner"]).to include( I18n.t("vagrant.provisioners.shell.env_must_be_a_hash") ) end end describe 'finalize!' do it 'changes integer args into strings' do subject.path = file_that_exists subject.args = 1 subject.finalize! expect(subject.args).to eq '1' end it 'changes integer args in arrays into strings' do subject.path = file_that_exists subject.args = ["string", 1, 2] subject.finalize! expect(subject.args).to eq ["string", '1', '2'] end context "with sensitive option enabled" do it 'marks environment variable values sensitive' do subject.env = {"KEY1" => "VAL1", "KEY2" => "VAL2"} subject.sensitive = true expect(Vagrant::Util::CredentialScrubber).to receive(:sensitive).with("VAL1") expect(Vagrant::Util::CredentialScrubber).to receive(:sensitive).with("VAL2") subject.finalize! end end context "with sensitive option disabled" do it 'does not mark environment variable values sensitive' do subject.env = {"KEY1" => "VAL1", "KEY2" => "VAL2"} subject.sensitive = false expect(Vagrant::Util::CredentialScrubber).not_to receive(:sensitive) subject.finalize! end end end end vagrant-2.0.2/test/unit/plugins/provisioners/shell/provisioner_test.rb000066400000000000000000000103521323370221500264070ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/provisioners/shell/provisioner") describe "Vagrant::Shell::Provisioner" do include_context "unit" let(:env){ isolated_environment } let(:machine) { double(:machine, env: env, id: "ID").tap { |machine| allow(machine).to receive_message_chain(:config, :vm, :communicator).and_return(:not_winrm) allow(machine).to receive_message_chain(:communicate, :tap) {} } } before do allow(env).to receive(:tmp_path).and_return(Pathname.new("/dev/null")) end context "with a script that contains invalid us-ascii byte sequences" do let(:config) { double( :config, :args => "doesn't matter", :env => {}, :upload_path => "arbitrary", :remote? => false, :path => nil, :inline => script_that_is_incorrectly_us_ascii_encoded, :binary => false, ) } let(:script_that_is_incorrectly_us_ascii_encoded) { [207].pack("c*").force_encoding("US-ASCII") } it "does not raise an exception when normalizing newlines" do vsp = VagrantPlugins::Shell::Provisioner.new(machine, config) expect { vsp.provision }.not_to raise_error end end context "with a script that was set to freeze the string" do TEST_CONSTANT_VARIABLE = <<-TEST_CONSTANT_VARIABLE.freeze echo test TEST_CONSTANT_VARIABLE let(:script) { TEST_CONSTANT_VARIABLE } let(:config) { double( :config, :args => "doesn't matter", :env => {}, :upload_path => "arbitrary", :remote? => false, :path => nil, :inline => script, :binary => false, ) } it "does not raise an exception" do vsp = VagrantPlugins::Shell::Provisioner.new(machine, config) RSpec::Expectations.configuration.on_potential_false_positives = :nothing # This test should be fine, since we are specifically looking for the # string 'freeze' when RuntimeError is raised expect { vsp.provision }.not_to raise_error(RuntimeError) end end context "with remote script" do context "that does not have matching sha1 checksum" do 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 => 'EXPECTED_VALUE' ) } 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') end 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(: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 ) } 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') end it "should raise an exception" do vsp = VagrantPlugins::Shell::Provisioner.new(machine, config) expect{ vsp.provision }.to raise_error(Vagrant::Errors::DownloaderChecksumError) end end end end vagrant-2.0.2/test/unit/plugins/provisioners/support/000077500000000000000000000000001323370221500230505ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/provisioners/support/shared/000077500000000000000000000000001323370221500243165ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/provisioners/support/shared/config.rb000066400000000000000000000020611323370221500261070ustar00rootroot00000000000000def get_provisioner_option_names(provisioner_class) config_options = provisioner_class.instance_methods(true).find_all { |i| i.to_s.end_with?('=') } config_options.map! { |i| i.to_s.sub('=', '') } (config_options - ["!", "=", "=="]).sort end shared_examples_for 'any VagrantConfigProvisioner strict boolean attribute' do |attr_name, attr_default_value| [true, false].each do |bool| it "returns the assigned boolean value (#{bool})" do subject.send("#{attr_name}=", bool) subject.finalize! expect(subject.send(attr_name)).to eql(bool) end end it "returns the default value (#{attr_default_value}) if undefined" do subject.finalize! expect(subject.send(attr_name)).to eql(attr_default_value) end [nil, 'true', 'false', 1, 0, 'this is not a boolean'].each do |nobool| it "returns the default value when assigned value is invalid (#{nobool.class}: #{nobool})" do subject.send("#{attr_name}=", nobool) subject.finalize! expect(subject.send(attr_name)).to eql(attr_default_value) end end end vagrant-2.0.2/test/unit/plugins/pushes/000077500000000000000000000000001323370221500201015ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/pushes/atlas/000077500000000000000000000000001323370221500212055ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/pushes/atlas/config_test.rb000066400000000000000000000117501323370221500240420ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/pushes/atlas/config") describe VagrantPlugins::AtlasPush::Config do include_context "unit" before(:all) do I18n.load_path << Vagrant.source_root.join("plugins/pushes/atlas/locales/en.yml") I18n.reload! end let(:machine) { double("machine") } around(:each) do |example| with_temp_env("ATLAS_TOKEN" => nil) do example.run end end before do subject.token = "foo" end describe "#address" do it "defaults to nil" do subject.finalize! expect(subject.address).to be(nil) end end describe "#app" do it "defaults to nil" do subject.finalize! expect(subject.app).to be(nil) end end describe "#dir" do it "defaults to ." do subject.finalize! expect(subject.dir).to eq(".") end end describe "#vcs" do it "defaults to true" do subject.finalize! expect(subject.vcs).to be(true) end end describe "#uploader_path" do it "defaults to nil" do subject.finalize! expect(subject.uploader_path).to be(nil) end end describe "#validate" do before do allow(machine).to receive(:env) .and_return(double("env", root_path: "", data_dir: Pathname.new(""), )) subject.app = "sethvargo/bacon" subject.dir = "." subject.vcs = true subject.uploader_path = "uploader" end let(:result) { subject.validate(machine) } let(:errors) { result["Atlas push"] } context "when the token is missing" do context "when a vagrant-login token exists" do before do allow(subject).to receive(:token_from_vagrant_login) .and_return("token_from_vagrant_login") end it "uses the token from vagrant-login" do subject.token = "" subject.finalize! expect(errors).to be_empty expect(subject.token).to eq("token_from_vagrant_login") end end context "when a token is given in the Vagrantfile" do before do allow(subject).to receive(:token_from_vagrant_login) .and_return("token_from_vagrant_login") end it "uses the token in the Vagrantfile" do subject.token = "token_from_vagrantfile" subject.finalize! expect(errors).to be_empty expect(subject.token).to eq("token_from_vagrantfile") end end context "when a token is in the environment" do it "uses the token in the Vagrantfile" do with_temp_env("ATLAS_TOKEN" => "foo") do subject.finalize! end expect(errors).to be_empty expect(subject.token).to eq("foo") end end context "when no token is given" do before do allow(subject).to receive(:token_from_vagrant_login) .and_return(nil) end it "returns an error" do subject.token = "" subject.finalize! expect(errors).to include(I18n.t("atlas_push.errors.missing_token")) end end end context "when the app is missing" do it "returns an error" do subject.app = "" subject.finalize! expect(errors).to include(I18n.t("atlas_push.errors.missing_attribute", attribute: "app", )) end end context "when the dir is missing" do it "returns an error" do subject.dir = "" subject.finalize! expect(errors).to include(I18n.t("atlas_push.errors.missing_attribute", attribute: "dir", )) end end context "when the vcs is missing" do it "does not return an error" do subject.vcs = "" subject.finalize! expect(errors).to be_empty end end context "when the uploader_path is missing" do it "returns an error" do subject.uploader_path = "" subject.finalize! expect(errors).to be_empty end end end describe "#merge" do context "when includes are given" do let(:one) { described_class.new } let(:two) { described_class.new } it "merges the result" do one.includes = %w(a b c) two.includes = %w(c d e) result = one.merge(two) expect(result.includes).to eq(%w(a b c d e)) end end context "when excludes are given" do let(:one) { described_class.new } let(:two) { described_class.new } it "merges the result" do one.excludes = %w(a b c) two.excludes = %w(c d e) result = one.merge(two) expect(result.excludes).to eq(%w(a b c d e)) end end end describe "#include" do it "adds the item to the list" do subject.include("me") expect(subject.includes).to include("me") end end describe "#exclude" do it "adds the item to the list" do subject.exclude("not me") expect(subject.excludes).to include("not me") end end end vagrant-2.0.2/test/unit/plugins/pushes/atlas/push_test.rb000066400000000000000000000124501323370221500235520ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/pushes/atlas/config") require Vagrant.source_root.join("plugins/pushes/atlas/push") describe VagrantPlugins::AtlasPush::Push do include_context "unit" let(:bin) { VagrantPlugins::AtlasPush::Push::UPLOADER_BIN } let(:env) do iso_env = isolated_environment iso_env.vagrantfile("") iso_env.create_vagrant_env end let(:config) do VagrantPlugins::AtlasPush::Config.new.tap do |c| c.finalize! end end subject { described_class.new(env, config) } before do # Stub this right away to avoid real execs allow(Vagrant::Util::SafeExec).to receive(:exec) end # DEPRECATED # describe "#push" do # it "pushes with the uploader" do # allow(subject).to receive(:uploader_path).and_return("foo") # expect(subject).to receive(:execute).with("foo") # subject.push # end # it "raises an exception if the uploader couldn't be found" do # expect(subject).to receive(:uploader_path).and_return(nil) # expect { subject.push }.to raise_error( # VagrantPlugins::AtlasPush::Errors::UploaderNotFound) # end # end # describe "#execute" do # let(:app) { "foo/bar" } # before do # config.app = app # end # it "sends the basic flags" do # expect(Vagrant::Util::SafeExec).to receive(:exec). # with("foo", "-vcs", app, env.root_path.to_s) # subject.execute("foo") # end # it "doesn't send VCS if disabled" do # expect(Vagrant::Util::SafeExec).to receive(:exec). # with("foo", app, env.root_path.to_s) # config.vcs = false # subject.execute("foo") # end # it "sends includes" do # expect(Vagrant::Util::SafeExec).to receive(:exec). # with("foo", "-vcs", "-include", "foo", "-include", # "bar", app, env.root_path.to_s) # config.includes = ["foo", "bar"] # subject.execute("foo") # end # it "sends excludes" do # expect(Vagrant::Util::SafeExec).to receive(:exec). # with("foo", "-vcs", "-exclude", "foo", "-exclude", # "bar", app, env.root_path.to_s) # config.excludes = ["foo", "bar"] # subject.execute("foo") # end # it "sends custom server address" do # expect(Vagrant::Util::SafeExec).to receive(:exec). # with("foo", "-vcs", "-address", "foo", app, env.root_path.to_s) # config.address = "foo" # subject.execute("foo") # end # it "sends custom token" do # expect(Vagrant::Util::SafeExec).to receive(:exec). # with("foo", "-vcs", "-token", "atlas_token", app, env.root_path.to_s) # config.token = "atlas_token" # subject.execute("foo") # end # context "when metadata is available" do # let(:env) do # iso_env = isolated_environment # iso_env.vagrantfile <<-EOH # Vagrant.configure("2") do |config| # config.vm.box = "hashicorp/precise64" # config.vm.box_url = "https://atlas.hashicorp.com/hashicorp/precise64" # end # EOH # iso_env.create_vagrant_env # end # it "sends the metadata" do # expect(Vagrant::Util::SafeExec).to receive(:exec). # with("foo", "-vcs", "-metadata", "box=hashicorp/precise64", # "-metadata", "box_url=https://atlas.hashicorp.com/hashicorp/precise64", # "-token", "atlas_token", app, env.root_path.to_s) # config.token = "atlas_token" # subject.execute("foo") # end # end # end # describe "#uploader_path" do # let(:scratch) do # Pathname.new(Dir.mktmpdir("vagrant-test-atlas-push-upload-path")) # end # after do # FileUtils.rm_rf(scratch) # end # it "should return the configured path if set" do # config.uploader_path = "foo" # expect(subject.uploader_path).to eq("foo") # end # it "should look up the uploader via PATH if not set" do # allow(Vagrant).to receive(:in_installer?).and_return(false) # expect(Vagrant::Util::Which).to receive(:which). # with(described_class.const_get(:UPLOADER_BIN)). # and_return("bar") # expect(subject.uploader_path).to eq("bar") # end # it "should look up the uploader in the embedded dir if installer" do # allow(Vagrant).to receive(:in_installer?).and_return(true) # allow(Vagrant).to receive(:installer_embedded_dir).and_return(scratch.to_s) # bin_path = scratch.join("bin", bin) # bin_path.dirname.mkpath # bin_path.open("w+") { |f| f.write("hi") } # expect(subject.uploader_path).to eq(bin_path.to_s) # end # it "should look up the uploader in the PATH if not in the installer" do # allow(Vagrant).to receive(:in_installer?).and_return(true) # allow(Vagrant).to receive(:installer_embedded_dir).and_return(scratch.to_s) # expect(Vagrant::Util::Which).to receive(:which). # with(described_class.const_get(:UPLOADER_BIN)). # and_return("bar") # expect(subject.uploader_path).to eq("bar") # end # it "should return nil if its not found anywhere" do # allow(Vagrant).to receive(:in_installer?).and_return(false) # allow(Vagrant::Util::Which).to receive(:which).and_return(nil) # expect(subject.uploader_path).to be_nil # end # end end vagrant-2.0.2/test/unit/plugins/pushes/ftp/000077500000000000000000000000001323370221500206725ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/pushes/ftp/adapter_test.rb000066400000000000000000000046621323370221500237060ustar00rootroot00000000000000require_relative "../../../base" require "fake_ftp" require Vagrant.source_root.join("plugins/pushes/ftp/adapter") describe VagrantPlugins::FTPPush::Adapter do include_context "unit" subject do described_class.new("127.0.0.1:2345", "sethvargo", "bacon", foo: "bar", ) end describe "#initialize" do it "sets the instance variables" do expect(subject.host).to eq("127.0.0.1") expect(subject.port).to eq(2345) expect(subject.username).to eq("sethvargo") expect(subject.password).to eq("bacon") expect(subject.options).to eq(foo: "bar") expect(subject.server).to be(nil) end end describe "#parse_host" do it "has a default value" do allow(subject).to receive(:default_port) .and_return(5555) result = subject.parse_host("127.0.0.1") expect(result[0]).to eq("127.0.0.1") expect(result[1]).to eq(5555) end end end describe VagrantPlugins::FTPPush::FTPAdapter do include_context "unit" before(:all) do @server = nil with_random_port do |port1, port2| @server = FakeFtp::Server.new(port1, port2) end @server.start end after(:all) { @server.stop } let(:server) { @server } before { server.reset } subject do described_class.new("127.0.0.1:#{server.port}", "sethvargo", "bacon") end describe "#default_port" do it "is 21" do expect(subject.default_port).to eq(21) end end describe "#upload" do before do @dir = Dir.mktmpdir("vagrant-ftp-push-adapter-upload") FileUtils.touch("#{@dir}/file") end after do FileUtils.rm_rf(@dir) end it "uploads the file" do subject.connect do |ftp| ftp.upload("#{@dir}/file", "/file") end expect(server.files).to include("file") end it "uploads in passive mode" do subject.options[:passive] = true subject.connect do |ftp| ftp.upload("#{@dir}/file", "/file") end expect(server.file("file")).to be_passive end end end describe VagrantPlugins::FTPPush::SFTPAdapter do include_context "unit" subject do described_class.new("127.0.0.1:2345", "sethvargo", "bacon", foo: "bar", ) end describe "#default_port" do it "is 22" do expect(subject.default_port).to eq(22) end end describe "#upload" do it "uploads the file" do pending "a way to mock an SFTP server" test_with_mock_sftp_server end end end vagrant-2.0.2/test/unit/plugins/pushes/ftp/config_test.rb000066400000000000000000000076431323370221500235350ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/pushes/ftp/config") describe VagrantPlugins::FTPPush::Config do include_context "unit" before(:all) do I18n.load_path << Vagrant.source_root.join("plugins/pushes/ftp/locales/en.yml") I18n.reload! end subject { described_class.new } let(:machine) { double("machine") } describe "#host" do it "defaults to nil" do subject.finalize! expect(subject.host).to be(nil) end end describe "#username" do it "defaults to nil" do subject.finalize! expect(subject.username).to be(nil) end end describe "#password" do it "defaults to nil" do subject.finalize! expect(subject.password).to be(nil) end end describe "#passive" do it "defaults to true" do subject.finalize! expect(subject.passive).to be(true) end end describe "#secure" do it "defaults to false" do subject.finalize! expect(subject.secure).to be(false) end end describe "#destination" do it "defaults to /" do subject.finalize! expect(subject.destination).to eq("/") end end describe "#dir" do it "defaults to ." do subject.finalize! expect(subject.dir).to eq(".") end end describe "#merge" do context "when includes are given" do let(:one) { described_class.new } let(:two) { described_class.new } it "merges the result" do one.includes = %w(a b c) two.includes = %w(c d e) result = one.merge(two) expect(result.includes).to eq(%w(a b c d e)) end end context "when excludes are given" do let(:one) { described_class.new } let(:two) { described_class.new } it "merges the result" do one.excludes = %w(a b c) two.excludes = %w(c d e) result = one.merge(two) expect(result.excludes).to eq(%w(a b c d e)) end end end describe "#validate" do before do allow(machine).to receive(:env) .and_return(double("env", root_path: "", )) subject.host = "ftp.example.com" subject.username = "sethvargo" subject.password = "bacon" subject.destination = "/" subject.dir = "." end let(:result) { subject.validate(machine) } let(:errors) { result["FTP push"] } context "when the host is missing" do it "returns an error" do subject.host = "" subject.finalize! expect(errors).to include(I18n.t("ftp_push.errors.missing_attribute", attribute: "host", )) end end context "when the username is missing" do it "returns an error" do subject.username = "" subject.finalize! expect(errors).to include(I18n.t("ftp_push.errors.missing_attribute", attribute: "username", )) end end context "when the password is missing" do it "does not return an error" do subject.password = "" subject.finalize! expect(errors).to be_empty end end context "when the destination is missing" do it "returns an error" do subject.destination = "" subject.finalize! expect(errors).to include(I18n.t("ftp_push.errors.missing_attribute", attribute: "destination", )) end end context "when the dir is missing" do it "returns an error" do subject.dir = "" subject.finalize! expect(errors).to include(I18n.t("ftp_push.errors.missing_attribute", attribute: "dir", )) end end end describe "#include" do it "adds the item to the list" do subject.include("me") expect(subject.includes).to include("me") end end describe "#exclude" do it "adds the item to the list" do subject.exclude("not me") expect(subject.excludes).to include("not me") end end end vagrant-2.0.2/test/unit/plugins/pushes/ftp/push_test.rb000066400000000000000000000154541323370221500232460ustar00rootroot00000000000000require_relative "../../../base" require "fake_ftp" require Vagrant.source_root.join("plugins/pushes/ftp/push") describe VagrantPlugins::FTPPush::Push do include_context "unit" let(:env) { isolated_environment } let(:config) do double("config", host: "127.0.0.1:#{@port}", username: "sethvargo", password: "bacon", passive: false, secure: false, destination: "/var/www/site", ) end let(:ui) do double("ui", info: nil, ) end subject { described_class.new(env, config) } before do allow(env).to receive(:root_path) .and_return(File.expand_path("..", __FILE__)) allow(env).to receive(:ui) .and_return(ui) end describe "#push" do before(:all) do @server = nil with_random_port do |port1, port2| @port = port1 @server = FakeFtp::Server.new(port1, port2) end @server.start @dir = Dir.mktmpdir("vagrant-ftp-push") FileUtils.touch("#{@dir}/.hidden.rb") FileUtils.touch("#{@dir}/application.rb") FileUtils.touch("#{@dir}/config.rb") FileUtils.touch("#{@dir}/Gemfile") FileUtils.touch("#{@dir}/data.txt") FileUtils.mkdir("#{@dir}/empty_folder") end after(:all) do FileUtils.rm_rf(@dir) @server.stop end let(:server) { @server } before do allow(config).to receive(:dir) .and_return(@dir) allow(config).to receive(:includes) .and_return([]) allow(config).to receive(:excludes) .and_return(%w(*.rb)) end it "pushes the files to the server" do subject.push expect(server.files).to eq(%w(Gemfile data.txt)) end end describe "#connect" do before do allow_any_instance_of(VagrantPlugins::FTPPush::FTPAdapter) .to receive(:connect) .and_yield(:ftp) allow_any_instance_of(VagrantPlugins::FTPPush::SFTPAdapter) .to receive(:connect) .and_yield(:sftp) end context "when secure is requested" do before do allow(config).to receive(:secure) .and_return(true) end it "yields a new SFTPAdapter" do expect { |b| subject.connect(&b) }.to yield_with_args(:sftp) end end context "when secure is not requested" do before do allow(config).to receive(:secure) .and_return(false) end it "yields a new FTPAdapter" do expect { |b| subject.connect(&b) }.to yield_with_args(:ftp) end end end describe "#all_files" do before(:all) do @dir = Dir.mktmpdir("vagrant-ftp-push-push-all-files") FileUtils.touch("#{@dir}/.hidden.rb") FileUtils.touch("#{@dir}/application.rb") FileUtils.touch("#{@dir}/config.rb") FileUtils.touch("#{@dir}/Gemfile") FileUtils.mkdir("#{@dir}/empty_folder") FileUtils.mkdir("#{@dir}/folder") FileUtils.mkdir("#{@dir}/folder/.git") FileUtils.touch("#{@dir}/folder/.git/config") FileUtils.touch("#{@dir}/folder/server.rb") end after(:all) do FileUtils.rm_rf(@dir) end let(:files) do subject.all_files.map do |file| file.sub("#{@dir}/", "") end end before do allow(config).to receive(:dir) .and_return(@dir) allow(config).to receive(:includes) .and_return(%w(not_a_file.rb still_not_a_file.rb)) allow(config).to receive(:excludes) .and_return(%w(*.rb)) end it "returns the list of real files + includes, without excludes" do expect(files).to eq(%w( Gemfile folder/.git/config )) end end describe "#includes_files" do before(:all) do @dir = Dir.mktmpdir("vagrant-ftp-push-includes-files") FileUtils.touch("#{@dir}/.hidden.rb") FileUtils.touch("#{@dir}/application.rb") FileUtils.touch("#{@dir}/config.rb") FileUtils.touch("#{@dir}/Gemfile") FileUtils.mkdir("#{@dir}/folder") FileUtils.mkdir("#{@dir}/folder/.git") FileUtils.touch("#{@dir}/folder/.git/config") FileUtils.touch("#{@dir}/folder/server.rb") end after(:all) do FileUtils.rm_rf(@dir) end let(:files) do subject.includes_files.map do |file| file.sub("#{@dir}/", "") end end before do allow(config).to receive(:dir) .and_return(@dir) end def set_includes(value) allow(config).to receive(:includes) .and_return(value) end it "includes the file" do set_includes(["Gemfile"]) expect(files).to eq(%w( Gemfile )) end it "includes the files that are subdirectories" do set_includes(["folder"]) expect(files).to eq(%w( folder folder/.git folder/.git/config folder/server.rb )) end it "includes files that match a pattern" do set_includes(["*.rb"]) expect(files).to eq(%w( .hidden.rb application.rb config.rb )) end end describe "#filter_excludes" do let(:dir) { "/root/dir" } let(:list) do %W( #{dir}/.hidden.rb #{dir}/application.rb #{dir}/config.rb #{dir}/Gemfile #{dir}/folder #{dir}/folder/.git #{dir}/folder/.git/config #{dir}/folder/server.rb /path/outside/you.rb /path/outside/me.rb /path/outside/folder/bacon.rb ) end before do allow(config).to receive(:dir) .and_return(dir) end it "excludes files" do subject.filter_excludes!(list, %w(*.rb)) expect(list).to eq(%W( #{dir}/Gemfile #{dir}/folder #{dir}/folder/.git #{dir}/folder/.git/config )) end it "excludes files in a directory" do subject.filter_excludes!(list, %w(folder)) expect(list).to eq(%W( #{dir}/.hidden.rb #{dir}/application.rb #{dir}/config.rb #{dir}/Gemfile /path/outside/you.rb /path/outside/me.rb /path/outside/folder/bacon.rb )) end it "excludes specific files in a directory" do subject.filter_excludes!(list, %w(/path/outside/folder/*.rb)) expect(list).to eq(%W( #{dir}/.hidden.rb #{dir}/application.rb #{dir}/config.rb #{dir}/Gemfile #{dir}/folder #{dir}/folder/.git #{dir}/folder/.git/config #{dir}/folder/server.rb /path/outside/you.rb /path/outside/me.rb )) end it "excludes files outside the #dir" do subject.filter_excludes!(list, %w(/path/outside)) expect(list).to eq(%W( #{dir}/.hidden.rb #{dir}/application.rb #{dir}/config.rb #{dir}/Gemfile #{dir}/folder #{dir}/folder/.git #{dir}/folder/.git/config #{dir}/folder/server.rb )) end end end vagrant-2.0.2/test/unit/plugins/pushes/heroku/000077500000000000000000000000001323370221500213765ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/pushes/heroku/config_test.rb000066400000000000000000000043231323370221500242310ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/pushes/heroku/config") describe VagrantPlugins::HerokuPush::Config do include_context "unit" before(:all) do I18n.load_path << Vagrant.source_root.join("plugins/pushes/heroku/locales/en.yml") I18n.reload! end subject { described_class.new } let(:machine) { double("machine") } describe "#app" do it "defaults to nil" do subject.finalize! expect(subject.app).to be(nil) end end describe "#dir" do it "defaults to ." do subject.finalize! expect(subject.dir).to eq(".") end end describe "#git_bin" do it "defaults to git" do subject.finalize! expect(subject.git_bin).to eq("git") end end describe "#remote" do it "defaults to git" do subject.finalize! expect(subject.remote).to eq("heroku") end end describe "#validate" do before do allow(machine).to receive(:env) .and_return(double("env", root_path: "", )) subject.app = "bacon" subject.dir = "." subject.git_bin = "git" subject.remote = "heroku" end let(:result) { subject.validate(machine) } let(:errors) { result["Heroku push"] } context "when the app is missing" do it "does not return an error" do subject.app = "" subject.finalize! expect(errors).to be_empty end end context "when the git_bin is missing" do it "returns an error" do subject.git_bin = "" subject.finalize! expect(errors).to include(I18n.t("heroku_push.errors.missing_attribute", attribute: "git_bin", )) end end context "when the remote is missing" do it "returns an error" do subject.remote = "" subject.finalize! expect(errors).to include(I18n.t("heroku_push.errors.missing_attribute", attribute: "remote", )) end end context "when the dir is missing" do it "returns an error" do subject.dir = "" subject.finalize! expect(errors).to include(I18n.t("heroku_push.errors.missing_attribute", attribute: "dir", )) end end end end vagrant-2.0.2/test/unit/plugins/pushes/heroku/push_test.rb000066400000000000000000000174631323370221500237540ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/platform" require Vagrant.source_root.join("plugins/pushes/heroku/push") describe VagrantPlugins::HerokuPush::Push do include_context "unit" before(:all) do I18n.load_path << Vagrant.source_root.join("plugins/pushes/heroku/locales/en.yml") I18n.reload! end let(:env) { isolated_environment } let(:config) do double("config", app: "bacon", dir: "lib", git_bin: "git", remote: "heroku", ) end subject { described_class.new(env, config) } describe "#push" do let(:branch) { "master" } let(:dir) { "#{root_path}/#{config.dir}" } let(:root_path) do next "/handy/dandy" if !Vagrant::Util::Platform.windows? "C:/handy/dandy" end before do allow(subject).to receive(:git_branch) .and_return(branch) allow(subject).to receive(:verify_git_bin!) allow(subject).to receive(:verify_git_repo!) allow(subject).to receive(:has_git_remote?) allow(subject).to receive(:add_heroku_git_remote) allow(subject).to receive(:git_push_heroku) allow(subject).to receive(:execute!) allow(env).to receive(:root_path) .and_return(root_path) end it "verifies the git bin is present" do expect(subject).to receive(:verify_git_bin!) .with(config.git_bin) subject.push end it "verifies the directory is a git repo" do expect(subject).to receive(:verify_git_repo!) .with(dir) subject.push end context "when the heroku remote exists" do before do allow(subject).to receive(:has_git_remote?) .and_return(true) end it "does not add the heroku remote" do expect(subject).to_not receive(:add_heroku_git_remote) subject.push end end context "when the heroku remote does not exist" do before do allow(subject).to receive(:has_git_remote?) .and_return(false) end it "adds the heroku remote" do expect(subject).to receive(:add_heroku_git_remote) .with(config.remote, config.app, dir) subject.push end end it "pushes to heroku" do expect(subject).to receive(:git_push_heroku) .with(config.remote, branch, dir) subject.push end end describe "#verify_git_bin!" do context "when git does not exist" do before do allow(Vagrant::Util::Which).to receive(:which) .with("git") .and_return(nil) end it "raises an exception" do expect { subject.verify_git_bin!("git") } .to raise_error(VagrantPlugins::HerokuPush::Errors::GitNotFound) { |error| expect(error.message).to eq(I18n.t("heroku_push.errors.git_not_found", bin: "git", )) } end end context "when git exists" do before do allow(Vagrant::Util::Which).to receive(:which) .with("git") .and_return("git") end it "does not raise an exception" do expect { subject.verify_git_bin!("git") }.to_not raise_error end end end describe "#verify_git_repo!" do context "when the path is a git repo" do before do allow(File).to receive(:directory?) .with("/repo/path/.git") .and_return(false) end it "raises an exception" do expect { subject.verify_git_repo!("/repo/path") } .to raise_error(VagrantPlugins::HerokuPush::Errors::NotAGitRepo) { |error| expect(error.message).to eq(I18n.t("heroku_push.errors.not_a_git_repo", path: "/repo/path", )) } end end context "when the path is not a git repo" do before do allow(File).to receive(:directory?) .with("/repo/path/.git") .and_return(true) end it "does not raise an exception" do expect { subject.verify_git_repo!("/repo/path") }.to_not raise_error end end end describe "#git_push_heroku" do let(:dir) { "." } before { allow(subject).to receive(:execute!) } it "executes the proper command" do expect(subject).to receive(:execute!) .with("git", "--git-dir", "#{dir}/.git", "--work-tree", dir, "push", "bacon", "hamlet:master", ) subject.git_push_heroku("bacon", "hamlet", dir) end end describe "#has_git_remote?" do let(:dir) { "." } let(:process) do double("process", stdout: "origin\r\nbacon\nhello" ) end before do allow(subject).to receive(:execute!) .and_return(process) end it "executes the proper command" do expect(subject).to receive(:execute!) .with("git", "--git-dir", "#{dir}/.git", "--work-tree", dir, "remote", ) subject.has_git_remote?("bacon", dir) end it "returns true when the remote exists" do expect(subject.has_git_remote?("origin", dir)).to be(true) expect(subject.has_git_remote?("bacon", dir)).to be(true) expect(subject.has_git_remote?("hello", dir)).to be(true) end it "returns false when the remote does not exist" do expect(subject.has_git_remote?("nope", dir)).to be(false) end end describe "#add_heroku_git_remote" do let(:dir) { "." } before do allow(subject).to receive(:execute!) allow(subject).to receive(:heroku_git_url) .with("app") .and_return("HEROKU_URL") end it "executes the proper command" do expect(subject).to receive(:execute!) .with("git", "--git-dir", "#{dir}/.git", "--work-tree", dir, "remote", "add", "bacon", "HEROKU_URL", ) subject.add_heroku_git_remote("bacon", "app", dir) end end describe "#interpret_app" do it "returns the basename of the directory" do expect(subject.interpret_app("/foo/bar/blitz")).to eq("blitz") end end describe "#heroku_git_url" do it "returns the proper string" do expect(subject.heroku_git_url("bacon")) .to eq("git@heroku.com:bacon.git") end end describe "#git_dir" do it "returns the .git directory for the path" do expect(subject.git_dir("/path")).to eq("/path/.git") end end describe "#git_branch" do let(:stdout) { "" } let(:process) { double("process", stdout: stdout) } before do allow(subject).to receive(:execute!) .and_return(process) end let(:branch) { subject.git_branch("/path") } context "when the branch is not prefixed" do let(:stdout) { "bacon" } it "returns the correct name" do expect(branch).to eq("bacon") end end end describe "#execute!" do let(:exit_code) { 0 } let(:stdout) { "This is the output" } let(:stderr) { "This is the errput" } let(:process) do double("process", exit_code: exit_code, stdout: stdout, stderr: stderr, ) end before do allow(Vagrant::Util::Subprocess).to receive(:execute) .and_return(process) end it "creates a subprocess" do expect(Vagrant::Util::Subprocess).to receive(:execute) expect { subject.execute! }.to_not raise_error end it "returns the resulting process" do expect(subject.execute!).to be(process) end context "when the exit code is non-zero" do let(:exit_code) { 1 } it "raises an exception" do klass = VagrantPlugins::HerokuPush::Errors::CommandFailed cmd = ["foo", "bar"] expect { subject.execute!(*cmd) }.to raise_error(klass) { |error| expect(error.message).to eq(I18n.t("heroku_push.errors.command_failed", cmd: cmd.join(" "), stdout: stdout, stderr: stderr, )) } end end end end vagrant-2.0.2/test/unit/plugins/pushes/local-exec/000077500000000000000000000000001323370221500221155ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/pushes/local-exec/config_test.rb000066400000000000000000000101301323370221500247410ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/pushes/local-exec/config") describe VagrantPlugins::LocalExecPush::Config do include_context "unit" before(:all) do I18n.load_path << Vagrant.source_root.join("plugins/pushes/local-exec/locales/en.yml") I18n.reload! end let(:machine) { double("machine") } describe "#script" do it "defaults to nil" do subject.finalize! expect(subject.script).to be(nil) end end describe "#inline" do it "defaults to nil" do subject.finalize! expect(subject.inline).to be(nil) end end describe "#args" do it "defaults to nil" do subject.finalize! expect(subject.args).to be(nil) end end describe "#validate" do before do allow(machine).to receive(:env) .and_return(double("env", root_path: "", )) subject.finalize! end let(:result) { subject.validate(machine) } let(:errors) { result["Local Exec push"] } context "when script is present" do before { subject.script = "foo.sh" } context "when inline is present" do before { subject.inline = "echo" } it "returns an error" do expect(errors).to include( I18n.t("local_exec_push.errors.cannot_specify_script_and_inline") ) end end context "when inline is not present" do before { subject.inline = "" } it "does not return an error" do expect(errors).to be_empty end it "passes with string args" do subject.args = "a string" expect(errors).to be_empty end it "passes with integer args" do subject.args = 1 expect(errors).to be_empty end it "passes with array args" do subject.args = ["an", "array"] expect(errors).to be_empty end it "returns an error if args is neither a string nor an array" do neither_array_nor_string = Object.new subject.args = neither_array_nor_string expect(errors).to include( I18n.t("local_exec_push.errors.args_bad_type") ) end it "handles scalar array args" do subject.args = ["string", 1, 2] expect(errors).to be_empty end it "returns an error if args is an array with non-scalar types" do subject.args = [[1]] expect(errors).to include( I18n.t("local_exec_push.errors.args_bad_type") ) end end end context "when script is not present" do before { subject.script = "" } context "when inline is present" do before { subject.inline = "echo" } it "does not return an error" do expect(errors).to be_empty end it "passes with string args" do subject.args = "a string" expect(errors).to be_empty end it "passes with integer args" do subject.args = 1 expect(errors).to be_empty end it "passes with array args" do subject.args = ["an", "array"] expect(errors).to be_empty end it "returns an error if args is neither a string nor an array" do neither_array_nor_string = Object.new subject.args = neither_array_nor_string expect(errors).to include( I18n.t("local_exec_push.errors.args_bad_type") ) end it "handles scalar array args" do subject.args = ["string", 1, 2] expect(errors).to be_empty end it "returns an error if args is an array with non-scalar types" do subject.args = [[1]] expect(errors).to include( I18n.t("local_exec_push.errors.args_bad_type") ) end end context "when inline is not present" do before { subject.inline = "" } it "returns an error" do expect(errors).to include(I18n.t("local_exec_push.errors.missing_attribute", attribute: "script", )) end end end end end vagrant-2.0.2/test/unit/plugins/pushes/local-exec/push_test.rb000066400000000000000000000071661323370221500244720ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/pushes/local-exec/push") describe VagrantPlugins::LocalExecPush::Push do include_context "unit" before(:all) do I18n.load_path << Vagrant.source_root.join("plugins/pushes/local-exec/locales/en.yml") I18n.reload! end let(:env) { isolated_environment } let(:config) do double("config", script: nil, inline: nil, args: "some args", ) end subject { described_class.new(env, config) } before do allow(env).to receive(:root_path) .and_return(File.expand_path("..", __FILE__)) end describe "#push" do before do allow(subject).to receive(:execute_inline!) allow(subject).to receive(:execute_script!) allow(subject).to receive(:execute!) end context "when inline is given" do before { allow(config).to receive(:inline).and_return("echo") } it "executes the inline script" do expect(subject).to receive(:execute_inline!) .with(config.inline, config.args) subject.push end end context "when script is given" do before { allow(config).to receive(:script).and_return("foo.sh") } it "executes the script" do expect(subject).to receive(:execute_script!) .with(config.script, config.args) subject.push end end end describe "#execute_inline!" do before { allow(subject).to receive(:execute_script!) } it "writes the script to a tempfile" do expect(Tempfile).to receive(:new).and_call_original subject.execute_inline!("echo", config.args) end it "executes the script" do expect(subject).to receive(:execute_script!) subject.execute_inline!("echo", config.args) end end describe "#execute_script!" do before do allow(subject).to receive(:execute!) allow(FileUtils).to receive(:chmod) end it "expands the path relative to the machine root" do expect(subject).to receive(:execute!) .with(File.expand_path("foo.sh", env.root_path)) subject.execute_script!("./foo.sh", nil) end it "makes the file executable" do expect(FileUtils).to receive(:chmod) .with("+x", File.expand_path("foo.sh", env.root_path)) subject.execute_script!("./foo.sh", config.args) end it "calls execute!" do expect(subject).to receive(:execute!) .with(File.expand_path("foo.sh", env.root_path)) subject.execute_script!("./foo.sh", nil) end context "when args is given" do it "passes string args to execute!" do expect(subject).to receive(:execute!) .with(File.expand_path("foo.sh", env.root_path) + " " + config.args) subject.execute_script!("./foo.sh", config.args) end it "passes array args as string to execute!" do expect(subject).to receive(:execute!) .with(File.expand_path("foo.sh", env.root_path) + " \"one\" \"two\" \"three\"") subject.execute_script!("./foo.sh", ["one", "two", "three"]) end end end describe "#execute!" do it "uses exec on unix" do allow(Vagrant::Util::Platform).to receive(:windows?).and_return(false) expect(Vagrant::Util::SafeExec).to receive(:exec) expect { subject.execute! }.to_not raise_error end it "uses subprocess on windows" do allow(Vagrant::Util::Platform).to receive(:windows?).and_return(true) result = double("result", exit_code: 0) expect(Vagrant::Util::Subprocess).to receive(:execute).and_return(result) expect { subject.execute! }.to raise_error { |e| expect(e).to be_a(SystemExit) } end end end vagrant-2.0.2/test/unit/plugins/pushes/noop/000077500000000000000000000000001323370221500210545ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/pushes/noop/config_test.rb000066400000000000000000000004311323370221500237030ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/pushes/noop/config") describe VagrantPlugins::NoopDeploy::Config do include_context "unit" subject { described_class.new } let(:machine) { double("machine") } describe "#validate" do end end vagrant-2.0.2/test/unit/plugins/synced_folders/000077500000000000000000000000001323370221500215755ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/synced_folders/nfs/000077500000000000000000000000001323370221500223635ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/synced_folders/nfs/action_cleanup_test.rb000066400000000000000000000025531323370221500267400ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/synced_folders/nfs/action_cleanup") describe VagrantPlugins::SyncedFolderNFS::ActionCleanup 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(:host) { double("host") } let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } let(:app) { lambda {} } let(:env) { { machine: machine, } } subject { described_class.new(app, env) } before do allow(machine.env).to receive(:host).and_return(host) end it "does nothing if there are no valid IDs" do expect(app).to receive(:call).with(env) subject.call(env) end it "does nothing if the host doesn't support pruning NFS" do allow(host).to receive(:capability?).with(:nfs_prune).and_return(false) expect(host).to receive(:capability).never expect(app).to receive(:call).with(env) subject.call(env) end it "prunes the NFS entries if valid IDs are given" do env[:nfs_valid_ids] = [1,2,3] allow(host).to receive(:capability?).with(:nfs_prune).and_return(true) expect(host).to receive(:capability).with(:nfs_prune, machine.ui, [1,2,3]).ordered expect(app).to receive(:call).with(env).ordered subject.call(env) end end vagrant-2.0.2/test/unit/plugins/synced_folders/nfs/config_test.rb000066400000000000000000000006361323370221500252210ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/synced_folders/nfs/config") describe VagrantPlugins::SyncedFolderNFS::Config do subject { described_class.new } context "defaults" do before do subject.finalize! end its(:functional) { should be(true) } its(:map_gid) { should eq(:auto) } its(:map_uid) { should eq(:auto) } end end vagrant-2.0.2/test/unit/plugins/synced_folders/rsync/000077500000000000000000000000001323370221500227335ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/synced_folders/rsync/command/000077500000000000000000000000001323370221500243515ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/synced_folders/rsync/command/rsync_auto_test.rb000066400000000000000000000170131323370221500301250ustar00rootroot00000000000000require_relative "../../../../base" require Vagrant.source_root.join("plugins/synced_folders/rsync/command/rsync_auto") describe VagrantPlugins::SyncedFolderRSync::Command::RsyncAuto do include_context "unit" let(:argv) { [] } 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(:synced_folders_empty) { {} } let(:synced_folders_dupe) { {"1234": {type: "rsync", exclude: false, hostpath: "/Users/brian/code/vagrant-sandbox"}, "5678": {type: "rsync", exclude: false, hostpath: "/Not/The/Same/Path"}, "0912": {type: "rsync", exclude: false, hostpath: "/Users/brian/code/relative-dir"}}} let(:helper_class) { VagrantPlugins::SyncedFolderRSync::RsyncHelper } let(:paths) { {} } let(:ssh_info) {{}} def machine_stub(name) double(name).tap do |m| allow(m).to receive(:id).and_return("foo") allow(m).to receive(:reload).and_return(nil) allow(m).to receive(:ssh_info).and_return(ssh_info) allow(m).to receive(:ui).and_return(iso_env.ui) allow(m).to receive(:provider).and_return(double("provider")) allow(m).to receive(:state).and_return(double("state", id: :not_created)) allow(m).to receive(:env).and_return(iso_env) allow(m).to receive(:config).and_return(double("config")) allow(m.ui).to receive(:error).and_return(nil) end end subject do described_class.new(argv, iso_env).tap end describe "#execute" do let (:machine) { machine_stub("m") } let (:cached_folders) { { rsync: synced_folders_dupe } } # NOTE: `relative-dir` is not actually a "relative dir" in this data structure # due to the fact that when vagrant stores synced folders, it path expands # them with root_dir, and when you grab those synced_folders options from # the machines config file, they end up being a full path rather than a # relative path, and so these tests reflect that. # For reference: # https://github.com/mitchellh/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 allow(subject).to receive(:with_target_vms) { |&block| block.call machine } end it "does not sync folders outside of the cwd" do allow(machine.ui).to receive(:info) allow(machine.state).to receive(:id).and_return(:created) allow(machine.env).to receive(:cwd). and_return("/Users/brian/code/vagrant-sandbox") allow(machine.provider).to receive(:capability?).and_return(false) allow(machine.config).to receive(:vm).and_return(true) allow(machine.config.vm).to receive(:synced_folders).and_return(config_synced_folders) allow(subject).to receive(:synced_folders). with(machine, cached: true).and_return(cached_folders) allow(helper_class).to receive(:rsync_single).and_return(true) allow(Vagrant::Util::Busy).to receive(:busy).and_return(true) allow(Listen).to receive(:to).and_return(true) expect(machine.ui).to receive(:info). with("Not syncing /Not/The/Same/Path as it is not part of the current working directory.") expect(machine.ui).to receive(:info). with("Watching: /Users/brian/code/vagrant-sandbox") expect(machine.ui).to receive(:info). with("Watching: /Users/brian/code/relative-dir") expect(helper_class).to receive(:rsync_single) subject.execute() end end subject do described_class.new(argv, iso_env).tap do |s| allow(s).to receive(:synced_folders).and_return(synced_folders_empty) end end describe "#callback" do it "syncs modified folders to the proper path" do paths["/foo"] = [ { machine: machine_stub("m1"), opts: double("opts_m1") }, { machine: machine_stub("m2"), opts: double("opts_m2") }, ] paths["/bar"] = [ { machine: machine_stub("m3"), opts: double("opts_m3") }, ] paths["/foo"].each do |data| expect(helper_class).to receive(:rsync_single). with(data[:machine], data[:machine].ssh_info, data[:opts]). once end m = ["/foo/bar"] a = [] r = [] subject.callback(paths, m, a, r) end it "syncs added folders to the proper path" do paths["/foo"] = [ { machine: machine_stub("m1"), opts: double("opts_m1") }, { machine: machine_stub("m2"), opts: double("opts_m2") }, ] paths["/bar"] = [ { machine: machine_stub("m3"), opts: double("opts_m3") }, ] paths["/foo"].each do |data| expect(helper_class).to receive(:rsync_single). with(data[:machine], data[:machine].ssh_info, data[:opts]). once end m = [] a = ["/foo/bar"] r = [] subject.callback(paths, m, a, r) end it "syncs removed folders to the proper path" do paths["/foo"] = [ { machine: machine_stub("m1"), opts: double("opts_m1") }, { machine: machine_stub("m2"), opts: double("opts_m2") }, ] paths["/bar"] = [ { machine: machine_stub("m3"), opts: double("opts_m3") }, ] paths["/foo"].each do |data| expect(helper_class).to receive(:rsync_single). with(data[:machine], data[:machine].ssh_info, data[:opts]). once end m = [] a = [] r = ["/foo/bar"] subject.callback(paths, m, a, r) end it "doesn't fail if guest error occurs" do paths["/foo"] = [ { machine: machine_stub("m1"), opts: double("opts_m1") }, { machine: machine_stub("m2"), opts: double("opts_m2") }, ] paths["/bar"] = [ { machine: machine_stub("m3"), opts: double("opts_m3") }, ] paths["/foo"].each do |data| expect(helper_class).to receive(:rsync_single). with(data[:machine], data[:machine].ssh_info, data[:opts]). and_raise(Vagrant::Errors::MachineGuestNotReady) end m = [] a = [] r = ["/foo/bar"] expect { subject.callback(paths, m, a, r) }. to_not raise_error end it "doesn't sync machines with no ID" do paths["/foo"] = [ { machine: machine_stub("m1"), opts: double("opts_m1") }, ] paths["/foo"].each do |data| allow(data[:machine]).to receive(:id).and_return(nil) expect(helper_class).to_not receive(:rsync_single) end m = [] a = [] r = ["/foo/bar"] expect { subject.callback(paths, m, a, r) }. to_not raise_error end end end vagrant-2.0.2/test/unit/plugins/synced_folders/rsync/command/rsync_test.rb000066400000000000000000000035671323370221500271060ustar00rootroot00000000000000require_relative "../../../../base" require Vagrant.source_root.join("plugins/synced_folders/rsync/command/rsync") describe VagrantPlugins::SyncedFolderRSync::Command::Rsync do include_context "unit" let(:argv) { [] } 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(:communicator) { double("comm") } let(:synced_folders) { {} } let(:helper_class) { VagrantPlugins::SyncedFolderRSync::RsyncHelper } subject do described_class.new(argv, iso_env).tap do |s| allow(s).to receive(:synced_folders).and_return(synced_folders) end end before do iso_env.machine_names.each do |name| m = iso_env.machine(name, iso_env.default_provider) allow(m).to receive(:communicate).and_return(communicator) end end describe "#execute" do context "with a single machine" do let(:ssh_info) {{ private_key_path: [], }} let(:machine) { iso_env.machine(iso_env.machine_names[0], iso_env.default_provider) } before do allow(communicator).to receive(:ready?).and_return(true) allow(machine).to receive(:ssh_info).and_return(ssh_info) synced_folders[:rsync] = [ [:one, {}], [:two, {}], ] end it "doesn't sync if communicator isn't ready and exits with 1" do allow(communicator).to receive(:ready?).and_return(false) expect(helper_class).to receive(:rsync_single).never expect(subject.execute).to eql(1) end it "rsyncs each folder and exits successfully" do synced_folders[:rsync].each do |_, opts| expect(helper_class).to receive(:rsync_single). with(machine, ssh_info, opts). ordered end expect(subject.execute).to eql(0) end end end end vagrant-2.0.2/test/unit/plugins/synced_folders/rsync/helper_test.rb000066400000000000000000000223731323370221500256050ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/platform" require Vagrant.source_root.join("plugins/synced_folders/rsync/helper") describe VagrantPlugins::SyncedFolderRSync::RsyncHelper 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) } subject { described_class } before do allow(machine).to receive(:guest).and_return(guest) # Don't do all the crazy Cygwin stuff allow(Vagrant::Util::Platform).to receive(:cygwin_path) do |path, **opts| path end end describe "#exclude_to_regexp" do let(:path) { "/foo/bar" } it "converts a directory match" do expect(described_class.exclude_to_regexp(path, "foo/")). to eq(/^#{Regexp.escape(path)}\/.*foo\//) end it "converts the start anchor" do expect(described_class.exclude_to_regexp(path, "/foo")). to eq(/^\/foo\/bar\/foo/) end it "converts the **" do expect(described_class.exclude_to_regexp(path, "fo**o")). to eq(/^#{Regexp.escape(path)}\/.*fo.*o/) end it "converts the *" do expect(described_class.exclude_to_regexp(path, "fo*o")). to eq(/^#{Regexp.escape(path)}\/.*fo[^\/]*o/) end end describe "#rsync_single" do let(:result) { Vagrant::Util::Subprocess::Result.new(0, "", "") } let(:ssh_info) {{ private_key_path: [], }} let(:opts) {{ hostpath: "/foo", }} let(:ui) { machine.ui } before do allow(Vagrant::Util::Subprocess).to receive(:execute){ result } allow(guest).to receive(:capability?){ false } end it "doesn't raise an error if it succeeds" do subject.rsync_single(machine, ssh_info, opts) end it "doesn't call cygwin_path on non-Windows" do allow(Vagrant::Util::Platform).to receive(:windows?).and_return(false) expect(Vagrant::Util::Platform).not_to receive(:cygwin_path) subject.rsync_single(machine, ssh_info, opts) end it "calls cygwin_path on Windows" do allow(Vagrant::Util::Platform).to receive(:windows?).and_return(true) expect(Vagrant::Util::Platform).to receive(:cygwin_path).and_return("foo") expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args| expect(args[args.length - 3]).to eql("foo/") }.and_return(result) subject.rsync_single(machine, ssh_info, opts) end it "raises an error if the exit code is non-zero" do allow(Vagrant::Util::Subprocess).to receive(:execute) .and_return(Vagrant::Util::Subprocess::Result.new(1, "", "")) expect {subject.rsync_single(machine, ssh_info, opts) }. to raise_error(Vagrant::Errors::RSyncError) end context "host and guest paths" do it "syncs the hostpath to the guest path" do opts[:hostpath] = "/foo" opts[:guestpath] = "/bar" expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args| expected = Vagrant::Util::Platform.fs_real_path("/foo").to_s expect(args[args.length - 3]).to eql("#{expected}/") expect(args[args.length - 2]).to include("/bar") }.and_return(result) subject.rsync_single(machine, ssh_info, opts) end it "expands the hostpath relative to the root path" do opts[:hostpath] = "foo" opts[:guestpath] = "/bar" hostpath_expanded = File.expand_path(opts[:hostpath], machine.env.root_path) expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args| expect(args[args.length - 3]).to eql("#{hostpath_expanded}/") expect(args[args.length - 2]).to include("/bar") }.and_return(result) subject.rsync_single(machine, ssh_info, opts) end end it "executes within the root path" do expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args| expect(args.last).to be_kind_of(Hash) opts = args.last expect(opts[:workdir]).to eql(machine.env.root_path.to_s) }.and_return(result) subject.rsync_single(machine, ssh_info, opts) end it "executes the rsync_pre capability first if it exists" do expect(guest).to receive(:capability?).with(:rsync_pre).and_return(true) expect(guest).to receive(:capability).with(:rsync_pre, opts).ordered expect(Vagrant::Util::Subprocess).to receive(:execute).ordered.and_return(result) subject.rsync_single(machine, ssh_info, opts) end it "executes the rsync_post capability after if it exists" do expect(guest).to receive(:capability?).with(:rsync_post).and_return(true) expect(Vagrant::Util::Subprocess).to receive(:execute).ordered.and_return(result) expect(guest).to receive(:capability).with(:rsync_post, opts).ordered subject.rsync_single(machine, ssh_info, opts) end context "excluding files" do it "excludes files if given as a string" do opts[:exclude] = "foo" expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args| index = args.find_index("foo") expect(index).to be > 0 expect(args[index-1]).to eql("--exclude") }.and_return(result) subject.rsync_single(machine, ssh_info, opts) end it "excludes multiple files" do opts[:exclude] = ["foo", "bar"] expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args| index = args.find_index("foo") expect(index).to be > 0 expect(args[index-1]).to eql("--exclude") index = args.find_index("bar") expect(index).to be > 0 expect(args[index-1]).to eql("--exclude") }.and_return(result) subject.rsync_single(machine, ssh_info, opts) end end context "custom arguments" do it "uses the default arguments if not given" do expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args| expect(args[1]).to eq("--verbose") expect(args[2]).to eq("--archive") expect(args[3]).to eq("--delete") expected = Vagrant::Util::Platform.fs_real_path("/foo").to_s expect(args[args.length - 3]).to eql("#{expected}/") }.and_return(result) subject.rsync_single(machine, ssh_info, opts) end it "uses the custom arguments if given" do opts[:args] = ["--verbose", "-z"] expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args| expect(args[1]).to eq("--verbose") expect(args[2]).to eq("-z") expected = Vagrant::Util::Platform.fs_real_path("/foo").to_s expect(args[args.length - 3]).to eql("#{expected}/") }.and_return(result) subject.rsync_single(machine, ssh_info, opts) end end end describe "#rsync_single with custom ssh_info" do let(:result) { Vagrant::Util::Subprocess::Result.new(0, "", "") } let(:ssh_info) {{ :private_key_path => ['/path/to/key'], :keys_only => true, :verify_host_key => false, }} let(:opts) {{ hostpath: "/foo", }} let(:ui) { machine.ui } before do allow(Vagrant::Util::Subprocess).to receive(:execute){ result } allow(guest).to receive(:capability?){ false } end context "with an IPv6 address" do before { ssh_info[:host] = "fe00::0" } it "formats the address correctly" do expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args, "@[#{ssh_info[:host]}]:''", instance_of(Hash)) subject.rsync_single(machine, ssh_info, opts) end end context "with an IPv4 address" do before { ssh_info[:host] = "127.0.0.1" } it "formats the address correctly" do expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args, "@#{ssh_info[:host]}:''", instance_of(Hash)) subject.rsync_single(machine, ssh_info, opts) end end it "includes IdentitiesOnly, StrictHostKeyChecking, and UserKnownHostsFile with defaults" do expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args| expect(args[9]).to include('IdentitiesOnly') expect(args[9]).to include('StrictHostKeyChecking') expect(args[9]).to include('UserKnownHostsFile') expect(args[9]).to include("-i '/path/to/key'") }.and_return(result) subject.rsync_single(machine, ssh_info, opts) end it "omits IdentitiesOnly with keys_only = false" do ssh_info[:keys_only] = false expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args| expect(args[9]).not_to include('IdentitiesOnly') result end subject.rsync_single(machine, ssh_info, opts) end it "omits StrictHostKeyChecking and UserKnownHostsFile with paranoid = true" do ssh_info[:keys_only] = false expect(Vagrant::Util::Subprocess).to receive(:execute) do |*args| expect(args[9]).not_to include('StrictHostKeyChecking ') expect(args[9]).not_to include('UserKnownHostsFile ') result end subject.rsync_single(machine, ssh_info, opts) end end end vagrant-2.0.2/test/unit/plugins/synced_folders/rsync/synced_folder_test.rb000066400000000000000000000055701323370221500271460ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/synced_folders/rsync/synced_folder") describe VagrantPlugins::SyncedFolderRSync::SyncedFolder 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(:host) { double("host") } let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } let(:helper_class) { VagrantPlugins::SyncedFolderRSync::RsyncHelper } before do allow(machine.env).to receive(:host).and_return(host) allow(machine).to receive(:guest).and_return(guest) end describe "#usable?" do it "is usable if rsync can be found" do expect(Vagrant::Util::Which).to receive(:which).with("rsync").and_return(true) expect(subject.usable?(machine)).to be(true) end it "is not usable if rsync cant be found" do expect(Vagrant::Util::Which).to receive(:which).with("rsync").and_return(false) expect(subject.usable?(machine)).to be(false) end it "raises an exception if asked to" do expect(Vagrant::Util::Which).to receive(:which).with("rsync").and_return(false) expect { subject.usable?(machine, true) }. to raise_error(Vagrant::Errors::RSyncNotFound) end end describe "#enable" do let(:ssh_info) {{ private_key_path: [], }} before do allow(machine).to receive(:ssh_info).and_return(ssh_info) allow(guest).to receive(:capability?).with(:rsync_installed) end it "rsyncs each folder" do folders = [ [:one, {}], [:two, {}], ] folders.each do |_, opts| expect(helper_class).to receive(:rsync_single). with(machine, ssh_info, opts). ordered end subject.enable(machine, folders, {}) end it "installs rsync if capable" do folders = [ [:foo, {}] ] allow(helper_class).to receive(:rsync_single) allow(guest).to receive(:capability?).with(:rsync_installed).and_return(true) allow(guest).to receive(:capability?).with(:rsync_install).and_return(true) expect(guest).to receive(:capability).with(:rsync_installed).and_return(false) expect(guest).to receive(:capability).with(:rsync_install) subject.enable(machine, folders, {}) end it "errors if rsync not installable" do folders = [ [:foo, {}] ] allow(helper_class).to receive(:rsync_single) allow(guest).to receive(:capability?).with(:rsync_installed).and_return(true) allow(guest).to receive(:capability?).with(:rsync_install).and_return(false) expect(guest).to receive(:capability).with(:rsync_installed).and_return(false) expect { subject.enable(machine, folders, {}) }. to raise_error(Vagrant::Errors::RSyncNotInstalledInGuest) end end end vagrant-2.0.2/test/unit/plugins/synced_folders/smb/000077500000000000000000000000001323370221500223565ustar00rootroot00000000000000vagrant-2.0.2/test/unit/plugins/synced_folders/smb/synced_folder_test.rb000066400000000000000000000226621323370221500265720ustar00rootroot00000000000000require_relative "../../../base" require Vagrant.source_root.join("plugins/synced_folders/smb/synced_folder") describe VagrantPlugins::SyncedFolderSMB::SyncedFolder do include_context "unit" let(:iso_env) do env = isolated_environment env.vagrantfile("") env.create_vagrant_env end let(:guest){ double("guest") } let(:host){ double("host") } let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } let(:host_caps){ [] } let(:guest_caps){ [] } let(:folders){ {"/first/path" => {}, "/second/path" => {}} } let(:options){ {} } before do allow(machine.env).to receive(:host).and_return(host) allow(machine).to receive(:guest).and_return(guest) allow(machine).to receive(:ssh_info).and_return(username: 'sshuser') allow(guest).to receive(:name).and_return("guest_name") allow(host).to receive(:capability?).and_return(false) host_caps.each do |cap| allow(host).to receive(:capability?).with(cap).and_return(true) allow(host).to receive(:capability).with(cap, any_args).and_return(true) end allow(guest).to receive(:capability?).and_return(false) guest_caps.each do |cap| allow(guest).to receive(:capability?).with(cap).and_return(true) allow(guest).to receive(:capability).with(cap, any_args).and_return(true) end end describe ".usable?" do context "without supporting capabilities" do it "is not usable" do expect(subject.usable?(machine)).to be(false) end it "raises exception when raise_error enabled" do expect{subject.usable?(machine, true)}.to raise_error( VagrantPlugins::SyncedFolderSMB::Errors::SMBNotSupported) end end context "with smb not installed" do let(:host_caps){ [:smb_installed] } it "is not usable" do expect(host).to receive(:capability).with(:smb_installed).and_return(false) expect(subject.usable?(machine)).to be(false) end end context "with smb installed" do let(:host_caps){ [:smb_installed] } it "is usable" do expect(subject.usable?(machine)).to be(true) end end end describe ".prepare" do let(:host_caps){ [:smb_start, :smb_prepare] } context "without credentials provided" do before do expect(machine.env.ui).to receive(:ask).with(/name/, any_args).and_return('username').at_least(1) expect(machine.env.ui).to receive(:ask).with(/word/, any_args).and_return('password').at_least(1) end it "should prompt for credentials" do subject.prepare(machine, folders, options) end it "should set credential information into all folder options" do subject.prepare(machine, folders, options) expect(folders['/first/path'][:smb_username]).to eq('username') expect(folders['/first/path'][:smb_password]).to eq('password') expect(folders['/second/path'][:smb_username]).to eq('username') expect(folders['/second/path'][:smb_password]).to eq('password') end it "should start the SMB service if capability is available" do expect(host).to receive(:capability).with(:smb_start, any_args) subject.prepare(machine, folders, options) end context "with host smb_validate_password capability" do let(:host_caps){ [:smb_start, :smb_prepare, :smb_validate_password] } it "should validate the password" do expect(host).to receive(:capability).with(:smb_validate_password, 'username', 'password').and_return(true) subject.prepare(machine, folders, options) end it "should retry when validation fails" do expect(host).to receive(:capability).with(:smb_validate_password, 'username', 'password').and_return(false) expect(host).to receive(:capability).with(:smb_validate_password, 'username', 'password').and_return(true) subject.prepare(machine, folders, options) end it "should raise an error if it exceeds the maximum number of retries" do expect(host).to receive(:capability).with(:smb_validate_password, 'username', 'password').and_return(false). exactly(VagrantPlugins::SyncedFolderSMB::SyncedFolder::CREDENTIAL_RETRY_MAX).times expect{ subject.prepare(machine, folders, options) }.to raise_error(VagrantPlugins::SyncedFolderSMB::Errors::CredentialsRequestError) end end end context "with credentials provided" do context "in single share entry" do let(:folders){ {'/first/path' => {}, '/second/path' => {smb_username: 'smbuser', smb_password: 'smbpass'}} } it "should not prompt for credentials" do expect(machine.env.ui).not_to receive(:ask) subject.prepare(machine, folders, options) end it "should add existing credentials to folder options without" do subject.prepare(machine, folders, options) expect(folders['/first/path'][:smb_username]).to eq('smbuser') expect(folders['/first/path'][:smb_password]).to eq('smbpass') end end context "in both entries" do let(:folders){ {'/first/path' => {smb_username: 'user', smb_password: 'pass'}, '/second/path' => {smb_username: 'smbuser', smb_password: 'smbpass'}} } it "should not modify existing credentials" do subject.prepare(machine, folders, options) expect(folders['/first/path'][:smb_username]).to eq('user') expect(folders['/first/path'][:smb_password]).to eq('pass') expect(folders['/second/path'][:smb_username]).to eq('smbuser') expect(folders['/second/path'][:smb_password]).to eq('smbpass') end it "should register passwords with scrubber" do expect(Vagrant::Util::CredentialScrubber).to receive(:sensitive).with('pass') expect(Vagrant::Util::CredentialScrubber).to receive(:sensitive).with('smbpass') subject.prepare(machine, folders, options) end end end end describe ".enable" do it "fails when guest does not support capability" do expect{ subject.enable(machine, folders, options) }.to raise_error(Vagrant::Errors::GuestCapabilityNotFound) end context "with guest capability supported" do let(:guest_caps){ [:mount_smb_shared_folder, :choose_addressable_ip_addr] } let(:host_caps){ [:configured_ip_addresses] } it "should attempt to install smb on guest" do expect(guest).to receive(:capability?).with(:smb_install).and_return(true) expect(guest).to receive(:capability).with(:smb_install, any_args) subject.enable(machine, folders, options) end it "should request host IP addresses" do expect(host).to receive(:capability).with(:configured_ip_addresses) subject.enable(machine, folders, options) end it "should determine guest accessible address" do expect(guest).to receive(:capability).with(:choose_addressable_ip_addr, any_args) subject.enable(machine, folders, options) end it "should error if no guest accessible address is available" do expect(guest).to receive(:capability).with(:choose_addressable_ip_addr, any_args).and_return(nil) expect{ subject.enable(machine, folders, options) }.to raise_error( VagrantPlugins::SyncedFolderSMB::Errors::NoHostIPAddr) end it "should default owner and group to ssh username" do subject.enable(machine, folders, options) expect(folders["/first/path"][:owner]).to eq("sshuser") expect(folders["/first/path"][:group]).to eq("sshuser") expect(folders["/second/path"][:owner]).to eq("sshuser") expect(folders["/second/path"][:group]).to eq("sshuser") end it "should set the host address in folder options" do expect(guest).to receive(:capability).with(:choose_addressable_ip_addr, any_args).and_return("ADDR") subject.enable(machine, folders, options) expect(folders["/first/path"][:smb_host]).to eq("ADDR") expect(folders["/second/path"][:smb_host]).to eq("ADDR") end context "with smb_host option set" do let(:folders){ {"/first/path" => {smb_host: "ADDR"}, "/second/path" => {}} } it "should not update the value" do expect(guest).to receive(:capability).with(:choose_addressable_ip_addr, any_args).and_return("OTHER") subject.enable(machine, folders, options) expect(folders["/first/path"][:smb_host]).to eq("ADDR") expect(folders["/second/path"][:smb_host]).to eq("OTHER") end end context "with owner and group set" do let(:folders){ {"/first/path" => {owner: "smbowner"}, "/second/path" => {group: "smbgroup"}} } it "should not update set owner or group" do subject.enable(machine, folders, options) expect(folders["/first/path"][:owner]).to eq("smbowner") expect(folders["/first/path"][:group]).to eq("sshuser") expect(folders["/second/path"][:owner]).to eq("sshuser") expect(folders["/second/path"][:group]).to eq("smbgroup") end end end end describe ".cleanup" do context "without supporting capability" do it "does nothing" do subject.cleanup(machine, options) end end context "with supporting capability" do let(:host_caps){ [:smb_cleanup] } it "runs cleanup" do expect(host).to receive(:capability).with(:smb_cleanup, any_args) subject.cleanup(machine, options) end end end end vagrant-2.0.2/test/unit/support/000077500000000000000000000000001323370221500166255ustar00rootroot00000000000000vagrant-2.0.2/test/unit/support/dummy_communicator.rb000066400000000000000000000036751323370221500231000ustar00rootroot00000000000000module VagrantTests module DummyCommunicator class Communicator < Vagrant.plugin("2", :communicator) def ready? true end attr_reader :known_commands def initialize(machine) @known_commands = Hash.new do |hash, key| hash[key] = { expected: 0, received: 0, response: nil } end end def expected_commands known_commands.select do |command, info| info[:expected] > 0 end end def received_commands known_commands.select do |command, info| info[:received] > 0 end.keys end def stub_command(command, response) known_commands[command][:response] = response end def expect_command(command) known_commands[command][:expected] += 1 end def received_summary received_commands.map { |cmd| " - #{cmd}" }.unshift('received:').join("\n") end def verify_expectations! expected_commands.each do |command, info| if info[:expected] != info[:received] fail([ "expected to receive '#{command}' #{info[:expected]} times", "got #{info[:received]} times instead", received_summary ].join("\n")) end end end def execute(command, opts=nil) known = known_commands[command] known[:received] += 1 response = known[:response] return unless response if block_given? [:stdout, :stderr].each do |type| Array(response[type]).each do |line| yield type, line end end end if response[:raise] raise response[:raise] end response[:exit_code] end def sudo(command, opts=nil, &block) execute(command, opts, &block) end def test(command, opts=nil) execute(command, opts) == 0 end end end end vagrant-2.0.2/test/unit/support/dummy_provider.rb000066400000000000000000000017011323370221500222160ustar00rootroot00000000000000module VagrantTests class DummyProviderPlugin < Vagrant.plugin("2") name "Dummy Provider" description <<-EOF This creates a provider named "dummy" which does nothing, so that the unit tests aren't reliant on VirtualBox (or any other real provider for that matter). EOF provider(:dummy) { DummyProvider } end class DummyProvider < Vagrant.plugin("2", :provider) def initialize(machine) @machine = machine end def state=(id) state_file.open("w+") do |f| f.write(id.to_s) end end def state if !state_file.file? new_state = @machine.id new_state = Vagrant::MachineState::NOT_CREATED_ID if !new_state self.state = new_state end state_id = state_file.read.to_sym Vagrant::MachineState.new(state_id, state_id.to_s, state_id.to_s) end protected def state_file @machine.data_dir.join("dummy_state") end end end vagrant-2.0.2/test/unit/support/isolated_environment.rb000066400000000000000000000135241323370221500234070ustar00rootroot00000000000000require "fileutils" require "pathname" require "tempfile" require "tmpdir" require "json" require "log4r" require "vagrant/util/platform" require "vagrant/util/subprocess" require "support/isolated_environment" module Unit class IsolatedEnvironment < ::IsolatedEnvironment def create_vagrant_env(options=nil) options = { cwd: @workdir, home_path: @homedir }.merge(options || {}) Vagrant::Environment.new(options) end # This creates a file in the isolated environment. By default this file # will be created in the working directory of the isolated environment. def file(name, contents) @workdir.join(name).open("w+") do |f| f.write(contents) end end def vagrantfile(contents, root=nil) root ||= @workdir root.join("Vagrantfile").open("w+") do |f| f.write(contents) end end def box(name, vagrantfile_contents="") # Create the box directory box_dir = boxes_dir.join(name) box_dir.mkpath # Create the "box.ovf" file because that is how Vagrant heuristically # determines a box is a V1 box. box_dir.join("box.ovf").open("w") { |f| f.write("") } # Populate the vagrantfile vagrantfile(vagrantfile_contents, box_dir) # Return the directory box_dir end # Create an alias because "box" makes a V1 box, so "box1" alias :box1 :box # Creates a fake box to exist in this environment. # # @param [String] name Name of the box # @param [Symbol] provider Provider the box was built for. # @return [Pathname] Path to the box directory. def box2(name, provider, options=nil) # Default options options = { vagrantfile: "" }.merge(options || {}) # Make the box directory box_dir = boxes_dir.join(name, provider.to_s) box_dir.mkpath # Create a metadata.json file box_metadata_file = box_dir.join("metadata.json") box_metadata_file.open("w") do |f| f.write(JSON.generate({ provider: provider.to_s })) end # Create a Vagrantfile box_vagrantfile = box_dir.join("Vagrantfile") box_vagrantfile.open("w") do |f| f.write(options[:vagrantfile]) end # Return the box directory box_dir end # Creates a fake box to exist in this environment according # to the "gen-3" box format. # # @param [String] name # @param [String] version # @param [String] provider # @return [Pathname] def box3(name, version, provider, **opts) # Create the directory for the box box_dir = boxes_dir.join(name, version, provider.to_s) box_dir.mkpath # Create the metadata.json for it box_metadata_file = box_dir.join("metadata.json") box_metadata_file.open("w") do |f| f.write(JSON.generate({ provider: provider.to_s })) end # Create a Vagrantfile if opts[:vagrantfile] box_vagrantfile = box_dir.join("Vagrantfile") box_vagrantfile.open("w") do |f| f.write(opts[:vagrantfile]) end end # Create the metadata URL if opts[:metadata_url] boxes_dir.join(name, "metadata_url").open("w") do |f| f.write(opts[:metadata_url]) end end box_dir end # This creates a "box" file that is a valid V1 box. # # @return [Pathname] Path to the newly created box. def box1_file # Create a temporary directory to store our data we will tar up td_source = Dir.mktmpdir("vagrant-box1-source") td_dest = Dir.mktmpdir("vagrant-box-1-dest") # Store the temporary directory so it is not deleted until # this instance is garbage collected. @_box2_file_temp ||= [] @_box2_file_temp << td_dest # The source as a Pathname, which is easier to work with source = Pathname.new(td_source) # The destination file result = Pathname.new(td_dest).join("temporary.box") # Put a "box.ovf" in there. source.join("box.ovf").open("w") do |f| f.write("FOO!") end Dir.chdir(source) do # Find all the files in our current directory and tar it up! files = Dir.glob(File.join(".", "**", "*")) # Package! Vagrant::Util::Subprocess.execute("bsdtar", "-czf", result.to_s, *files) end # Resulting box result end # This creates a "box" file with the given provider. # # @param [Symbol] provider Provider for the box. # @return [Pathname] Path to the newly created box. def box2_file(provider, options=nil) options ||= {} # This is the metadata we want to store in our file metadata = { "type" => "v2_box", "provider" => provider }.merge(options[:metadata] || {}) # Create a temporary directory to store our data we will tar up td_source = Dir.mktmpdir("vagrant-box-2-source") td_dest = Dir.mktmpdir("vagrant-box-2-dest") # Store the temporary directory so it is not deleted until # this instance is garbage collected. @_box2_file_temp ||= [] @_box2_file_temp << td_dest # The source as a Pathname, which is easier to work with source = Pathname.new(td_source) # The destination file result = Pathname.new(td_dest).join("temporary.box") # Put the metadata.json in here. source.join("metadata.json").open("w") do |f| f.write(JSON.generate(metadata)) end Dir.chdir(source) do # Find all the files in our current directory and tar it up! files = Dir.glob(File.join(".", "**", "*")) # Package! Vagrant::Util::Subprocess.execute("bsdtar", "-czf", result.to_s, *files) end # Resulting box result end def boxes_dir dir = @homedir.join("boxes") dir.mkpath dir end end end vagrant-2.0.2/test/unit/support/shared/000077500000000000000000000000001323370221500200735ustar00rootroot00000000000000vagrant-2.0.2/test/unit/support/shared/action_synced_folders_context.rb000066400000000000000000000006031323370221500265230ustar00rootroot00000000000000shared_context "synced folder actions" do # This creates a synced folder implementation. def impl(usable, name) Class.new(Vagrant.plugin("2", :synced_folder)) do define_method(:name) do name end define_method(:usable?) do |machine, raise_error=false| raise "#{name}: usable" if raise_error && !usable usable end end end end vagrant-2.0.2/test/unit/support/shared/base_context.rb000066400000000000000000000075601323370221500231060ustar00rootroot00000000000000require "tempfile" require "tmpdir" require "vagrant/util/platform" require "unit/support/isolated_environment" shared_context "unit" do before(:each) do # State to store the list of registered plugins that we have to # unregister later. @_plugins = [] # Create a thing to store our temporary files so that they aren't # unlinked right away. @_temp_files = [] # Roughly simulate the embedded Bundler availability $vagrant_bundler_runtime = Object.new end after(:each) do # Unregister each of the plugins we have may have temporarily # registered for the duration of this test. @_plugins.each do |plugin| Vagrant.plugin("1").manager.unregister(plugin) Vagrant.plugin("2").manager.unregister(plugin) end end # This creates an isolated environment so that Vagrant doesn't # muck around with your real system during unit tests. # # The returned isolated environment has a variety of helper # methods on it to easily create files, Vagrantfiles, boxes, # etc. def isolated_environment env = Unit::IsolatedEnvironment.new yield env if block_given? env end # This registers a Vagrant plugin for the duration of a single test. # This will yield a new plugin class that you can then call the # public plugin methods on. # # @yield [plugin] Yields the plugin class for you to call the public # API that you need to. def register_plugin(version=nil) version ||= Vagrant::Config::CURRENT_VERSION plugin = Class.new(Vagrant.plugin(version)) plugin.name("Test Plugin #{plugin.inspect}") yield plugin if block_given? @_plugins << plugin plugin end # This helper creates a temporary file and returns a Pathname # object pointed to it. # # @return [Pathname] def temporary_file(contents=nil) dir = temporary_dir f = dir.join("tempfile") contents ||= "" f.open("w") do |f| f.write(contents) f.flush end return Pathname.new(Vagrant::Util::Platform.fs_real_path(f.to_s)) end # This creates a temporary directory and returns a {Pathname} # pointing to it. If a block is given, the pathname is yielded and the # temporary directory is removed at the end of the block. # # @return [Pathname] def temporary_dir # Create a temporary directory and append it to the instance # variabe so that it isn't garbage collected and deleted d = Dir.mktmpdir("vagrant-temporary-dir") @_temp_files ||= [] @_temp_files << d # Return the pathname result = Pathname.new(Vagrant::Util::Platform.fs_real_path(d)) if block_given? begin yield result ensure FileUtils.rm_rf(result) end end return result end # Stub the given environment in ENV, without actually touching ENV. Keys and # values are converted to strings because that's how the real ENV works. def stub_env(hash) allow(ENV).to receive(:[]).and_call_original hash.each do |key, value| allow(ENV).to receive(:[]) .with(key.to_s) .and_return(value.to_s) end end # This helper provides temporary environmental variable changes. def with_temp_env(environment) # Build up the new environment, preserving the old values so we # can replace them back in later. old_env = {} environment.each do |key, value| key = key.to_s old_env[key] = ENV[key] ENV[key] = value end # Call the block, returning its return value return yield ensure # Reset the environment no matter what old_env.each do |key, value| ENV[key] = value end end # This helper provides a randomly available port(s) for each argument to the # block. def with_random_port(&block) ports = [] block.arity.times do server = TCPServer.new('127.0.0.1', 0) ports << server.addr[1] server.close end block.call(*ports) end end vagrant-2.0.2/test/unit/support/shared/capability_helpers_context.rb000066400000000000000000000010351323370221500260260ustar00rootroot00000000000000shared_context "capability_helpers" do def detect_class(result) Class.new do define_method(:detect?) do |*args| result end end end def provider_usable_class(result) Class.new do define_singleton_method(:usable?) do |*args| result end end end def cap_instance(name, options=nil) options ||= {} Class.new do if !options[:corrupt] define_method(name) do |*args| raise "cap: #{name} #{args.inspect}" end end end.new end end vagrant-2.0.2/test/unit/support/shared/plugin_command_context.rb000066400000000000000000000004501323370221500251570ustar00rootroot00000000000000shared_context "command plugin helpers" do def command_lambda(name, result, **opts) lambda do Class.new(Vagrant.plugin("2", "command")) do define_method(:execute) do raise opts[:exception] if opts[:exception] result end end end end end vagrant-2.0.2/test/unit/support/shared/virtualbox_context.rb000066400000000000000000000027601323370221500243700ustar00rootroot00000000000000shared_context "virtualbox" do include_context "unit" let(:vbox_context) { true } let(:uuid) { "1234-abcd-5678-efgh" } let(:vbox_version) { "4.3.4" } let(:subprocess) { double("Vagrant::Util::Subprocess") } # this is a helper that returns a duck type suitable from a system command # execution; allows setting exit_code, stdout, and stderr in stubs. def subprocess_result(options={}) defaults = {exit_code: 0, stdout: "", stderr: ""} double("subprocess_result", defaults.merge(options)) end before do # we don't want unit tests to ever run commands on the system; so we wire # in a double to ensure any unexpected messages raise exceptions stub_const("Vagrant::Util::Subprocess", subprocess) # drivers will blow up on instantiation if they cannot determine the # virtualbox version, so wire this stub in automatically allow(subprocess).to receive(:execute). with("VBoxManage", "--version", an_instance_of(Hash)). and_return(subprocess_result(stdout: vbox_version)) # drivers also call vm_exists? during init; allow(subprocess).to receive(:execute). with("VBoxManage", "showvminfo", kind_of(String), kind_of(Hash)). and_return(subprocess_result(exit_code: 0)) end around do |example| # On Windows, we don't want to accidentally call the actual VirtualBox with_temp_env("VBOX_INSTALL_PATH" => nil) do example.run end end end vagrant-2.0.2/test/unit/templates/000077500000000000000000000000001323370221500171075ustar00rootroot00000000000000vagrant-2.0.2/test/unit/templates/commands/000077500000000000000000000000001323370221500207105ustar00rootroot00000000000000vagrant-2.0.2/test/unit/templates/commands/init/000077500000000000000000000000001323370221500216535ustar00rootroot00000000000000vagrant-2.0.2/test/unit/templates/commands/init/Vagrantfile.erb000066400000000000000000000012221323370221500246040ustar00rootroot00000000000000# -*- mode: ruby -*- # vi: set ft=ruby : # All Vagrant configuration is done below. The "2" in Vagrant.configure # configures the configuration version (we support older styles for # backwards compatibility). Please don't change it unless you know what # you're doing. Vagrant.configure("2") do |config| config.vm.hostname = "vagrant.dev" config.vm.box = "<%= box_name %>" <% if box_version -%> config.vm.box_version = "<%= box_version %>" <% end -%> <% if box_url -%> # The url from where the 'config.vm.box' box will be fetched if it # doesn't already exist on the user's system. config.vm.box_url = "<%= box_url %>" <% end -%> end vagrant-2.0.2/test/unit/templates/guests/000077500000000000000000000000001323370221500204215ustar00rootroot00000000000000vagrant-2.0.2/test/unit/templates/guests/arch/000077500000000000000000000000001323370221500213365ustar00rootroot00000000000000vagrant-2.0.2/test/unit/templates/guests/arch/network_dhcp_test.rb000066400000000000000000000007511323370221500254140ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/template_renderer" describe "templates/guests/arch/network_dhcp" do let(:template) { "guests/arch/network_dhcp" } it "renders the template" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "eth1", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") Description='A basic dhcp ethernet connection' Interface=eth1 Connection=ethernet IP=dhcp EOH end end vagrant-2.0.2/test/unit/templates/guests/arch/network_static_test.rb000066400000000000000000000017711323370221500257700ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/template_renderer" describe "templates/guests/arch/network_static" do let(:template) { "guests/arch/network_static" } it "renders the template" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "eth1", ip: "1.1.1.1", netmask: "24", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") Connection=ethernet Description='A basic static ethernet connection' Interface=eth1 IP=static Address=('1.1.1.1/24') EOH end it "includes the gateway" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "eth1", ip: "1.1.1.1", gateway: "1.2.3.4", netmask: "24", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") Connection=ethernet Description='A basic static ethernet connection' Interface=eth1 IP=static Address=('1.1.1.1/24') Gateway='1.2.3.4' EOH end end vagrant-2.0.2/test/unit/templates/guests/debian/000077500000000000000000000000001323370221500216435ustar00rootroot00000000000000vagrant-2.0.2/test/unit/templates/guests/debian/network_dhcp_test.rb000066400000000000000000000024451323370221500257230ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/template_renderer" describe "templates/guests/debian/network_dhcp" do let(:template) { "guests/debian/network_dhcp" } it "renders the template" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "eth1", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. auto eth1 iface eth1 inet dhcp post-up route del default dev $IFACE || true #VAGRANT-END EOH end context "when use_dhcp_assigned_default_route is set" do it "renders the template" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "eth1", root_device: "eth0", use_dhcp_assigned_default_route: true, }) expect(result).to eq <<-EOH.gsub(/^ {8}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. auto eth1 iface eth1 inet dhcp # We need to disable eth0, see GH-2648 post-up route del default dev eth0 || true post-up dhclient $IFACE pre-down route add default dev eth0 #VAGRANT-END EOH end end end vagrant-2.0.2/test/unit/templates/guests/debian/network_static_test.rb000066400000000000000000000022631323370221500262720ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/template_renderer" describe "templates/guests/debian/network_static" do let(:template) { "guests/debian/network_static" } it "renders the template" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "eth1", ip: "1.1.1.1", netmask: "255.255.0.0", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. auto eth1 iface eth1 inet static address 1.1.1.1 netmask 255.255.0.0 #VAGRANT-END EOH end it "includes the gateway" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "eth1", ip: "1.1.1.1", netmask: "255.255.0.0", gateway: "1.2.3.4", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. auto eth1 iface eth1 inet static address 1.1.1.1 netmask 255.255.0.0 gateway 1.2.3.4 #VAGRANT-END EOH end end vagrant-2.0.2/test/unit/templates/guests/freebsd/000077500000000000000000000000001323370221500220335ustar00rootroot00000000000000vagrant-2.0.2/test/unit/templates/guests/freebsd/network_dhcp_test.rb000066400000000000000000000007411323370221500261100ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/template_renderer" describe "templates/guests/freebsd/network_dhcp" do let(:template) { "guests/freebsd/network_dhcp" } it "renders the template" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "eth1", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN ifconfig_eth1="DHCP" synchronous_dhclient="YES" #VAGRANT-END EOH end end vagrant-2.0.2/test/unit/templates/guests/freebsd/network_static_test.rb000066400000000000000000000016601323370221500264620ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/template_renderer" describe "templates/guests/freebsd/network_static" do let(:template) { "guests/freebsd/network_static" } it "renders the template" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "eth1", ip: "1.1.1.1", netmask: "255.255.0.0", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN ifconfig_eth1="inet 1.1.1.1 netmask 255.255.0.0" #VAGRANT-END EOH end it "includes the gateway" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "eth1", ip: "1.1.1.1", netmask: "255.255.0.0", gateway: "1.2.3.4", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN ifconfig_eth1="inet 1.1.1.1 netmask 255.255.0.0" default_router="1.2.3.4" #VAGRANT-END EOH end end vagrant-2.0.2/test/unit/templates/guests/funtoo/000077500000000000000000000000001323370221500217335ustar00rootroot00000000000000vagrant-2.0.2/test/unit/templates/guests/funtoo/network_dhcp_test.rb000066400000000000000000000010121323370221500260000ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/template_renderer" describe "templates/guests/funtoo/network_dhcp" do let(:template) { "guests/funtoo/network_dhcp" } it "renders the template" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "en0", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. template='dhcp' #VAGRANT-END EOH end end vagrant-2.0.2/test/unit/templates/guests/funtoo/network_static_test.rb000066400000000000000000000077511323370221500263710ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/template_renderer" describe "templates/guests/funtoo/network_static" do let(:template) { "guests/funtoo/network_static" } it "renders the template" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "en0", ip: "1.1.1.1", netmask: "255.255.0.0", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. template='interface' ipaddr='1.1.1.1/255.255.0.0' #VAGRANT-END EOH end it "includes the gateway" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "en0", ip: "1.1.1.1", netmask: "255.255.0.0", gateway: "1.2.3.4", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. template='interface' ipaddr='1.1.1.1/255.255.0.0' gateway='1.2.3.4' #VAGRANT-END EOH end it "includes the nameservers" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "en0", ip: "1.1.1.1", netmask: "255.255.0.0", nameservers: "ns1.company.com", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. template='interface' ipaddr='1.1.1.1/255.255.0.0' nameservers='ns1.company.com' #VAGRANT-END EOH end it "includes the domain" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "en0", ip: "1.1.1.1", netmask: "255.255.0.0", domain: "company.com", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. template='interface' ipaddr='1.1.1.1/255.255.0.0' domain='company.com' #VAGRANT-END EOH end it "includes the route" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "en0", ip: "1.1.1.1", netmask: "255.255.0.0", route: "5.6.7.8", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. template='interface' ipaddr='1.1.1.1/255.255.0.0' route='5.6.7.8' #VAGRANT-END EOH end it "includes the gateway6" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "en0", ip: "1.1.1.1", netmask: "255.255.0.0", gateway6: "aaaa:0000", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. template='interface' ipaddr='1.1.1.1/255.255.0.0' gateway6='aaaa:0000' #VAGRANT-END EOH end it "includes the route6" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "en0", ip: "1.1.1.1", netmask: "255.255.0.0", route6: "bbbb:1111", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. template='interface' ipaddr='1.1.1.1/255.255.0.0' route6='bbbb:1111' #VAGRANT-END EOH end it "includes the mtu" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "en0", ip: "1.1.1.1", netmask: "255.255.0.0", mtu: "1", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. template='interface' ipaddr='1.1.1.1/255.255.0.0' mtu='1' #VAGRANT-END EOH end end vagrant-2.0.2/test/unit/templates/guests/gentoo/000077500000000000000000000000001323370221500217145ustar00rootroot00000000000000vagrant-2.0.2/test/unit/templates/guests/gentoo/network_dhcp_test.rb000066400000000000000000000010141323370221500257630ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/template_renderer" describe "templates/guests/gentoo/network_dhcp" do let(:template) { "guests/gentoo/network_dhcp" } it "renders the template" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "en0", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. config_en0="dhcp" #VAGRANT-END EOH end end vagrant-2.0.2/test/unit/templates/guests/gentoo/network_static_test.rb000066400000000000000000000021021323370221500263330ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/template_renderer" describe "templates/guests/gentoo/network_static" do let(:template) { "guests/gentoo/network_static" } it "renders the template" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "en0", ip: "1.1.1.1", netmask: "255.255.0.0", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. config_en0=("1.1.1.1 netmask 255.255.0.0") #VAGRANT-END EOH end it "includes the gateway" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "en0", ip: "1.1.1.1", netmask: "255.255.0.0", gateway: "1.2.3.4", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. config_en0=("1.1.1.1 netmask 255.255.0.0") gateways_en0="1.2.3.4" #VAGRANT-END EOH end end vagrant-2.0.2/test/unit/templates/guests/gentoo/systemd_network_test.rb000066400000000000000000000031501323370221500265400ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/template_renderer" describe "templates/guests/gentoo/network_systemd" do let(:template) { "guests/gentoo/network_systemd" } it "renders the template with a static ip" do result = Vagrant::Util::TemplateRenderer.render(template, networks: [{ device: "eth0", type: "dhcp", }]) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") [Match] Name=eth0 [Network] DHCP=yes EOH end it "renders the template with multiple ips" do result = Vagrant::Util::TemplateRenderer.render(template, networks: [{ device: "eth0", ip: "1.1.1.1", netmask: "16", },{ device: "eth0", ip: "1.1.2.2", netmask: "16", }]) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") [Match] Name=eth0 [Network] Address=1.1.1.1/16 Address=1.1.2.2/16 EOH end it "renders the template with a static ip" do result = Vagrant::Util::TemplateRenderer.render(template, networks: [{ device: "eth0", ip: "1.1.1.1", netmask: "16", }]) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") [Match] Name=eth0 [Network] Address=1.1.1.1/16 EOH end it "includes the gateway" do result = Vagrant::Util::TemplateRenderer.render(template, networks: [{ device: "eth0", ip: "1.1.1.1", netmask: "16", gateway: "1.2.3.4", }]) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") [Match] Name=eth0 [Network] Address=1.1.1.1/16 Gateway=1.2.3.4 EOH end end vagrant-2.0.2/test/unit/templates/guests/netbsd/000077500000000000000000000000001323370221500217005ustar00rootroot00000000000000vagrant-2.0.2/test/unit/templates/guests/netbsd/network_dhcp_test.rb000066400000000000000000000006771323370221500257650ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/template_renderer" describe "templates/guests/netbsd/network_dhcp" do let(:template) { "guests/netbsd/network_dhcp" } it "renders the template" do result = Vagrant::Util::TemplateRenderer.render(template, options: { interface: "en0", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN ifconfig_wmen0=dhcp #VAGRANT-END EOH end end vagrant-2.0.2/test/unit/templates/guests/netbsd/network_static_test.rb000066400000000000000000000010611323370221500263220ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/template_renderer" describe "templates/guests/netbsd/network_static" do let(:template) { "guests/netbsd/network_static" } it "renders the template" do result = Vagrant::Util::TemplateRenderer.render(template, options: { interface: "en0", ip: "1.1.1.1", netmask: "255.255.0.0", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN ifconfig_wmen0="media autoselect up;inet 1.1.1.1 netmask 255.255.0.0" #VAGRANT-END EOH end end vagrant-2.0.2/test/unit/templates/guests/redhat/000077500000000000000000000000001323370221500216705ustar00rootroot00000000000000vagrant-2.0.2/test/unit/templates/guests/redhat/network_dhcp_test.rb000066400000000000000000000020101323370221500257340ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/template_renderer" describe "templates/guests/redhat/network_dhcp" do let(:template) { "guests/redhat/network_dhcp" } it "renders the template" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "en0", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. BOOTPROTO=dhcp ONBOOT=yes DEVICE=en0 NM_CONTROLLED=no #VAGRANT-END EOH end it "renders the template with NetworkManager enabled" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "en0", nm_controlled: "yes" }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. BOOTPROTO=dhcp ONBOOT=yes DEVICE=en0 NM_CONTROLLED=yes #VAGRANT-END EOH end end vagrant-2.0.2/test/unit/templates/guests/redhat/network_static_test.rb000066400000000000000000000023711323370221500263170ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/template_renderer" describe "templates/guests/redhat/network_static" do let(:template) { "guests/redhat/network_static" } it "renders the template" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "en0", ip: "1.1.1.1", netmask: "255.255.0.0", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. NM_CONTROLLED=no BOOTPROTO=none ONBOOT=yes IPADDR=1.1.1.1 NETMASK=255.255.0.0 DEVICE=en0 PEERDNS=no #VAGRANT-END EOH end it "includes the gateway" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "en0", ip: "1.1.1.1", gateway: "1.2.3.4", netmask: "255.255.0.0", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. NM_CONTROLLED=no BOOTPROTO=none ONBOOT=yes IPADDR=1.1.1.1 NETMASK=255.255.0.0 DEVICE=en0 GATEWAY=1.2.3.4 PEERDNS=no #VAGRANT-END EOH end end vagrant-2.0.2/test/unit/templates/guests/suse/000077500000000000000000000000001323370221500214005ustar00rootroot00000000000000vagrant-2.0.2/test/unit/templates/guests/suse/network_dhcp_test.rb000066400000000000000000000010621323370221500254520ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/template_renderer" describe "templates/guests/suse/network_dhcp" do let(:template) { "guests/suse/network_dhcp" } it "renders the template" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "eth0" }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. BOOTPROTO='dhcp' STARTMODE='auto' DEVICE='eth0' #VAGRANT-END EOH end end vagrant-2.0.2/test/unit/templates/guests/suse/network_static_test.rb000066400000000000000000000024551323370221500260320ustar00rootroot00000000000000require_relative "../../../base" require "vagrant/util/template_renderer" describe "templates/guests/suse/network_static" do let(:template) { "guests/suse/network_static" } it "renders the template" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "eth1", ip: "1.1.1.1", netmask: "255.255.0.0", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. BOOTPROTO='static' IPADDR='1.1.1.1' NETMASK='255.255.0.0' DEVICE='eth1' PEERDNS='no' STARTMODE='auto' USERCONTROL='no' #VAGRANT-END EOH end it "includes the gateway" do result = Vagrant::Util::TemplateRenderer.render(template, options: { device: "eth1", ip: "1.1.1.1", gateway: "1.2.3.4", netmask: "255.255.0.0", }) expect(result).to eq <<-EOH.gsub(/^ {6}/, "") #VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. BOOTPROTO='static' IPADDR='1.1.1.1' NETMASK='255.255.0.0' DEVICE='eth1' GATEWAY='1.2.3.4' PEERDNS='no' STARTMODE='auto' USERCONTROL='no' #VAGRANT-END EOH end end vagrant-2.0.2/test/unit/vagrant/000077500000000000000000000000001323370221500165535ustar00rootroot00000000000000vagrant-2.0.2/test/unit/vagrant/action/000077500000000000000000000000001323370221500200305ustar00rootroot00000000000000vagrant-2.0.2/test/unit/vagrant/action/builder_test.rb000066400000000000000000000153551323370221500230530ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) describe Vagrant::Action::Builder do let(:data) { { data: [] } } # This returns a proc that can be used with the builder # that simply appends data to an array in the env. def appender_proc(data) result = Proc.new { |env| env[:data] << data } # Define a to_s on it for helpful output result.define_singleton_method(:to_s) do "" end result end def wrapper_proc(data) Class.new do def initialize(app, env) @app = app end define_method(:call) do |env| env[:data] << "#{data}_in" @app.call(env) env[:data] << "#{data}_out" end end end context "copying" do it "should copy the stack" do copy = subject.dup expect(copy.stack.object_id).not_to eq(subject.stack.object_id) end end context "build" do it "should provide build as a shortcut for basic sequences" do data = {} proc = Proc.new { |env| env[:data] = true } subject = described_class.build(proc) subject.call(data) expect(data[:data]).to eq(true) end end context "basic `use`" do it "should add items to the stack and make them callable" do data = {} proc = Proc.new { |env| env[:data] = true } subject.use proc subject.call(data) expect(data[:data]).to eq(true) end it "should be able to add multiple items" do data = {} proc1 = Proc.new { |env| env[:one] = true } proc2 = Proc.new { |env| env[:two] = true } subject.use proc1 subject.use proc2 subject.call(data) expect(data[:one]).to eq(true) expect(data[:two]).to eq(true) end it "should be able to add another builder" do data = {} proc1 = Proc.new { |env| env[:one] = true } # Build the first builder one = described_class.new one.use proc1 # Add it to this builder two = described_class.new two.use one # Call the 2nd and verify results two.call(data) expect(data[:one]).to eq(true) end end context "inserting" do it "can insert at an index" do subject.use appender_proc(1) subject.insert(0, appender_proc(2)) subject.call(data) expect(data[:data]).to eq([2, 1]) end it "can insert by name" do # Create the proc then make sure it has a name bar_proc = appender_proc(2) def bar_proc.name; :bar; end subject.use appender_proc(1) subject.use bar_proc subject.insert_before :bar, appender_proc(3) subject.call(data) expect(data[:data]).to eq([1, 3, 2]) end it "can insert next to a previous object" do proc2 = appender_proc(2) subject.use appender_proc(1) subject.use proc2 subject.insert(proc2, appender_proc(3)) subject.call(data) expect(data[:data]).to eq([1, 3, 2]) end it "can insert before" do subject.use appender_proc(1) subject.insert_before 0, appender_proc(2) subject.call(data) expect(data[:data]).to eq([2, 1]) end it "can insert after" do subject.use appender_proc(1) subject.use appender_proc(3) subject.insert_after 0, appender_proc(2) subject.call(data) expect(data[:data]).to eq([1, 2, 3]) end it "merges middleware stacks of other builders" do wrapper_class = Proc.new do |letter| Class.new do def initialize(app, env) @app = app end define_method(:call) do |env| env[:data] << "#{letter}1" @app.call(env) env[:data] << "#{letter}2" end end end proc2 = appender_proc(2) subject.use appender_proc(1) subject.use proc2 builder = described_class.new builder.use wrapper_class.call("A") builder.use wrapper_class.call("B") subject.insert(proc2, builder) subject.call(data) expect(data[:data]).to eq([1, "A1", "B1", 2, "B2", "A2"]) end it "raises an exception if an invalid object given for insert" do expect { subject.insert "object", appender_proc(1) }. to raise_error(RuntimeError) end it "raises an exception if an invalid object given for insert_after" do expect { subject.insert_after "object", appender_proc(1) }. to raise_error(RuntimeError) end end context "replace" do it "can replace an object" do proc1 = appender_proc(1) proc2 = appender_proc(2) subject.use proc1 subject.replace proc1, proc2 subject.call(data) expect(data[:data]).to eq([2]) end it "can replace by index" do proc1 = appender_proc(1) proc2 = appender_proc(2) subject.use proc1 subject.replace 0, proc2 subject.call(data) expect(data[:data]).to eq([2]) end end context "deleting" do it "can delete by object" do proc1 = appender_proc(1) subject.use proc1 subject.use appender_proc(2) subject.delete proc1 subject.call(data) expect(data[:data]).to eq([2]) end it "can delete by index" do proc1 = appender_proc(1) subject.use proc1 subject.use appender_proc(2) subject.delete 0 subject.call(data) expect(data[:data]).to eq([2]) end end describe "action hooks" do it "applies them properly" do hook = double("hook") allow(hook).to receive(:apply) do |builder| builder.use appender_proc(2) end data[:action_hooks] = [hook] subject.use appender_proc(1) subject.call(data) expect(data[:data]).to eq([1, 2]) expect(data[:action_hooks_already_ran]).to eq(true) end it "applies without prepend/append if it has already" do hook = double("hook") expect(hook).to receive(:apply).with(anything, { no_prepend_or_append: true }).once data[:action_hooks] = [hook] data[:action_hooks_already_ran] = true subject.call(data) end end describe "calling another app later" do it "calls in the proper order" do # We have to do this because inside the Class.new, it can't see these # rspec methods... described_klass = described_class wrapper_proc = self.method(:wrapper_proc) wrapper = Class.new do def initialize(app, env) @app = app end define_method(:call) do |env| inner = described_klass.new inner.use wrapper_proc[2] inner.use @app inner.call(env) end end subject.use wrapper_proc(1) subject.use wrapper subject.use wrapper_proc(3) subject.call(data) expect(data[:data]).to eq([ "1_in", "2_in", "3_in", "3_out", "2_out", "1_out"]) end end end vagrant-2.0.2/test/unit/vagrant/action/builtin/000077500000000000000000000000001323370221500214765ustar00rootroot00000000000000vagrant-2.0.2/test/unit/vagrant/action/builtin/box_add_test.rb000066400000000000000000001035031323370221500244640ustar00rootroot00000000000000require "digest/sha1" require "pathname" require "tempfile" require "tmpdir" require "webrick" require "fake_ftp" require File.expand_path("../../../../base", __FILE__) require "vagrant/util/file_checksum" describe Vagrant::Action::Builtin::BoxAdd, :skip_windows do include_context "unit" let(:app) { lambda { |env| } } let(:env) { { box_collection: box_collection, hook: Proc.new { |name, env| env }, tmp_path: Pathname.new(Dir.mktmpdir("vagrant-test-builtin-box-add")), ui: Vagrant::UI::Silent.new, } } subject { described_class.new(app, env) } let(:box_collection) { double("box_collection") } let(:iso_env) { isolated_environment } let(:box) do box_dir = iso_env.box3("foo", "1.0", :virtualbox) Vagrant::Box.new("foo", :virtualbox, "1.0", box_dir) end after do FileUtils.rm_rf(env[:tmp_path]) end # Helper to quickly SHA1 checksum a path def checksum(path) FileChecksum.new(path, Digest::SHA1).checksum end def with_ftp_server(path, **opts) path = Pathname.new(path) port = nil server = nil with_random_port do |port1, port2| port = port1 server = FakeFtp::Server.new(port1, port2) end server.add_file(path.basename, path.read) server.start yield port ensure server.stop rescue nil end def with_web_server(path, **opts) tf = Tempfile.new("vagrant-web-server") tf.close opts[:json_type] ||= "application/json" mime_types = WEBrick::HTTPUtils::DefaultMimeTypes mime_types.store "json", opts[:json_type] port = 3838 server = WEBrick::HTTPServer.new( AccessLog: [], Logger: WEBrick::Log.new(tf.path, 7), Port: port, DocumentRoot: path.dirname.to_s, MimeTypes: mime_types) thr = Thread.new { server.start } yield port ensure tf.unlink server.shutdown rescue nil thr.join rescue nil end before do allow(box_collection).to receive(:find).and_return(nil) end context "with box file directly" do it "adds it" do box_path = iso_env.box2_file(:virtualbox) env[:box_name] = "foo" env[:box_url] = box_path.to_s expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(checksum(path)).to eq(checksum(box_path)) expect(name).to eq("foo") expect(version).to eq("0") expect(opts[:metadata_url]).to be_nil true }.and_return(box) expect(app).to receive(:call).with(env) subject.call(env) end it "adds from multiple URLs" do box_path = iso_env.box2_file(:virtualbox) env[:box_name] = "foo" env[:box_url] = [ "/foo/bar/baz", box_path.to_s, ] expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(checksum(path)).to eq(checksum(box_path)) expect(name).to eq("foo") expect(version).to eq("0") expect(opts[:metadata_url]).to be_nil true }.and_return(box) expect(app).to receive(:call).with(env) subject.call(env) end it "adds from HTTP URL" do box_path = iso_env.box2_file(:virtualbox) with_web_server(box_path) do |port| env[:box_name] = "foo" env[:box_url] = "http://127.0.0.1:#{port}/#{box_path.basename}" expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(checksum(path)).to eq(checksum(box_path)) expect(name).to eq("foo") expect(version).to eq("0") expect(opts[:metadata_url]).to be_nil true }.and_return(box) expect(app).to receive(:call).with(env) subject.call(env) end end it "adds from FTP URL" do box_path = iso_env.box2_file(:virtualbox) with_ftp_server(box_path) do |port| env[:box_name] = "foo" env[:box_url] = "ftp://127.0.0.1:#{port}/#{box_path.basename}" expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(checksum(path)).to eq(checksum(box_path)) expect(name).to eq("foo") expect(version).to eq("0") expect(opts[:metadata_url]).to be_nil true }.and_return(box) expect(app).to receive(:call).with(env) subject.call(env) end end it "raises an error if no name is given" do box_path = iso_env.box2_file(:virtualbox) env[:box_url] = box_path.to_s expect(box_collection).to receive(:add).never expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxAddNameRequired) end it "raises an error if the box already exists" do box_path = iso_env.box2_file(:virtualbox) env[:box_name] = "foo" env[:box_url] = box_path.to_s env[:box_provider] = "virtualbox" expect(box_collection).to receive(:find).with( "foo", ["virtualbox"], "0").and_return(box) expect(box_collection).to receive(:add).never expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxAlreadyExists) end it "raises an error if checksum specified and doesn't match" do box_path = iso_env.box2_file(:virtualbox) env[:box_name] = "foo" env[:box_url] = box_path.to_s env[:box_checksum] = checksum(box_path) + "A" env[:box_checksum_type] = "sha1" expect(box_collection).to receive(:add).never expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxChecksumMismatch) end it "does not raise an error if the checksum has different case" do box_path = iso_env.box2_file(:virtualbox) box = double( name: "foo", version: "1.2.3", provider: "virtualbox", ) env[:box_name] = box.name env[:box_url] = box_path.to_s env[:box_checksum] = checksum(box_path) env[:box_checksum_type] = "sha1" # Convert to a different case env[:box_checksum].upcase! expect(box_collection).to receive(:add).and_return(box) expect { subject.call(env) }.to_not raise_error end it "raises an error if the box path doesn't exist" do box_path = iso_env.box2_file(:virtualbox) env[:box_name] = "foo" env[:box_url] = box_path.to_s + "nope" expect(box_collection).to receive(:add).never expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::DownloaderError) end it "raises an error if a version was specified" do box_path = iso_env.box2_file(:virtualbox) env[:box_name] = "foo" env[:box_url] = box_path.to_s env[:box_version] = "1" expect(box_collection).to receive(:add).never expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxAddDirectVersion) end it "force adds if exists and specified" do box_path = iso_env.box2_file(:virtualbox) env[:box_force] = true env[:box_name] = "foo" env[:box_url] = box_path.to_s env[:box_provider] = "virtualbox" allow(box_collection).to receive(:find).and_return(box) expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(checksum(path)).to eq(checksum(box_path)) expect(name).to eq("foo") expect(version).to eq("0") expect(opts[:metadata_url]).to be_nil true }.and_return(box) expect(app).to receive(:call).with(env).once subject.call(env) end context "with a box name accidentally set as a URL" do it "displays a warning to the user" do box_path = iso_env.box2_file(:virtualbox) port = 9999 box_url_name = "http://127.0.0.1:#{port}/#{box_path.basename}" env[:box_name] = box_url_name expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(name).to eq(box_url_name) expect(version).to eq("0") expect(opts[:metadata_url]).to be_nil true }.and_return(box) expect(app).to receive(:call).with(env) expect(env[:ui]).to receive(:warn) .with(/It looks like you attempted to add a box with a URL for the name/) subject.call(env) end end context "with a box name containing invalid URI characters" do it "should not raise an error" do box_path = iso_env.box2_file(:virtualbox) box_url_name = "box name with spaces" env[:box_name] = box_url_name expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(name).to eq(box_url_name) expect(version).to eq("0") expect(opts[:metadata_url]).to be_nil true }.and_return(box) expect(app).to receive(:call).with(env) subject.call(env) end end context "with URL containing credentials" do let(:username){ "box-username" } let(:password){ "box-password" } it "scrubs credentials in output" do box_path = iso_env.box2_file(:virtualbox) with_web_server(box_path) do |port| env[:box_name] = "foo" env[:box_url] = "http://#{username}:#{password}@127.0.0.1:#{port}/#{box_path.basename}" expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(checksum(path)).to eq(checksum(box_path)) expect(name).to eq("foo") expect(version).to eq("0") expect(opts[:metadata_url]).to be_nil true }.and_return(box) allow(env[:ui]).to receive(:detail) expect(env[:ui]).to receive(:detail).with(%r{.*http://(?!#{username}).+?:(?!#{password}).+?@127\.0\.0\.1:#{port}/#{box_path.basename}.*}) expect(app).to receive(:call).with(env) subject.call(env) end end end end context "with box metadata" do it "adds from HTTP URL" do box_path = iso_env.box2_file(:virtualbox) tf = Tempfile.new(["vagrant-test-box-http-url", ".json"]).tap do |f| f.write(<<-RAW) { "name": "foo/bar", "versions": [ { "version": "0.5" }, { "version": "0.7", "providers": [ { "name": "virtualbox", "url": "#{box_path}" } ] } ] } RAW f.close end md_path = Pathname.new(tf.path) with_web_server(md_path) do |port| env[:box_url] = "http://127.0.0.1:#{port}/#{md_path.basename}" expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(name).to eq("foo/bar") expect(version).to eq("0.7") expect(checksum(path)).to eq(checksum(box_path)) expect(opts[:metadata_url]).to eq(env[:box_url]) true }.and_return(box) expect(app).to receive(:call).with(env) subject.call(env) end end it "adds from HTTP URL with complex JSON mime type" do box_path = iso_env.box2_file(:virtualbox) tf = Tempfile.new(["vagrant-test-http-json", ".json"]).tap do |f| f.write(<<-RAW) { "name": "foo/bar", "versions": [ { "version": "0.5" }, { "version": "0.7", "providers": [ { "name": "virtualbox", "url": "#{box_path}" } ] } ] } RAW f.close end opts = { json_type: "application/json; charset=utf-8" } md_path = Pathname.new(tf.path) with_web_server(md_path, **opts) do |port| env[:box_url] = "http://127.0.0.1:#{port}/#{md_path.basename}" expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(name).to eq("foo/bar") expect(version).to eq("0.7") expect(checksum(path)).to eq(checksum(box_path)) expect(opts[:metadata_url]).to eq(env[:box_url]) true }.and_return(box) expect(app).to receive(:call).with(env) subject.call(env) end end it "adds from shorthand path" do box_path = iso_env.box2_file(:virtualbox) td = Pathname.new(Dir.mktmpdir("vagrant-test-box-add-shorthand-path")) tf = td.join("mitchellh", "precise64.json") tf.dirname.mkpath tf.open("w") do |f| f.write(<<-RAW) { "name": "mitchellh/precise64", "versions": [ { "version": "0.5" }, { "version": "0.7", "providers": [ { "name": "virtualbox", "url": "#{box_path}" } ] } ] } RAW end with_web_server(tf.dirname) do |port| url = "http://127.0.0.1:#{port}" env[:box_url] = "mitchellh/precise64.json" expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(name).to eq("mitchellh/precise64") expect(version).to eq("0.7") expect(checksum(path)).to eq(checksum(box_path)) expect(opts[:metadata_url]).to eq( "#{url}/#{env[:box_url]}") true }.and_return(box) expect(app).to receive(:call).with(env) with_temp_env("VAGRANT_SERVER_URL" => url) do subject.call(env) end end FileUtils.rm_rf(td) end it "add from shorthand path with configured server url" do box_path = iso_env.box2_file(:virtualbox) td = Pathname.new(Dir.mktmpdir("vagrant-test-box-add-server-url")) tf = td.join("mitchellh", "precise64.json") tf.dirname.mkpath tf.open("w") do |f| f.write(<<-RAW) { "name": "mitchellh/precise64", "versions": [ { "version": "0.5" }, { "version": "0.7", "providers": [ { "name": "virtualbox", "url": "#{box_path}" } ] } ] } RAW end with_web_server(tf.dirname) do |port| url = "http://127.0.0.1:#{port}" env[:box_url] = "mitchellh/precise64.json" env[:box_server_url] = url expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(name).to eq("mitchellh/precise64") expect(version).to eq("0.7") expect(checksum(path)).to eq(checksum(box_path)) expect(opts[:metadata_url]).to eq( "#{url}/#{env[:box_url]}") true }.and_return(box) expect(app).to receive(:call).with(env) subject.call(env) end FileUtils.rm_rf(td) end it "authenticates HTTP URLs and adds them" do box_path = iso_env.box2_file(:virtualbox) tf = Tempfile.new(["vagrant-test-http", ".json"]).tap do |f| f.write(<<-RAW) { "name": "foo/bar", "versions": [ { "version": "0.5" }, { "version": "0.7", "providers": [ { "name": "virtualbox", "url": "bar" } ] } ] } RAW f.close end md_path = Pathname.new(tf.path) with_web_server(md_path) do |port| real_url = "http://127.0.0.1:#{port}/#{md_path.basename}" # Set the box URL to something fake so we can modify it in place env[:box_url] = "foo" env[:hook] = double("hook") allow(env[:hook]).to receive(:call) do |name, opts| expect(name).to eq(:authenticate_box_url) if opts[:box_urls] == ["foo"] next { box_urls: [real_url] } elsif opts[:box_urls] == ["bar"] next { box_urls: [box_path.to_s] } else raise "UNKNOWN: #{opts[:box_urls].inspect}" end end expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(name).to eq("foo/bar") expect(version).to eq("0.7") expect(checksum(path)).to eq(checksum(box_path)) expect(opts[:metadata_url]).to eq("foo") true }.and_return(box) expect(app).to receive(:call).with(env) subject.call(env) end end it "adds from HTTP URL with a checksum" do box_path = iso_env.box2_file(:virtualbox) tf = Tempfile.new(["vagrant-test-http-checksum", ".json"]).tap do |f| f.write(<<-RAW) { "name": "foo/bar", "versions": [ { "version": "0.5" }, { "version": "0.7", "providers": [ { "name": "virtualbox", "url": "#{box_path}", "checksum_type": "sha1", "checksum": "#{checksum(box_path)}" } ] } ] } RAW f.close end md_path = Pathname.new(tf.path) with_web_server(md_path) do |port| env[:box_url] = "http://127.0.0.1:#{port}/#{md_path.basename}" expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(name).to eq("foo/bar") expect(version).to eq("0.7") expect(checksum(path)).to eq(checksum(box_path)) expect(opts[:metadata_url]).to eq(env[:box_url]) true }.and_return(box) expect(app).to receive(:call).with(env) subject.call(env) end end it "raises an exception if checksum given but not correct" do box_path = iso_env.box2_file(:virtualbox) tf = Tempfile.new(["vagrant-test-bad-checksum", ".json"]).tap do |f| f.write(<<-RAW) { "name": "foo/bar", "versions": [ { "version": "0.5" }, { "version": "0.7", "providers": [ { "name": "virtualbox", "url": "#{box_path}", "checksum_type": "sha1", "checksum": "thisisnotcorrect" } ] } ] } RAW f.close end md_path = Pathname.new(tf.path) with_web_server(md_path) do |port| env[:box_url] = "http://127.0.0.1:#{port}/#{md_path.basename}" expect(box_collection).to receive(:add).never expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxChecksumMismatch) end end it "raises an error if no Vagrant server is set" do env[:box_url] = "mitchellh/precise64.json" expect(box_collection).to receive(:add).never expect(app).to receive(:call).never allow(Vagrant).to receive(:server_url).and_return(nil) expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxServerNotSet) end it "raises an error if shorthand is invalid" do path = Dir::Tmpname.create("vagrant-shorthand-invalid") {} with_web_server(Pathname.new(path)) do |port| env[:box_url] = "mitchellh/precise64.json" expect(box_collection).to receive(:add).never expect(app).to receive(:call).never url = "http://127.0.0.1:#{port}" with_temp_env("VAGRANT_SERVER_URL" => url) do expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxAddShortNotFound) end end end it "raises an error if multiple metadata URLs are given" do box_path = iso_env.box2_file(:virtualbox) tf = Tempfile.new(["vagrant-box-multi-metadata", ".json"]).tap do |f| f.write(<<-RAW) { "name": "foo/bar", "versions": [ { "version": "0.5" }, { "version": "0.7", "providers": [ { "name": "virtualbox", "url": "#{box_path}" } ] } ] } RAW f.close end env[:box_url] = [ "/foo/bar/baz", tf.path, ] expect(box_collection).to receive(:add).never expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxAddMetadataMultiURL) end it "adds the latest version of a box with only one provider" do box_path = iso_env.box2_file(:virtualbox) tf = Tempfile.new(["vagrant-box-latest-version", ".json"]).tap do |f| f.write(<<-RAW) { "name": "foo/bar", "versions": [ { "version": "0.5" }, { "version": "0.7", "providers": [ { "name": "virtualbox", "url": "#{box_path}" } ] } ] } RAW f.close end env[:box_url] = tf.path expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(checksum(path)).to eq(checksum(box_path)) expect(name).to eq("foo/bar") expect(version).to eq("0.7") expect(opts[:metadata_url]).to eq("file://#{tf.path}") true }.and_return(box) expect(app).to receive(:call).with(env) subject.call(env) end it "adds the latest version of a box with the specified provider" do box_path = iso_env.box2_file(:vmware) tf = Tempfile.new(["vagrant-box-specific-provider", ".json"]).tap do |f| f.write(<<-RAW) { "name": "foo/bar", "versions": [ { "version": "0.5" }, { "version": "0.7", "providers": [ { "name": "virtualbox", "url": "#{iso_env.box2_file(:virtualbox)}" }, { "name": "vmware", "url": "#{box_path}" } ] } ] } RAW f.close end env[:box_url] = tf.path env[:box_provider] = "vmware" expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(checksum(path)).to eq(checksum(box_path)) expect(name).to eq("foo/bar") expect(version).to eq("0.7") expect(opts[:metadata_url]).to eq("file://#{tf.path}") true }.and_return(box) expect(app).to receive(:call).with(env) subject.call(env) expect(env[:box_added]).to equal(box) end it "adds the latest version of a box with the specified provider, even if not latest" do box_path = iso_env.box2_file(:vmware) tf = Tempfile.new(["vagrant-box-specified-provider", ".json"]).tap do |f| f.write(<<-RAW) { "name": "foo/bar", "versions": [ { "version": "0.5" }, { "version": "0.7", "providers": [ { "name": "virtualbox", "url": "#{iso_env.box2_file(:virtualbox)}" }, { "name": "vmware", "url": "#{box_path}" } ] }, { "version": "1.5" } ] } RAW f.close end env[:box_url] = tf.path env[:box_provider] = "vmware" expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(checksum(path)).to eq(checksum(box_path)) expect(name).to eq("foo/bar") expect(version).to eq("0.7") expect(opts[:metadata_url]).to eq("file://#{tf.path}") true }.and_return(box) expect(app).to receive(:call).with(env) subject.call(env) expect(env[:box_added]).to equal(box) end it "adds the constrained version of a box with the only provider" do box_path = iso_env.box2_file(:vmware) tf = Tempfile.new(["vagrant-box-constrained", ".json"]).tap do |f| f.write(<<-RAW) { "name": "foo/bar", "versions": [ { "version": "0.5", "providers": [ { "name": "vmware", "url": "#{box_path}" } ] }, { "version": "1.1" } ] } RAW f.close end env[:box_url] = tf.path env[:box_version] = "~> 0.1" expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(checksum(path)).to eq(checksum(box_path)) expect(name).to eq("foo/bar") expect(version).to eq("0.5") expect(opts[:metadata_url]).to eq("file://#{tf.path}") true }.and_return(box) expect(app).to receive(:call).with(env) subject.call(env) expect(env[:box_added]).to equal(box) end it "adds the constrained version of a box with the specified provider" do box_path = iso_env.box2_file(:vmware) tf = Tempfile.new(["vagrant-box-constrained-provider", ".json"]).tap do |f| f.write(<<-RAW) { "name": "foo/bar", "versions": [ { "version": "0.5", "providers": [ { "name": "vmware", "url": "#{box_path}" }, { "name": "virtualbox", "url": "#{iso_env.box2_file(:virtualbox)}" } ] }, { "version": "1.1" } ] } RAW f.close end env[:box_url] = tf.path env[:box_provider] = "vmware" env[:box_version] = "~> 0.1" expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(checksum(path)).to eq(checksum(box_path)) expect(name).to eq("foo/bar") expect(version).to eq("0.5") expect(opts[:metadata_url]).to eq("file://#{tf.path}") true }.and_return(box) expect(app).to receive(:call).with(env) subject.call(env) expect(env[:box_added]).to equal(box) end it "adds the latest version of a box with any specified provider" do box_path = iso_env.box2_file(:vmware) tf = Tempfile.new(["vagrant-box-latest-version", ".json"]).tap do |f| f.write(<<-RAW) { "name": "foo/bar", "versions": [ { "version": "0.5", "providers": [ { "name": "virtualbox", "url": "#{iso_env.box2_file(:virtualbox)}" } ] }, { "version": "0.7", "providers": [ { "name": "vmware", "url": "#{box_path}" } ] } ] } RAW f.close end env[:box_url] = tf.path env[:box_provider] = ["virtualbox", "vmware"] expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(checksum(path)).to eq(checksum(box_path)) expect(name).to eq("foo/bar") expect(version).to eq("0.7") expect(opts[:metadata_url]).to eq("file://#{tf.path}") true }.and_return(box) expect(app).to receive(:call).with(env) subject.call(env) expect(env[:box_added]).to equal(box) end it "asks the user what provider if multiple options" do box_path = iso_env.box2_file(:virtualbox) tf = Tempfile.new(["vagrant-box-provider-asks", ".json"]).tap do |f| f.write(<<-RAW) { "name": "foo/bar", "versions": [ { "version": "0.5" }, { "version": "0.7", "providers": [ { "name": "virtualbox", "url": "#{box_path}" }, { "name": "vmware", "url": "#{iso_env.box2_file(:vmware)}" } ] } ] } RAW f.close end env[:box_url] = tf.path expect(env[:ui]).to receive(:ask).and_return("1") expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(checksum(path)).to eq(checksum(box_path)) expect(name).to eq("foo/bar") expect(version).to eq("0.7") expect(opts[:metadata_url]).to eq("file://#{tf.path}") true }.and_return(box) expect(app).to receive(:call).with(env) subject.call(env) end it "raises an exception if the name doesn't match a requested name" do box_path = iso_env.box2_file(:virtualbox) tf = Tempfile.new(["vagrant-box-name-mismatch", ".json"]).tap do |f| f.write(<<-RAW) { "name": "foo/bar", "versions": [ { "version": "0.5" }, { "version": "0.7", "providers": [ { "name": "virtualbox", "url": "#{box_path}" } ] } ] } RAW f.close end env[:box_name] = "foo" env[:box_url] = tf.path expect(box_collection).to receive(:add).never expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxAddNameMismatch) end it "raises an exception if no matching version" do box_path = iso_env.box2_file(:vmware) tf = Tempfile.new(["vagrant-box-no-matching-version", ".json"]).tap do |f| f.write(<<-RAW) { "name": "foo/bar", "versions": [ { "version": "0.5", "providers": [ { "name": "vmware", "url": "#{box_path}" } ] }, { "version": "1.1" } ] } RAW f.close end env[:box_url] = tf.path env[:box_version] = "~> 2.0" expect(box_collection).to receive(:add).never expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxAddNoMatchingVersion) end it "raises an error if there is no matching provider" do tf = Tempfile.new(["vagrant-box-no-matching-provider", ".json"]).tap do |f| f.write(<<-RAW) { "name": "foo/bar", "versions": [ { "version": "0.5" }, { "version": "0.7", "providers": [ { "name": "virtualbox", "url": "#{iso_env.box2_file(:virtualbox)}" } ] } ] } RAW f.close end env[:box_url] = tf.path env[:box_provider] = "vmware" expect(box_collection).to receive(:add).never expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxAddNoMatchingProvider) end it "raises an error if a box already exists" do box_path = iso_env.box2_file(:virtualbox) tf = Tempfile.new(["vagrant-box-already-exists", ".json"]).tap do |f| f.write(<<-RAW) { "name": "foo/bar", "versions": [ { "version": "0.5" }, { "version": "0.7", "providers": [ { "name": "virtualbox", "url": "#{box_path}" } ] } ] } RAW f.close end env[:box_url] = tf.path expect(box_collection).to receive(:find). with("foo/bar", "virtualbox", "0.7").and_return(box) expect(box_collection).to receive(:add).never expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxAlreadyExists) end it "force adds a box if specified" do box_path = iso_env.box2_file(:virtualbox) tf = Tempfile.new(["vagrant-box-force-add", ".json"]).tap do |f| f.write(<<-RAW) { "name": "foo/bar", "versions": [ { "version": "0.5" }, { "version": "0.7", "providers": [ { "name": "virtualbox", "url": "#{box_path}" } ] } ] } RAW f.close end env[:box_force] = true env[:box_url] = tf.path allow(box_collection).to receive(:find).and_return(box) expect(box_collection).to receive(:add).with(any_args) { |path, name, version, **opts| expect(checksum(path)).to eq(checksum(box_path)) expect(name).to eq("foo/bar") expect(version).to eq("0.7") expect(opts[:force]).to be(true) expect(opts[:metadata_url]).to eq("file://#{tf.path}") true }.and_return(box) expect(app).to receive(:call).with(env) subject.call(env) expect(env[:box_added]).to equal(box) end end end vagrant-2.0.2/test/unit/vagrant/action/builtin/box_check_outdated_test.rb000066400000000000000000000133041323370221500267010ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::BoxCheckOutdated do include_context "unit" let(:app) { lambda { |env| } } let(:env) { { box_collection: iso_vagrant_env.boxes, machine: machine, ui: Vagrant::UI::Silent.new, } } subject { described_class.new(app, env) } let(:iso_env) do # We have to create a Vagrantfile so there is a root path isolated_environment.tap do |env| env.vagrantfile("") end end let(:iso_vagrant_env) { iso_env.create_vagrant_env } let(:box) do box_dir = iso_env.box3("foo", "1.0", :virtualbox) Vagrant::Box.new("foo", :virtualbox, "1.0", box_dir).tap do |b| allow(b).to receive(:has_update?).and_return(nil) end end let(:machine) do m = iso_vagrant_env.machine(iso_vagrant_env.machine_names[0], :dummy) m.config.vm.box_check_update = true m end before do allow(machine).to receive(:box).and_return(box) end context "disabling outdated checking" do it "doesn't check" do machine.config.vm.box_check_update = false expect(app).to receive(:call).with(env).once subject.call(env) expect(env).to_not have_key(:box_outdated) end it "checks if forced" do machine.config.vm.box_check_update = false env[:box_outdated_force] = true expect(app).to receive(:call).with(env).once subject.call(env) expect(env).to have_key(:box_outdated) end end context "no box" do it "raises an exception if the machine doesn't have a box yet" do allow(machine).to receive(:box).and_return(nil) expect(app).to receive(:call).with(env).once subject.call(env) expect(env).to_not have_key(:box_outdated) end end context "with a non-versioned box" do it "does nothing" do allow(box).to receive(:metadata_url).and_return(nil) allow(box).to receive(:version).and_return("0") expect(app).to receive(:call).once expect(box).to receive(:has_update?).never subject.call(env) end end context "with a box" do it "sets env if no update" do expect(box).to receive(:has_update?).and_return(nil) expect(app).to receive(:call).with(env).once subject.call(env) expect(env[:box_outdated]).to be(false) end it "sets env if there is an update" do md = Vagrant::BoxMetadata.new(StringIO.new(<<-RAW)) { "name": "foo", "versions": [ { "version": "1.0" }, { "version": "1.1", "providers": [ { "name": "virtualbox", "url": "bar" } ] } ] } RAW expect(box).to receive(:has_update?).with(machine.config.vm.box_version, {download_options: {ca_cert: nil, ca_path: nil, client_cert: nil, insecure: false}}). and_return([md, md.version("1.1"), md.version("1.1").provider("virtualbox")]) expect(app).to receive(:call).with(env).once subject.call(env) expect(env[:box_outdated]).to be(true) end it "has an update if it is local" do iso_env.box3("foo", "1.1", :virtualbox) expect(box).to receive(:has_update?).and_return(nil) expect(app).to receive(:call).with(env).once subject.call(env) expect(env[:box_outdated]).to be(true) end it "does not have a local update if not within constraints" do iso_env.box3("foo", "1.1", :virtualbox) machine.config.vm.box_version = "> 1.0, < 1.1" expect(box).to receive(:has_update?).and_return(nil) expect(app).to receive(:call).with(env).once subject.call(env) expect(env[:box_outdated]).to be(false) end it "does nothing if metadata download fails" do expect(box).to receive(:has_update?).and_raise( Vagrant::Errors::BoxMetadataDownloadError.new(message: "foo")) expect(app).to receive(:call).once subject.call(env) expect(env[:box_outdated]).to be(false) end it "raises error if has_update? errors" do expect(box).to receive(:has_update?).and_raise(Vagrant::Errors::VagrantError) expect(app).to receive(:call).never expect { subject.call(env) }.to raise_error(Vagrant::Errors::VagrantError) end it "doesn't raise an error if ignore errors is on" do env[:box_outdated_ignore_errors] = true expect(box).to receive(:has_update?).and_raise(Vagrant::Errors::VagrantError) expect(app).to receive(:call).with(env).once expect { subject.call(env) }.to_not raise_error end context "when machine download options are specified" do before do machine.config.vm.box_download_ca_cert = "foo" machine.config.vm.box_download_ca_path = "bar" machine.config.vm.box_download_client_cert = "baz" machine.config.vm.box_download_insecure = true end it "uses download options from machine" do expect(box).to receive(:has_update?).with(machine.config.vm.box_version, {download_options: {ca_cert: "foo", ca_path: "bar", client_cert: "baz", insecure: true}}) expect(app).to receive(:call).with(env).once subject.call(env) end it "overrides download options from machine with options from env" do expect(box).to receive(:has_update?).with(machine.config.vm.box_version, {download_options: {ca_cert: "oof", ca_path: "rab", client_cert: "zab", insecure: false}}) env[:ca_cert] = "oof" env[:ca_path] = "rab" env[:client_cert] = "zab" env[:insecure] = false expect(app).to receive(:call).with(env).once subject.call(env) end end end end vagrant-2.0.2/test/unit/vagrant/action/builtin/box_remove_test.rb000066400000000000000000000137351323370221500252400ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::BoxRemove do include_context "unit" let(:app) { lambda { |env| } } let(:env) { { box_collection: box_collection, home_path: home_path, machine_index: machine_index, ui: Vagrant::UI::Silent.new, } } subject { described_class.new(app, env) } let(:box_collection) { double("box_collection") } let(:home_path) { "foo" } let(:machine_index) { [] } let(:iso_env) { isolated_environment } let(:box) do box_dir = iso_env.box3("foo", "1.0", :virtualbox) Vagrant::Box.new("foo", :virtualbox, "1.0", box_dir) end it "deletes the box if it is the only option" do allow(box_collection).to receive(:all).and_return([["foo", "1.0", :virtualbox]]) env[:box_name] = "foo" 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!).once expect(app).to receive(:call).with(env).once subject.call(env) expect(env[:box_removed]).to equal(box) end it "deletes the box with the specified provider if given" do allow(box_collection).to receive(:all) .and_return([ ["foo", "1.0", :virtualbox], ["foo", "1.0", :vmware], ]) env[:box_name] = "foo" env[:box_provider] = "virtualbox" 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(false) expect(box).to receive(:destroy!).once expect(app).to receive(:call).with(env).once subject.call(env) expect(env[:box_removed]).to equal(box) end it "deletes the box with the specified version if given" do allow(box_collection).to receive(:all) .and_return([ ["foo", "1.0", :virtualbox], ["foo", "1.1", :virtualbox], ]) env[:box_name] = "foo" env[:box_version] = "1.0" 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(false) expect(box).to receive(:destroy!).once expect(app).to receive(:call).with(env).once subject.call(env) expect(env[:box_removed]).to equal(box) end context "checking if a box is in use" do def new_entry(name, provider, version, valid=true) Vagrant::MachineIndex::Entry.new.tap do |entry| entry.extra_data["box"] = { "name" => "foo", "provider" => "virtualbox", "version" => "1.0", } allow(entry).to receive(:valid?).and_return(valid) end end let(:action_runner) { double("action_runner") } before do env[:action_runner] = action_runner allow(box_collection).to receive(:all) .and_return([ ["foo", "1.0", :virtualbox], ["foo", "1.1", :virtualbox], ]) env[:box_name] = "foo" env[:box_version] = "1.0" end it "does delete if the box is not in use" do expect(box_collection).to receive(:find).with( "foo", :virtualbox, "1.0").and_return(box) expect(box).to receive(:destroy!).once expect(box_collection).to receive(:clean).with(box.name) .and_return(true) subject.call(env) end it "does delete if the box is in use and user confirms" 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!).once subject.call(env) end it "doesn't delete if the box is in use" do machine_index << new_entry("foo", "virtualbox", "1.0") result = { result: false } 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 end it "errors if the box doesn't exist" do allow(box_collection).to receive(:all).and_return([]) expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxRemoveNotFound) end it "errors if the specified provider doesn't exist" do env[:box_name] = "foo" env[:box_provider] = "bar" allow(box_collection).to receive(:all).and_return([["foo", "1.0", :virtualbox]]) expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxRemoveProviderNotFound) end it "errors if there are multiple providers" do env[:box_name] = "foo" allow(box_collection).to receive(:all) .and_return([ ["foo", "1.0", :virtualbox], ["foo", "1.0", :vmware], ]) expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxRemoveMultiProvider) end it "errors if the specified provider has multiple versions" do env[:box_name] = "foo" env[:box_provider] = "virtualbox" allow(box_collection).to receive(:all) .and_return([ ["foo", "1.0", :virtualbox], ["foo", "1.1", :virtualbox], ]) expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxRemoveMultiVersion) end it "errors if the specified version doesn't exist" do env[:box_name] = "foo" env[:box_version] = "1.1" allow(box_collection).to receive(:all).and_return([["foo", "1.0", :virtualbox]]) expect(app).to receive(:call).never expect { subject.call(env) }. to raise_error(Vagrant::Errors::BoxRemoveVersionNotFound) end end vagrant-2.0.2/test/unit/vagrant/action/builtin/call_test.rb000066400000000000000000000077351323370221500240110ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::Call do let(:app) { lambda { |env| } } let(:env) { {} } def wrapper_proc(data) Class.new do def initialize(app, env) @app = app end define_method(:call) do |env| env[:data] << "#{data}_in" @app.call(env) env[:data] << "#{data}_out" end end end it "should yield the env to the block" do received = nil callable = lambda do |env| env[:result] = "value" end described_class.new(app, env, callable) do |env, builder| received = env[:result] end.call({}) expect(received).to eq("value") end it "should update the original env with any changes" do callable = lambda { |env| } next_step = lambda { |env| env[:inner] = true } described_class.new(app, env, callable) do |_env, builder| builder.use next_step end.call(env) expect(env[:inner]).to eq(true) end it "should call the callable with the original environment" do received = nil callable = lambda { |env| received = env[:foo] } described_class.new(app, env, callable) do |_env, _builder| # Nothing. end.call({ foo: :bar }) expect(received).to eq(:bar) end it "should call the next builder" do received = nil callable = lambda { |env| } next_step = lambda { |env| received = "value" } described_class.new(app, env, callable) do |_env, builder| builder.use next_step end.call({}) expect(received).to eq("value") end it "should call the next builder with the original environment" do received = nil callable = lambda { |env| } next_step = lambda { |env| received = env[:foo] } described_class.new(app, env, callable) do |_env, builder| builder.use next_step end.call({ foo: :bar }) expect(received).to eq(:bar) end it "should call the next builder inserted in our own stack" do callable = lambda { |env| } builder = Vagrant::Action::Builder.new.tap do |b| b.use wrapper_proc(1) b.use described_class, callable do |_env, b2| b2.use wrapper_proc(2) end b.use wrapper_proc(3) end env = { data: [] } builder.call(env) expect(env[:data]).to eq([ "1_in", "2_in", "3_in", "3_out", "2_out", "1_out"]) end it "should instantiate the callable with the extra args" do env = {} callable = Class.new do def initialize(app, env, arg) env[:arg] = arg end def call(env); end end result = nil instance = described_class.new(app, env, callable, :foo) do |inner_env, _builder| result = inner_env[:arg] end instance.call(env) expect(result).to eq(:foo) end it "should call the recover method for the sequence in an error" do # Basic variables callable = lambda { |env| } # Build the steps for the test basic_step = Class.new do def initialize(app, env) @app = app @env = env end def call(env) @app.call(env) end end step_a = Class.new(basic_step) do def call(env) env[:steps] << :call_A super end def recover(env) env[:steps] << :recover_A end end step_b = Class.new(basic_step) do def call(env) env[:steps] << :call_B super end def recover(env) env[:steps] << :recover_B end end instance = described_class.new(app, env, callable) do |_env, builder| builder.use step_a builder.use step_b end env[:steps] = [] instance.call(env) instance.recover(env) expect(env[:steps]).to eq([:call_A, :call_B, :recover_B, :recover_A]) end it "should recover even if it failed in the callable" do callable = lambda { |env| raise "error" } instance = described_class.new(app, env, callable) { |_env, _builder| } instance.call(env) rescue nil expect { instance.recover(env) }. to_not raise_error end end vagrant-2.0.2/test/unit/vagrant/action/builtin/confirm_test.rb000066400000000000000000000030711323370221500245200ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::Confirm do let(:app) { lambda { |env| } } let(:env) { { ui: double("ui") } } let(:message) { "foo" } ["y", "Y"].each do |valid| it "should set the result to true if '#{valid}' is given" do expect(env[:ui]).to receive(:ask).with(message).and_return(valid) described_class.new(app, env, message).call(env) expect(env[:result]).to be end end it "should set the result to true if force matches" do force_key = :tubes env[force_key] = true described_class.new(app, env, message, force_key).call(env) expect(env[:result]).to be end it "should ask if force is not true" do force_key = :tubes env[force_key] = false expect(env[:ui]).to receive(:ask).with(message).and_return("nope") described_class.new(app, env, message).call(env) expect(env[:result]).not_to be end it "should set result to false if anything else is given" do expect(env[:ui]).to receive(:ask).with(message).and_return("nope") described_class.new(app, env, message).call(env) expect(env[:result]).not_to be end it "should ask multiple times if an allowed set is given and response isn't in that set" do times = 0 allow(env[:ui]).to receive(:ask) do |arg| expect(arg).to eql(message) times += 1 if times <= 3 "nope" else "y" end end described_class.new(app, env, message, allowed: ["y", "N"]).call(env) expect(env[:result]).to be(true) expect(times).to eq(4) end end vagrant-2.0.2/test/unit/vagrant/action/builtin/env_set_test.rb000066400000000000000000000010261323370221500245240ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::EnvSet do let(:app) { lambda { |env| } } let(:env) { {} } it "should set the new environment" do described_class.new(app, env, foo: :bar).call(env) expect(env[:foo]).to eq(:bar) end it "should call the next middleware" do callable = lambda { |env| env[:called] = env[:foo] } expect(env[:called]).to be_nil described_class.new(callable, env, foo: :yep).call(env) expect(env[:called]).to eq(:yep) end end vagrant-2.0.2/test/unit/vagrant/action/builtin/graceful_halt_test.rb000066400000000000000000000035371323370221500256720ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::GracefulHalt do let(:app) { lambda { |env| } } let(:env) { { machine: machine, ui: ui } } let(:machine) do result = double("machine") allow(result).to receive(:config).and_return(machine_config) allow(result).to receive(:guest).and_return(machine_guest) allow(result).to receive(:state).and_return(machine_state) result end let(:machine_config) do double("machine_config").tap do |top_config| vm_config = double("machien_vm_config") allow(vm_config).to receive(:graceful_halt_timeout).and_return(10) allow(top_config).to receive(:vm).and_return(vm_config) end end let(:machine_guest) { double("machine_guest") } let(:machine_state) do double("machine_state").tap do |result| allow(result).to receive(:id).and_return(:unknown) end end let(:target_state) { :target } let(:ui) do double("ui").tap do |result| allow(result).to receive(:output) end end it "should do nothing if force is specified" do env[:force_halt] = true expect(machine_guest).not_to receive(:capability) described_class.new(app, env, target_state).call(env) expect(env[:result]).to eq(false) end it "should do nothing if there is an invalid source state" do allow(machine_state).to receive(:id).and_return(:invalid_source) expect(machine_guest).not_to receive(:capability) described_class.new(app, env, target_state, :target_source).call(env) expect(env[:result]).to eq(false) end it "should gracefully halt and wait for the target state" do expect(machine_guest).to receive(:capability).with(:halt).once allow(machine_state).to receive(:id).and_return(target_state) described_class.new(app, env, target_state).call(env) expect(env[:result]).to eq(true) end end vagrant-2.0.2/test/unit/vagrant/action/builtin/handle_box_test.rb000066400000000000000000000100321323370221500251610ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::HandleBox do include_context "unit" let(:app) { lambda { |env| } } let(:env) { { action_runner: action_runner, machine: machine, ui: Vagrant::UI::Silent.new, } } subject { described_class.new(app, env) } let(:iso_env) do # We have to create a Vagrantfile so there is a root path isolated_environment.tap do |env| env.vagrantfile("") end end let(:iso_vagrant_env) { iso_env.create_vagrant_env } let(:action_runner) { double("action_runner") } let(:box) do box_dir = iso_env.box3("foo", "1.0", :virtualbox) Vagrant::Box.new("foo", :virtualbox, "1.0", box_dir) end let(:machine) { iso_vagrant_env.machine(iso_vagrant_env.machine_names[0], :dummy) } it "works if there is no box set" do machine.config.vm.box = nil machine.config.vm.box_url = nil expect(app).to receive(:call).with(env) subject.call(env) end it "doesn't do anything if a box exists" do allow(machine).to receive(:box).and_return(box) expect(action_runner).to receive(:run).never expect(app).to receive(:call).with(env) subject.call(env) end context "with a box set and no box_url" do before do allow(machine).to receive(:box).and_return(nil) machine.config.vm.box = "foo" end it "adds a box that doesn't exist" do expect(action_runner).to receive(:run).with(any_args) { |action, opts| expect(opts[:box_name]).to eq(machine.config.vm.box) expect(opts[:box_url]).to eq(machine.config.vm.box) expect(opts[:box_provider]).to eq(:dummy) expect(opts[:box_version]).to eq(machine.config.vm.box_version) true } expect(app).to receive(:call).with(env) subject.call(env) end it "adds a box using any format the provider allows" do machine.provider_options[:box_format] = [:foo, :bar] expect(action_runner).to receive(:run).with(any_args) { |action, opts| expect(opts[:box_name]).to eq(machine.config.vm.box) expect(opts[:box_url]).to eq(machine.config.vm.box) expect(opts[:box_provider]).to eq([:foo, :bar]) expect(opts[:box_version]).to eq(machine.config.vm.box_version) true } expect(app).to receive(:call).with(env) subject.call(env) end end context "with a box and box_url set" do before do allow(machine).to receive(:box).and_return(nil) machine.config.vm.box = "foo" machine.config.vm.box_url = "bar" end it "adds a box that doesn't exist" do expect(action_runner).to receive(:run).with(any_args) { |action, opts| expect(opts[:box_name]).to eq(machine.config.vm.box) expect(opts[:box_url]).to eq(machine.config.vm.box_url) expect(opts[:box_provider]).to eq(:dummy) expect(opts[:box_version]).to eq(machine.config.vm.box_version) true } expect(app).to receive(:call).with(env) subject.call(env) end end context "with a box with a checksum set" do before do allow(machine).to receive(:box).and_return(nil) machine.config.vm.box = "foo" machine.config.vm.box_url = "bar" machine.config.vm.box_download_checksum_type = "sha256" machine.config.vm.box_download_checksum = "1f42ac2decf0169c4af02b2d8c77143ce35f7ba87d5d844e19bf7cbb34fbe74e" end it "adds a box that doesn't exist and maps checksum options correctly" do expect(action_runner).to receive(:run).with(any_args) { |action, opts| expect(opts[:box_name]).to eq(machine.config.vm.box) expect(opts[:box_url]).to eq(machine.config.vm.box_url) expect(opts[:box_provider]).to eq(:dummy) expect(opts[:box_version]).to eq(machine.config.vm.box_version) expect(opts[:box_checksum_type]).to eq(machine.config.vm.box_download_checksum_type) expect(opts[:box_checksum]).to eq(machine.config.vm.box_download_checksum) true } expect(app).to receive(:call).with(env) subject.call(env) end end end vagrant-2.0.2/test/unit/vagrant/action/builtin/handle_forwarded_port_collisions_test.rb000066400000000000000000000121341323370221500316550ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/kernel_v2/config/vm") describe Vagrant::Action::Builtin::HandleForwardedPortCollisions do include_context "unit" let(:app) { lambda { |env| } } let(:env) { { machine: machine, ui: ui, port_collision_extra_in_use: extra_in_use, port_collision_remap: collision_remap, port_collision_repair: collision_repair, port_collision_port_check: collision_port_check } } let(:extra_in_use){ nil } let(:collision_remap){ nil } let(:collision_repair){ nil } let(:collision_port_check){ nil } let(:port_check_method){ nil } let(:machine) do double("machine").tap do |machine| allow(machine).to receive(:config).and_return(machine_config) allow(machine).to receive(:env).and_return(machine_env) end end let(:machine_config) do double("machine_config").tap do |config| allow(config).to receive(:vm).and_return(vm_config) end end let(:data_dir){ temporary_dir } let(:machine_env) do isolated_environment.tap do |i_env| allow(i_env).to receive(:data_dir).and_return(data_dir) allow(i_env).to receive(:lock).and_yield end end let(:vm_config) do double("machine_vm_config").tap do |config| allow(config).to receive(:usable_port_range).and_return(1000..2000) allow(config).to receive(:networks).and_return([]) end end let(:ui) do double("ui").tap do |result| allow(result).to receive(:info) end end let(:instance){ described_class.new(app, env) } describe "#call" do it "should create a lock while action runs" do expect(machine_env).to receive(:lock).with("fpcollision").and_yield instance.call(env) end context "with extra ports in use provided as Array type" do let(:extra_in_use){ [80] } it "should not generate an error" do expect{ instance.call(env) }.not_to raise_error end end context "with forwarded port defined" do let(:port_options){ {guest: 80, host: 8080} } before do expect(vm_config).to receive(:networks).and_return([[:forwarded_port, port_options]]).twice end it "should check if host port is in use" do expect(instance).to receive(:is_forwarded_already).and_return false instance.call(env) end context "with forwarded port already in use" do let(:extra_in_use){ [8080] } it "should raise a port collision error" do expect{ instance.call(env) }.to raise_error(Vagrant::Errors::ForwardPortCollision) end context "with auto_correct enabled" do before{ port_options[:auto_correct] = true } it "should raise a port collision error" do expect{ instance.call(env) }.to raise_error(Vagrant::Errors::ForwardPortCollision) end context "with collision repair enabled" do let(:collision_repair){ true } it "should automatically correct collision" do expect{ instance.call(env) }.not_to raise_error end end end end context "with custom port_check method" do let(:check_result){ [] } let(:port_options){ {guest: 80, host: 8080, host_ip: "127.0.1.1"} } context "that accepts two parameters" do let(:collision_port_check) do lambda do |host_ip, host_port| check_result.push(host_ip) check_result.push(host_port) false end end it "should receive both host_ip and host_port" do instance.call(env) expect(check_result).to include(port_options[:host]) expect(check_result).to include(port_options[:host_ip]) end end context "that accepts one parameter" do let(:collision_port_check) do lambda do |host_port| check_result.push(host_port) false end end it "should receive the host_port only" do instance.call(env) expect(check_result).to eq([port_options[:host]]) end end end end end describe "#recover" do end describe "#port_check" do let(:host_ip){ "127.0.0.1" } let(:host_port){ 8080 } it "should check if the port is open" do expect(instance).to receive(:is_port_open?).with(host_ip, host_port).and_return true instance.send(:port_check, host_ip, host_port) end context "when host_ip is not set" do let(:host_ip){ nil } it "should set host_ip to 0.0.0.0 when unset" do expect(instance).to receive(:is_port_open?).with("0.0.0.0", host_port).and_return true instance.send(:port_check, host_ip, host_port) end it "should set host_ip to 127.0.0.1 when 0.0.0.0 is not available" do expect(instance).to receive(:is_port_open?).with("0.0.0.0", host_port).and_raise Errno::EADDRNOTAVAIL expect(instance).to receive(:is_port_open?).with("127.0.0.1", host_port).and_return true instance.send(:port_check, host_ip, host_port) end end end end vagrant-2.0.2/test/unit/vagrant/action/builtin/is_env_set_test.rb000066400000000000000000000012601323370221500252170ustar00rootroot00000000000000require "pathname" require "tmpdir" require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::IsEnvSet do let(:app) { lambda { |env| } } let(:env) { { } } describe "#call" do it "sets result to true if it is set" do env[:bar] = true subject = described_class.new(app, env, :bar) expect(app).to receive(:call).with(env) subject.call(env) expect(env[:result]).to be(true) end it "sets result to false if it isn't set" do subject = described_class.new(app, env, :bar) expect(app).to receive(:call).with(env) subject.call(env) expect(env[:result]).to be(false) end end end vagrant-2.0.2/test/unit/vagrant/action/builtin/is_state_test.rb000066400000000000000000000023341323370221500246770ustar00rootroot00000000000000require "pathname" require "tmpdir" require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::IsState do let(:app) { lambda { |env| } } let(:env) { { machine: machine } } let(:machine) do double("machine").tap do |machine| allow(machine).to receive(:state).and_return(state) end end let(:state) { double("state") } describe "#call" do it "sets result to false if is proper state" do allow(state).to receive(:id).and_return(:foo) subject = described_class.new(app, env, :bar) expect(app).to receive(:call).with(env) subject.call(env) expect(env[:result]).to be(false) end it "sets result to true if is proper state" do allow(state).to receive(:id).and_return(:foo) subject = described_class.new(app, env, :foo) expect(app).to receive(:call).with(env) subject.call(env) expect(env[:result]).to be(true) end it "inverts the result if specified" do allow(state).to receive(:id).and_return(:foo) subject = described_class.new(app, env, :foo, invert: true) expect(app).to receive(:call).with(env) subject.call(env) expect(env[:result]).to be(false) end end end vagrant-2.0.2/test/unit/vagrant/action/builtin/lock_test.rb000066400000000000000000000051171323370221500240160ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::Lock do let(:app) { lambda { |env| } } let(:env) { {} } let(:lock_path) do Dir::Tmpname.create("vagrant-test-lock") {} end let(:options) do { exception: Class.new(StandardError), path: lock_path } end after do File.unlink(lock_path) if File.file?(lock_path) end it "should require a path" do expect { described_class.new(app, env) }. to raise_error(ArgumentError) expect { described_class.new(app, env, path: "foo") }. to raise_error(ArgumentError) expect { described_class.new(app, env, exception: "foo") }. to raise_error(ArgumentError) expect { described_class.new(app, env, path: "bar", exception: "foo") }. to_not raise_error end it "should allow the path to be a proc" do inner_acquire = true app = lambda do |env| File.open(lock_path, "w+") do |f| inner_acquire = f.flock(File::LOCK_EX | File::LOCK_NB) end end options[:path] = lambda { |env| lock_path } instance = described_class.new(app, env, options) instance.call(env) expect(inner_acquire).to eq(false) end it "should allow the exception to be a proc" do exception = options[:exception] options[:exception] = lambda { |env| exception } File.open(lock_path, "w+") do |f| # Acquire lock expect(f.flock(File::LOCK_EX | File::LOCK_NB)).to eq(0) # Test! instance = described_class.new(app, env, options) expect { instance.call(env) }. to raise_error(exception) end end it "should call the middleware with the lock held" do inner_acquire = true app = lambda do |env| File.open(lock_path, "w+") do |f| inner_acquire = f.flock(File::LOCK_EX | File::LOCK_NB) end end instance = described_class.new(app, env, options) instance.call(env) expect(inner_acquire).to eq(false) end it "should raise an exception if the lock is already held" do File.open(lock_path, "w+") do |f| # Acquire lock expect(f.flock(File::LOCK_EX | File::LOCK_NB)).to eq(0) # Test! instance = described_class.new(app, env, options) expect { instance.call(env) }. to raise_error(options[:exception]) end end it "should allow nesting locks within the same middleware sequence" do called = false app = lambda { |env| called = true } inner = described_class.new(app, env, options) outer = described_class.new(inner, env, options) outer.call(env) expect(called).to eq(true) end end vagrant-2.0.2/test/unit/vagrant/action/builtin/message_test.rb000066400000000000000000000013721323370221500245110ustar00rootroot00000000000000require "pathname" require "tmpdir" require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::Message do let(:app) { lambda { |env| } } let(:env) { { ui: ui } } let(:ui) { double("ui") } describe "#call" do it "outputs the given message" do subject = described_class.new(app, env, "foo") expect(ui).to receive(:output).with("foo").ordered expect(app).to receive(:call).with(env).ordered subject.call(env) end it "outputs the given message after the call" do subject = described_class.new(app, env, "foo", post: true) expect(app).to receive(:call).with(env).ordered expect(ui).to receive(:output).with("foo").ordered subject.call(env) end end end vagrant-2.0.2/test/unit/vagrant/action/builtin/mixin_synced_folders_test.rb000066400000000000000000000220461323370221500272750ustar00rootroot00000000000000require "tmpdir" require File.expand_path("../../../../base", __FILE__) require "vagrant/action/builtin/mixin_synced_folders" describe Vagrant::Action::Builtin::MixinSyncedFolders do include_context "synced folder actions" subject do Class.new do extend Vagrant::Action::Builtin::MixinSyncedFolders end end let(:data_dir) { Pathname.new(Dir.mktmpdir("vagrant-test-mixin-synced-folders")) } let(:machine) do double("machine").tap do |machine| allow(machine).to receive(:config).and_return(machine_config) allow(machine).to receive(:data_dir).and_return(data_dir) end end let(:machine_config) do double("machine_config").tap do |top_config| allow(top_config).to receive(:vm).and_return(vm_config) end end let(:vm_config) { double("machine_vm_config", :allowed_synced_folder_types => nil) } after do FileUtils.rm_rf(data_dir) end describe "default_synced_folder_type" do it "returns the usable implementation" do plugins = { "bad" => [impl(false, "bad"), 0], "good" => [impl(true, "good"), 1], "best" => [impl(true, "best"), 5], } result = subject.default_synced_folder_type(machine, plugins) expect(result).to eq("best") end it "filters based on allowed_synced_folder_types" do expect(vm_config).to receive(:allowed_synced_folder_types).and_return(["bad", "good"]) plugins = { "bad" => [impl(false, "bad"), 0], "good" => [impl(true, "good"), 1], "best" => [impl(true, "best"), 5], } result = subject.default_synced_folder_type(machine, plugins) expect(result).to eq("good") end it "reprioritizes based on allowed_synced_folder_types" do plugins = { "bad" => [impl(false, "bad"), 0], "good" => [impl(true, "good"), 1], "same" => [impl(true, "same"), 1], } expect(vm_config).to receive(:allowed_synced_folder_types).and_return(["good", "same"]) result = subject.default_synced_folder_type(machine, plugins) expect(result).to eq("good") expect(vm_config).to receive(:allowed_synced_folder_types).and_return(["same", "good"]) result = subject.default_synced_folder_type(machine, plugins) expect(result).to eq("same") end end describe "impl_opts" do it "should return only relevant keys" do env = { foo_bar: "baz", bar_bar: "nope", foo_baz: "bar", } result = subject.impl_opts("foo", env) expect(result.length).to eq(2) expect(result[:foo_bar]).to eq("baz") expect(result[:foo_baz]).to eq("bar") end end describe "synced_folders" do let(:folders) { {} } let(:plugins) { {} } before do plugins[:default] = [impl(true, "default"), 10] plugins[:nfs] = [impl(true, "nfs"), 5] allow(subject).to receive(:plugins).and_return(plugins) allow(vm_config).to receive(:synced_folders).and_return(folders) end it "should raise exception if bad type is given" do folders["root"] = { type: "bad" } expect { subject.synced_folders(machine) }. to raise_error(StandardError) end it "should return the proper set of folders" do folders["root"] = {} folders["another"] = { type: "" } folders["foo"] = { type: "default" } folders["nfs"] = { type: "nfs" } result = subject.synced_folders(machine) expect(result.length).to eq(2) expect(result[:default]).to eq({ "another" => folders["another"].merge(__vagrantfile: true), "foo" => folders["foo"].merge(__vagrantfile: true), "root" => folders["root"].merge(__vagrantfile: true), }) expect(result[:nfs]).to eq({ "nfs" => folders["nfs"].merge(__vagrantfile: true), }) end it "should return the proper set of folders of a custom config" do folders["root"] = {} folders["another"] = {} other_folders = { "bar" => {} } other = double("config") allow(other).to receive(:synced_folders).and_return(other_folders) result = subject.synced_folders(machine, config: other) expect(result.length).to eq(1) expect(result[:default]).to eq({ "bar" => other_folders["bar"], }) end it "should error if an explicit type is unusable" do plugins[:unusable] = [impl(false, "bad"), 15] folders["root"] = { type: "unusable" } expect { subject.synced_folders(machine) }. to raise_error(RuntimeError) end it "should ignore disabled folders" do folders["root"] = {} folders["foo"] = { disabled: true } result = subject.synced_folders(machine) expect(result.length).to eq(1) expect(result[:default].length).to eq(1) end it "should scope hash override the settings" do folders["root"] = { hostpath: "foo", type: "nfs", nfs__foo: "bar", } result = subject.synced_folders(machine) expect(result[:nfs]["root"][:foo]).to eql("bar") end it "returns {} if cached read with no cache" do result = subject.synced_folders(machine, cached: true) expect(result).to eql({}) end it "should be able to save and retrieve cached versions" do folders["root"] = {} folders["another"] = { type: "" } folders["foo"] = { type: "default" } folders["nfs"] = { type: "nfs" } result = subject.synced_folders(machine) subject.save_synced_folders(machine, result) # Clear the folders so we know its reading from cache old_folders = folders.dup folders.clear result = subject.synced_folders(machine, cached: true) expect(result.length).to eq(2) expect(result[:default]).to eq({ "another" => old_folders["another"].merge(__vagrantfile: true), "foo" => old_folders["foo"].merge(__vagrantfile: true), "root" => old_folders["root"].merge(__vagrantfile: true), }) expect(result[:nfs]).to eq({ "nfs" => old_folders["nfs"].merge(__vagrantfile: true) }) end it "should be able to save and retrieve cached versions" do other_folders = {} other = double("config") allow(other).to receive(:synced_folders).and_return(other_folders) other_folders["foo"] = { type: "default" } result = subject.synced_folders(machine, config: other) subject.save_synced_folders(machine, result) # Clear the folders and set some more folders.clear folders["bar"] = { type: "default" } folders["baz"] = { type: "nfs" } result = subject.synced_folders(machine) subject.save_synced_folders(machine, result, merge: true) # Clear one last time folders.clear # Read them all back result = subject.synced_folders(machine, cached: true) expect(result.length).to eq(2) expect(result[:default]).to eq({ "foo" => { type: "default" }, "bar" => { type: "default", __vagrantfile: true}, }) expect(result[:nfs]).to eq({ "baz" => { type: "nfs", __vagrantfile: true } }) end it "should remove items from the vagrantfile that were removed" do folders["foo"] = { type: "default" } result = subject.synced_folders(machine) subject.save_synced_folders(machine, result) # Clear the folders and set some more folders.clear folders["bar"] = { type: "default" } folders["baz"] = { type: "nfs" } result = subject.synced_folders(machine) subject.save_synced_folders(machine, result, merge: true, vagrantfile: true) # Clear one last time folders.clear # Read them all back result = subject.synced_folders(machine, cached: true) expect(result.length).to eq(2) expect(result[:default]).to eq({ "bar" => { type: "default", __vagrantfile: true}, }) expect(result[:nfs]).to eq({ "baz" => { type: "nfs", __vagrantfile: true } }) end end describe "#synced_folders_diff" do it "sees two equal " do one = { default: { "foo" => {} }, } two = { default: { "foo" => {} }, } expect(subject.synced_folders_diff(one, two)).to be_empty end it "sees modifications" do one = { default: { "foo" => {} }, } two = { default: { "foo" => { hostpath: "foo" } }, } result = subject.synced_folders_diff(one, two) expect(result[:modified]).to_not be_empty end it "sees adding" do one = { default: { "foo" => {} }, } two = { default: { "foo" => {}, "bar" => {}, }, } result = subject.synced_folders_diff(one, two) expect(result[:added]).to_not be_empty expect(result[:removed]).to be_empty expect(result[:modified]).to be_empty end it "sees removing" do one = { default: { "foo" => {} }, } two = { default: {}, } result = subject.synced_folders_diff(one, two) expect(result[:added]).to be_empty expect(result[:removed]).to_not be_empty expect(result[:modified]).to be_empty end end end vagrant-2.0.2/test/unit/vagrant/action/builtin/provision_test.rb000066400000000000000000000160461323370221500251210ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/kernel_v2/config/vm") describe Vagrant::Action::Builtin::Provision do include_context "unit" let(:app) { lambda { |env| } } let(:env) { { machine: machine, ui: ui, hook: hook, provision_ignore_sentinel: false } } let(:hook){ double("hook") } let(:machine) do double("machine").tap do |machine| allow(machine).to receive(:id).and_return('machine-id') allow(machine).to receive(:data_dir).and_return(data_dir) allow(machine).to receive(:config).and_return(machine_config) allow(machine).to receive(:env).and_return(machine_env) end end let(:machine_config) do double("machine_config").tap do |config| allow(config).to receive(:vm).and_return(vm_config) end end let(:data_dir){ temporary_dir } let(:machine_env) do isolated_environment.tap do |i_env| allow(i_env).to receive(:data_dir).and_return(data_dir) allow(i_env).to receive(:lock).and_yield end end let(:vm_config) do double("machine_vm_config").tap do |config| allow(config).to receive(:provisioners).and_return([]) end end let(:ui) do double("ui").tap do |result| allow(result).to receive(:info) end end let(:instance){ described_class.new(app, env) } describe "#call" do context "with no provisioners defined" do it "should process empty set of provisioners" do expect(instance.call(env)).to eq([]) end context "with provisioning disabled" do before{ env[:provision_enabled] = false } after{ env.delete(:provision_enabled) } it "should not process any provisioners" do expect(instance.call(env)).to be_nil end end end context "with single provisioner defined" do let(:provisioner) do prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("spec-test", :shell) prov.config = provisioner_config prov end let(:provisioner_config){ {} } before{ expect(vm_config).to receive(:provisioners).and_return([provisioner]) } it "should call the defined provisioner" do expect(hook).to receive(:call).with(:provisioner_run, anything) instance.call(env) end context "with provisioning disabled" do before{ env[:provision_enabled] = false } after{ env.delete(:provision_enabled) } it "should not process any provisioners" do expect(hook).not_to receive(:call).with(:provisioner_run, anything) expect(instance.call(env)).to be_nil end end context "with provisioner configured to run once" do before{ provisioner.run = :once } it "should run if machine is not provisioned" do expect(hook).to receive(:call).with(:provisioner_run, anything) instance.call(env) end it "should not run if machine is provisioned" do File.open(File.join(data_dir.to_s, "action_provision"), "w") do |file| file.write("1.5:machine-id") end expect(hook).not_to receive(:call).with(:provisioner_run, anything) instance.call(env) end it "should not run if provision types are set and provisioner is not included" do env[:provision_types] = ["other-provisioner", "other-test"] expect(hook).not_to receive(:call).with(:provisioner_run, anything) instance.call(env) end it "should run if provision types are set and include provisioner name" do env[:provision_types] = ["spec-test"] expect(hook).to receive(:call).with(:provisioner_run, anything) instance.call(env) end it "should run if provision types are set and include provisioner type" do env[:provision_types] = [:shell] expect(hook).to receive(:call).with(:provisioner_run, anything) instance.call(env) end end context "with provisioner configured to run always" do before{ provisioner.run = :always } it "should run if machine is not provisioned" do expect(hook).to receive(:call).with(:provisioner_run, anything) instance.call(env) end it "should run if machine is provisioned" do File.open(File.join(data_dir.to_s, "action_provision"), "w") do |file| file.write("1.5:machine-id") end expect(hook).to receive(:call).with(:provisioner_run, anything) instance.call(env) end it "should not run if provision types are set and provisioner is not included" do env[:provision_types] = ["other-provisioner", "other-test"] expect(hook).not_to receive(:call).with(:provisioner_run, anything) instance.call(env) end it "should run if provision types are set and include provisioner name" do env[:provision_types] = ["spec-test"] expect(hook).to receive(:call).with(:provisioner_run, anything) instance.call(env) end it "should run if provision types are set and include provisioner type" do env[:provision_types] = [:shell] expect(hook).to receive(:call).with(:provisioner_run, anything) instance.call(env) end end context "with provisioner configured to never run" do before{ provisioner.run = :never } it "should not run if machine is not provisioned" do expect(hook).not_to receive(:call).with(:provisioner_run, anything) instance.call(env) end it "should not run if machine is provisioned" do File.open(File.join(data_dir.to_s, "action_provision"), "w") do |file| file.write("1.5:machine-id") end expect(hook).not_to receive(:call).with(:provisioner_run, anything) instance.call(env) end it "should not run if provision types are set and provisioner is not included" do env[:provision_types] = ["other-provisioner", "other-test"] expect(hook).not_to receive(:call).with(:provisioner_run, anything) instance.call(env) end it "should run if provision types are set and include provisioner name" do env[:provision_types] = ["spec-test"] expect(hook).to receive(:call).with(:provisioner_run, anything) instance.call(env) end it "should run if provision types are set and include provisioner name and machine is provisioned" do File.open(File.join(data_dir.to_s, "action_provision"), "w") do |file| file.write("1.5:machine-id") end env[:provision_types] = ["spec-test"] expect(hook).to receive(:call).with(:provisioner_run, anything) instance.call(env) end it "should not run if provision types are set and include provisioner type" do env[:provision_types] = [:shell] expect(hook).not_to receive(:call).with(:provisioner_run, anything) instance.call(env) end end end end end vagrant-2.0.2/test/unit/vagrant/action/builtin/provisioner_cleanup_test.rb000066400000000000000000000043041323370221500271510ustar00rootroot00000000000000require "pathname" require "tmpdir" require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::ProvisionerCleanup do let(:app) { lambda { |env| } } let(:env) { { machine: machine, ui: ui } } let(:machine) do double("machine").tap do |machine| allow(machine).to receive(:config).and_return(machine_config) end end let(:machine_config) do double("machine_config").tap do |config| allow(config).to receive(:vm).and_return(vm_config) end end let(:vm_config) { double("machine_vm_config") } let(:ui) do double("ui").tap do |result| allow(result).to receive(:info) end end let(:provisioner) do Class.new(Vagrant.plugin("2", :provisioner)) end before do allow_any_instance_of(described_class).to receive(:provisioner_type_map) .and_return(provisioner => :test_provisioner) allow_any_instance_of(described_class).to receive(:provisioner_instances) .and_return([provisioner]) end describe "initialize with :before" do it "runs cleanup before" do instance = described_class.new(app, env, :before) expect(provisioner).to receive(:cleanup).ordered expect(app).to receive(:call).ordered instance.call(env) end end describe "initialize with :after" do it "runs cleanup after" do instance = described_class.new(app, env, :after) expect(app).to receive(:call).ordered expect(provisioner).to receive(:cleanup).ordered instance.call(env) end end it "only runs cleanup tasks if the subclass defines it" do parent = Class.new do class_variable_set(:@@cleanup, false) def self.called? class_variable_get(:@@cleanup) end def cleanup self.class.class_variable_set(:@@cleanup) end end child = Class.new(parent) allow_any_instance_of(described_class).to receive(:provisioner_type_map) .and_return(child => :test_provisioner) allow_any_instance_of(described_class).to receive(:provisioner_instances) .and_return([child]) expect(parent.called?).to be(false) instance = described_class.new(app, env) instance.call(env) expect(parent.called?).to be(false) end end vagrant-2.0.2/test/unit/vagrant/action/builtin/ssh_exec_test.rb000066400000000000000000000025151323370221500246660ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::SSHExec do let(:app) { lambda { |env| } } let(:env) { { machine: machine } } let(:machine) do result = double("machine") allow(result).to receive(:ssh_info).and_return(machine_ssh_info) result end let(:machine_ssh_info) { {} } let(:ssh_klass) { Vagrant::Util::SSH } before(:each) do # Stub the methods so that even if we test incorrectly, no side # effects actually happen. allow(ssh_klass).to receive(:exec) end it "should raise an exception if SSH is not ready" do not_ready_machine = double("machine") allow(not_ready_machine).to receive(:ssh_info).and_return(nil) env[:machine] = not_ready_machine expect { described_class.new(app, env).call(env) }. to raise_error(Vagrant::Errors::SSHNotReady) end it "should exec with the SSH info in the env if given" do ssh_info = { foo: :bar } expect(ssh_klass).to receive(:exec). with(ssh_info, nil) env[:ssh_info] = ssh_info described_class.new(app, env).call(env) end it "should exec with the options given in `ssh_opts`" do ssh_opts = { foo: :bar } expect(ssh_klass).to receive(:exec). with(machine_ssh_info, ssh_opts) env[:ssh_opts] = ssh_opts described_class.new(app, env).call(env) end end vagrant-2.0.2/test/unit/vagrant/action/builtin/ssh_run_test.rb000066400000000000000000000043051323370221500245450ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::SSHRun do let(:app) { lambda { |env| } } let(:env) { { machine: machine, tty: true } } # SSH configuration information mock let(:ssh) do double("ssh", timeout: 1, host: nil, port: 5986, guest_port: 5986, pty: false, keep_alive: false, insert_key: false, shell: 'bash -l' ) end # Configuration mock let(:config) { double("config", ssh: ssh) } let(:machine) do double("machine", config: config,) end let(:machine_ssh_info) { {} } let(:ssh_klass) { Vagrant::Util::SSH } before(:each) do # Stub the methods so that even if we test incorrectly, no side # effects actually happen. allow(ssh_klass).to receive(:exec) allow(machine).to receive(:ssh_info).and_return(machine_ssh_info) end it "should raise an exception if SSH is not ready" do not_ready_machine = double("machine") allow(not_ready_machine).to receive(:ssh_info).and_return(nil) env[:machine] = not_ready_machine expect { described_class.new(app, env).call(env) }. to raise_error(Vagrant::Errors::SSHNotReady) end it "should exec with the SSH info in the env if given" do ssh_info = { foo: :bar } opts = {:extra_args=>["-t", "bash -l -c 'echo test'"], :subprocess=>true} expect(ssh_klass).to receive(:exec). with(ssh_info, opts) env[:ssh_info] = ssh_info env[:ssh_run_command] = "echo test" described_class.new(app, env).call(env) end it "should exec with the SSH info in the env if given and disable tty" do ssh_info = { foo: :bar } opts = {:extra_args=>["bash -l -c 'echo test'"], :subprocess=>true} env[:tty] = false expect(ssh_klass).to receive(:exec). with(ssh_info, opts) env[:ssh_info] = ssh_info env[:ssh_run_command] = "echo test" described_class.new(app, env).call(env) end it "should exec with the options given in `ssh_opts`" do ssh_opts = { foo: :bar } expect(ssh_klass).to receive(:exec). with(machine_ssh_info, ssh_opts) env[:ssh_opts] = ssh_opts env[:ssh_run_command] = "echo test" described_class.new(app, env).call(env) end end vagrant-2.0.2/test/unit/vagrant/action/builtin/synced_folder_cleanup_test.rb000066400000000000000000000060721323370221500274160ustar00rootroot00000000000000require "pathname" require "tmpdir" require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::SyncedFolderCleanup do include_context "synced folder actions" let(:app) { lambda { |env| } } let(:env) { { machine: machine, ui: ui } } let(:machine) do double("machine").tap do |machine| allow(machine).to receive(:config).and_return(machine_config) end end let(:machine_config) do double("machine_config").tap do |top_config| allow(top_config).to receive(:vm).and_return(vm_config) end end let(:vm_config) { double("machine_vm_config") } let(:ui) do double("ui").tap do |result| allow(result).to receive(:info) end end subject { described_class.new(app, env) } def create_cleanup_tracker Class.new(impl(true, "good")) do class_variable_set(:@@clean, false) def self.clean class_variable_get(:@@clean) end def cleanup(machine, opts) self.class.class_variable_set(:@@clean, true) end end end describe "call" do let(:synced_folders) { {} } let(:plugins) { {} } before do plugins[:default] = [impl(true, "default"), 10] plugins[:nfs] = [impl(true, "nfs"), 5] env[:machine] = Object.new env[:root_path] = Pathname.new(Dir.mktmpdir("vagrant-test-synced-folder-cleanup-call")) allow(subject).to receive(:plugins).and_return(plugins) allow(subject).to receive(:synced_folders).and_return(synced_folders) end after do FileUtils.rm_rf(env[:root_path]) end it "should invoke cleanup" do tracker = create_cleanup_tracker plugins[:tracker] = [tracker, 15] synced_folders["tracker"] = { "root" => { hostpath: "foo", }, "other" => { hostpath: "bar", create: true, } } expect_any_instance_of(tracker).to receive(:cleanup). with(env[:machine], { tracker_foo: :bar }) # Test that the impl-specific opts are passed through env[:tracker_foo] = :bar subject.call(env) end it "should invoke cleanup once per implementation" do trackers = [] (0..2).each do |tracker| trackers << create_cleanup_tracker end plugins[:tracker_0] = [trackers[0], 15] plugins[:tracker_1] = [trackers[1], 15] plugins[:tracker_2] = [trackers[2], 15] synced_folders["tracker_0"] = { "root" => { hostpath: "foo" }, "other" => { hostpath: "bar", create: true } } synced_folders["tracker_1"] = { "root" => { hostpath: "foo" } } synced_folders["tracker_2"] = { "root" => { hostpath: "foo" }, "other" => { hostpath: "bar", create: true }, "another" => { hostpath: "baz" } } subject.call(env) expect(trackers[0].clean).to be(true) expect(trackers[1].clean).to be(true) expect(trackers[2].clean).to be(true) end end end vagrant-2.0.2/test/unit/vagrant/action/builtin/synced_folders_test.rb000066400000000000000000000130711323370221500260670ustar00rootroot00000000000000require "pathname" require "tmpdir" require File.expand_path("../../../../base", __FILE__) require Vagrant.source_root.join("plugins/kernel_v2/config/vm") describe Vagrant::Action::Builtin::SyncedFolders do include_context "unit" include_context "synced folder actions" let(:app) { lambda { |env| } } let(:env) { { machine: machine, ui: ui } } let(:machine) do double("machine").tap do |machine| allow(machine).to receive(:config).and_return(machine_config) end end let(:machine_config) do double("machine_config").tap do |top_config| allow(top_config).to receive(:vm).and_return(vm_config) end end let(:vm_config) { double("machine_vm_config") } let(:ui) do double("ui").tap do |result| allow(result).to receive(:info) end end subject { described_class.new(app, env) } describe "call" do let(:synced_folders) { {} } let(:plugins) { {} } before do plugins[:default] = [impl(true, "default"), 10] plugins[:nfs] = [impl(true, "nfs"), 5] env[:root_path] = Pathname.new(Dir.mktmpdir("vagrant-test-synced-folders-call")) allow(subject).to receive(:plugins).and_return(plugins) allow(subject).to receive(:synced_folders).and_return(synced_folders) allow(subject).to receive(:save_synced_folders) end after do FileUtils.rm_rf(env[:root_path]) end it "should create on the host if specified" do synced_folders["default"] = { "root" => { hostpath: "foo", }, "other" => { hostpath: "bar", create: true, } } subject.call(env) expect(env[:root_path].join("foo")).not_to be_directory expect(env[:root_path].join("bar")).to be_directory end it "doesn't expand the host path if told not to" do called_folders = nil tracker = Class.new(impl(true, "good")) do define_method(:prepare) do |machine, folders, opts| called_folders = folders end end plugins[:tracker] = [tracker, 15] synced_folders["tracker"] = { "root" => { hostpath: "foo", hostpath_exact: true, }, "other" => { hostpath: "/bar", } } subject.call(env) expect(called_folders).to_not be_nil expect(called_folders["root"][:hostpath]).to eq("foo") end it "expands the host path relative to the root path" do called_folders = nil tracker = Class.new(impl(true, "good")) do define_method(:prepare) do |machine, folders, opts| called_folders = folders end end plugins[:tracker] = [tracker, 15] synced_folders["tracker"] = { "root" => { hostpath: "foo", }, "other" => { hostpath: "/bar", } } subject.call(env) expect(called_folders).to_not be_nil expect(called_folders["root"][:hostpath]).to eq( Pathname.new(File.expand_path( called_folders["root"][:hostpath], env[:root_path])).to_s) end it "should invoke prepare then enable" do ids = [] order = [] tracker = Class.new(impl(true, "good")) do define_method(:prepare) do |machine, folders, opts| ids << self.object_id order << :prepare end define_method(:enable) do |machine, folders, opts| ids << self.object_id order << :enable end end plugins[:tracker] = [tracker, 15] synced_folders["tracker"] = { "root" => { hostpath: "foo", }, "other" => { hostpath: "bar", create: true, } } subject.call(env) expect(order).to eq([:prepare, :enable]) expect(ids.length).to eq(2) expect(ids[0]).to eq(ids[1]) end it "syncs custom folders" do ids = [] order = [] tracker = Class.new(impl(true, "good")) do define_method(:prepare) do |machine, folders, opts| ids << self.object_id order << :prepare end define_method(:enable) do |machine, folders, opts| ids << self.object_id order << :enable end end plugins[:tracker] = [tracker, 15] synced_folders["tracker"] = { "root" => { hostpath: "foo", }, "other" => { hostpath: "bar", create: true, } } new_config = double("config") env[:synced_folders_config] = new_config expect(subject).to receive(:synced_folders). with(machine, config: new_config, cached: false). and_return(synced_folders) subject.call(env) expect(order).to eq([:prepare, :enable]) expect(ids.length).to eq(2) expect(ids[0]).to eq(ids[1]) end context "with folders from the machine" do it "removes outdated folders not present in config" do expect(subject).to receive(:save_synced_folders).with( machine, anything, merge: true, vagrantfile: true) subject.call(env) end end context "with custom folders" do before do new_config = double("config") env[:synced_folders_config] = new_config allow(subject).to receive(:synced_folders). with(machine, config: new_config, cached: false). and_return({}) end it "doesn't remove outdated folders not present in config" do expect(subject).to receive(:save_synced_folders).with( machine, anything, merge: true) subject.call(env) end end end end vagrant-2.0.2/test/unit/vagrant/action/hook_test.rb000066400000000000000000000054411323370221500223600ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/action/builder" require "vagrant/action/hook" describe Vagrant::Action::Hook do describe "defaults" do describe '#after_hooks' do subject { super().after_hooks } it { should be_empty } end describe '#before_hooks' do subject { super().before_hooks } it { should be_empty } end describe '#append_hooks' do subject { super().append_hooks } it { should be_empty } end describe '#prepend_hooks' do subject { super().prepend_hooks } it { should be_empty } end end describe "before hooks" do let(:existing) { "foo" } it "should append them" do block = Proc.new {} subject.before(existing, 1) subject.before(existing, 2) subject.before(existing, 3, :arg, &block) expect(subject.before_hooks[existing]).to eq([ [1, [], nil], [2, [], nil], [3, [:arg], block] ]) end end describe "after hooks" do let(:existing) { "foo" } it "should append them" do block = Proc.new {} subject.after(existing, 1) subject.after(existing, 2) subject.after(existing, 3, :arg, &block) expect(subject.after_hooks[existing]).to eq([ [1, [], nil], [2, [], nil], [3, [:arg], block] ]) end end describe "append" do it "should make a list" do block = Proc.new {} subject.append(1) subject.append(2) subject.append(3, :arg, &block) expect(subject.append_hooks).to eq([ [1, [], nil], [2, [], nil], [3, [:arg], block] ]) end end describe "prepend" do it "should make a list" do block = Proc.new {} subject.prepend(1) subject.prepend(2) subject.prepend(3, :arg, &block) expect(subject.prepend_hooks).to eq([ [1, [], nil], [2, [], nil], [3, [:arg], block] ]) end end describe "applying" do let(:builder) { Vagrant::Action::Builder.new } it "should build the proper stack" do subject.prepend("1", 2) subject.append("9") subject.after("1", "2") subject.before("9", "8") subject.apply(builder) expect(builder.stack).to eq([ ["1", [2], nil], ["2", [], nil], ["8", [], nil], ["9", [], nil] ]) end it "should not prepend or append if disabled" do builder.use("3") builder.use("8") subject.prepend("1", 2) subject.append("9") subject.after("3", "4") subject.before("8", "7") subject.apply(builder, no_prepend_or_append: true) expect(builder.stack).to eq([ ["3", [], nil], ["4", [], nil], ["7", [], nil], ["8", [], nil] ]) end end end vagrant-2.0.2/test/unit/vagrant/action/runner_test.rb000066400000000000000000000040441323370221500227270ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) describe Vagrant::Action::Runner do let(:instance) { described_class.new } it "should raise an error if an invalid callable is given" do expect { instance.run(7) }.to raise_error(ArgumentError, /must be a callable/) end it "should be able to use a Proc as a callable" do callable = Proc.new { raise Exception, "BOOM" } expect { instance.run(callable) }.to raise_error(Exception, "BOOM") end it "should be able to use a Method instance as a callable" do klass = Class.new do def action(env) raise Exception, "BANG" end end callable = klass.new.method(:action) expect { instance.run(callable) }.to raise_error(Exception, "BANG") end it "should be able to use a Class as a callable" do callable = Class.new do def initialize(app, env) end def call(env) raise Exception, "BOOM" end end expect { instance.run(callable) }.to raise_error(Exception, "BOOM") end it "should return the resulting environment" do callable = lambda do |env| env[:data] = "value" # Return nil so we can make sure it isn't using this return value nil end result = instance.run(callable) expect(result[:data]).to eq("value") end it "should pass options into hash given to callable" do result = nil callable = lambda do |env| result = env["data"] end instance.run(callable, "data" => "foo") expect(result).to eq("foo") end it "should pass global options into the hash" do result = nil callable = lambda do |env| result = env["data"] end instance = described_class.new("data" => "bar") instance.run(callable) expect(result).to eq("bar") end it "should yield the block passed to the init method to get lazy loaded globals" do result = nil callable = lambda do |env| result = env["data"] end instance = described_class.new { { "data" => "bar" } } instance.run(callable) expect(result).to eq("bar") end end vagrant-2.0.2/test/unit/vagrant/action/warden_test.rb000066400000000000000000000041441323370221500226770ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) describe Vagrant::Action::Warden do let(:data) { { data: [] } } let(:instance) { described_class.new } # This returns a proc that can be used with the builder # that simply appends data to an array in the env. def appender_proc(data) Proc.new { |env| env[:data] << data } end it "calls the actions like normal" do instance = described_class.new([appender_proc(1), appender_proc(2)], data) instance.call(data) expect(data[:data]).to eq([1, 2]) end it "starts a recovery sequence when an exception is raised" do class Action def initialize(app, env) @app = app end def call(env) @app.call(env) end def recover(env) env[:recover] << 1 end end class ActionTwo def initialize(app, env) @app = app end def call(env) @app.call(env) end def recover(env) env[:recover] << 2 end end error_proc = Proc.new { raise "ERROR!" } data = { recover: [] } instance = described_class.new([Action, ActionTwo, error_proc], data) # The error should be raised back up expect { instance.call(data) }. to raise_error(RuntimeError) # Verify the recovery process goes in reverse order expect(data[:recover]).to eq([2, 1]) # Verify that the error is available in the data expect(data["vagrant.error"]).to be_kind_of(RuntimeError) end it "does not do a recovery sequence if SystemExit is raised" do class Action def initialize(app, env) @app = app end def call(env) @app.call(env) end def recover(env) env[:recover] = true end end # Make a proc that just calls "abort" which raises a # SystemExit exception. error_proc = Proc.new { abort } instance = described_class.new([Action, error_proc], data) # The SystemExit should come through expect { instance.call(data) }.to raise_error(SystemExit) # The recover should not have been called expect(data.key?(:recover)).not_to be end end vagrant-2.0.2/test/unit/vagrant/batch_action_test.rb000066400000000000000000000034401323370221500225560ustar00rootroot00000000000000require 'thread' require 'timeout' require File.expand_path("../../base", __FILE__) describe Vagrant::BatchAction do let(:called_actions) { [] } let!(:lock) { Mutex.new } let(:provider_name) { "test" } let(:provider_options) { {} } def new_machine(options) double("machine").tap do |m| allow(m).to receive(:provider_name).and_return(provider_name) allow(m).to receive(:provider_options).and_return(options) allow(m).to receive(:action) do |action, opts| lock.synchronize do called_actions << [m, action, opts] end end end end describe "#run" do let(:machine) { new_machine(provider_options) } let(:machine2) { new_machine(provider_options) } it "should run the actions on the machines in order" do subject.action(machine, "up") subject.action(machine2, "destroy") subject.run expect(called_actions.include?([machine, "up", nil])).to be expect(called_actions.include?([machine2, "destroy", nil])).to be end it "should run the arbitrary methods in order" do called = [] subject.custom(machine) { |m| called << m } subject.custom(machine2) { |m| called << m } subject.run expect(called[0]).to equal(machine) expect(called[1]).to equal(machine2) end it "should handle forks gracefully", :skip_windows do # Doesn't need to be tested on Windows since Windows doesn't # support fork(1) allow(machine).to receive(:action) do |action, opts| pid = fork if !pid # Child process exit end # Parent process, wait for the child to exit Timeout.timeout(1) do Process.waitpid(pid) end end subject.action(machine, "up") subject.run end end end vagrant-2.0.2/test/unit/vagrant/box_collection_test.rb000066400000000000000000000375661323370221500231630ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) require "pathname" require 'tempfile' describe Vagrant::BoxCollection, :skip_windows do include_context "unit" let(:box_class) { Vagrant::Box } let(:environment) { isolated_environment } subject { described_class.new(environment.boxes_dir) } it "should tell us the directory it is using" do expect(subject.directory).to eq(environment.boxes_dir) end describe "#all" do it "should return an empty array when no boxes are there" do expect(subject.all).to eq([]) end it "should return the boxes and their providers" do # Create some boxes environment.box3("foo", "1.0", :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) # Verify some output results = subject.all expect(results.length).to eq(5) expect(results.include?(["foo", "1.0", :virtualbox])).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 it 'does not raise an exception when a file appears in the boxes dir' do Tempfile.open('vagrant-a_file', environment.boxes_dir) do expect { subject.all }.to_not raise_error end end context "with multiple versions of the same box" do before do environment.box3("foo", "1.0", :virtualbox) environment.box3("foo", "2.0.3", :virtualbox) environment.box3("foo", "2.0.4", :virtualbox) environment.box3("foo", "10.3", :virtualbox) environment.box3("foo", "1.0", :vmware) environment.box3("foo", "0.4.3", :vmware) environment.box3("foo", "2.0.1", :vmware) environment.box3("foo", "2.0.2.dev", :vmware) environment.box3("foo", "2.0.2", :vmware) environment.box3("bar", "20161203.2", :ec2) environment.box3("bar", "20161203.2.3", :ec2) environment.box3("bar", "20151102.0.0", :ec2) environment.box3("foo-VAGRANTSLASH-bar", "1.0", :virtualbox) environment.box3("foo-VAGRANTCOLON-colon", "1.0", :virtualbox) end it "should sort boxes by name" do result = subject.all.map(&:first).uniq expect(result).to eq(["bar", "foo", "foo/bar", "foo:colon"]) end it "should group boxes by provider" do expect do current = "" seen_pairs = {} subject.all.each do |box_info| box_key = "#{box_info[0]}_#{box_info[2]}" if current != box_key if seen_pairs[box_key] raise KeyError.new("Box/provider pair already seen. Invalid sort!") else current = box_key seen_pairs[box_key] = true end end end end.not_to raise_error end it "should sort boxes by version" do box_list = subject.all.find_all do |box_info| box_info[0] == "foo" && box_info[2].to_s == "virtualbox" end result = box_list.map{|box_info| box_info[1]} expect(result).to eq([ "1.0", "2.0.3", "2.0.4", "10.3" ]) end it "should sort boxes with pre-release versions" do box_list = subject.all.find_all do |box_info| box_info[0] == "foo" && box_info[2].to_s == "vmware" end result = box_list.map{|box_info| box_info[1]} expect(result).to eq([ "0.4.3", "1.0", "2.0.1", "2.0.2.dev", "2.0.2" ]) end end end describe "#clean" do it "removes the directory if no other versions of the box exists" do # Create a few boxes, immediately destroy them environment.box3("foo", "1.0", :virtualbox) environment.box3("foo", "1.0", :vmware) # Delete them all subject.all.each do |parts| subject.find(parts[0], parts[2], ">= 0").destroy! end # Cleanup subject.clean("foo") # Make sure the whole directory is empty expect(environment.boxes_dir.children).to be_empty end it "doesn't remove the directory if a provider exists" do # Create a few boxes, immediately destroy them environment.box3("foo", "1.0", :virtualbox) environment.box3("foo", "1.0", :vmware) # Delete them all subject.find("foo", :virtualbox, ">= 0").destroy! # Cleanup subject.clean("foo") # Make sure the whole directory is not empty expect(environment.boxes_dir.children).to_not be_empty # Make sure the results still exist results = subject.all expect(results.length).to eq(1) expect(results.include?(["foo", "1.0", :vmware])).to be end it "doesn't remove the directory if a version exists" do # Create a few boxes, immediately destroy them environment.box3("foo", "1.0", :virtualbox) environment.box3("foo", "1.2", :virtualbox) # Delete them all subject.find("foo", :virtualbox, ">= 1.1").destroy! # Cleanup subject.clean("foo") # Make sure the whole directory is not empty expect(environment.boxes_dir.children).to_not be_empty # Make sure the results still exist results = subject.all expect(results.length).to eq(1) expect(results.include?(["foo", "1.0", :virtualbox])).to be end end describe "#find" do it "returns nil if the box does not exist" do expect(subject.find("foo", :i_dont_exist, ">= 0")).to be_nil end it "returns a box if the box does exist" do # Create the "box" environment.box3("foo", "0", :virtualbox) # Actual test result = subject.find("foo", :virtualbox, ">= 0") expect(result).to_not be_nil expect(result).to be_kind_of(box_class) expect(result.name).to eq("foo") expect(result.metadata_url).to be_nil end it "returns a box if the box does exist, with no constraints" do # Create the "box" environment.box3("foo", "0", :virtualbox) # Actual test result = subject.find("foo", :virtualbox, nil) expect(result).to_not be_nil expect(result).to be_kind_of(box_class) expect(result.name).to eq("foo") expect(result.metadata_url).to be_nil end it "sets a metadata URL if it has one" do # Create the "box" environment.box3("foo", "0", :virtualbox, metadata_url: "foourl") # Actual test result = subject.find("foo", :virtualbox, ">= 0") expect(result).to_not be_nil expect(result).to be_kind_of(box_class) expect(result.name).to eq("foo") expect(result.metadata_url).to eq("foourl") end it "sets the metadata URL to an authenticated URL if it has one" do hook = double("hook") subject = described_class.new(environment.boxes_dir, hook: hook) # Create the "box" environment.box3("foo", "0", :virtualbox, metadata_url: "foourl") expect(hook).to receive(:call).with(any_args) { |name, env| expect(name).to eq(:authenticate_box_url) expect(env[:box_urls]).to eq(["foourl"]) true }.and_return(box_urls: ["bar"]) # Actual test result = subject.find("foo", :virtualbox, ">= 0") expect(result).to_not be_nil expect(result).to be_kind_of(box_class) expect(result.name).to eq("foo") expect(result.metadata_url).to eq("bar") end it "returns latest version matching constraint" do # Create the "box" environment.box3("foo", "1.0", :virtualbox) environment.box3("foo", "1.5", :virtualbox) # Actual test result = subject.find("foo", :virtualbox, ">= 0") expect(result).to_not be_nil expect(result).to be_kind_of(box_class) expect(result.name).to eq("foo") expect(result.version).to eq("1.5") end it "can satisfy complex constraints" do # Create the "box" environment.box3("foo", "0.1", :virtualbox) environment.box3("foo", "1.0", :virtualbox) environment.box3("foo", "2.1", :virtualbox) # Actual test result = subject.find("foo", :virtualbox, ">= 0.9, < 1.5") expect(result).to_not be_nil expect(result).to be_kind_of(box_class) expect(result.name).to eq("foo") expect(result.version).to eq("1.0") end it "handles prerelease versions" do # Create the "box" environment.box3("foo", "0.1.0-alpha.1", :virtualbox) environment.box3("foo", "0.1.0-alpha.2", :virtualbox) # Actual test result = subject.find("foo", :virtualbox, ">= 0") expect(result).to_not be_nil expect(result).to be_kind_of(box_class) expect(result.name).to eq("foo") expect(result.version).to eq("0.1.0-alpha.2") end it "returns nil if a box's constraints can't be satisfied" do # Create the "box" environment.box3("foo", "0.1", :virtualbox) environment.box3("foo", "1.0", :virtualbox) environment.box3("foo", "2.1", :virtualbox) # Actual test result = subject.find("foo", :virtualbox, "> 1.0, < 1.5") expect(result).to be_nil end context "with multiple versions of the same box" do before do environment.box3("foo", "1.0", :virtualbox) environment.box3("foo", "2.0.3", :virtualbox) environment.box3("foo", "2.0.4", :virtualbox) environment.box3("foo", "10.3", :virtualbox) environment.box3("foo", "1.0", :vmware) environment.box3("foo", "0.4.3", :vmware) environment.box3("foo", "2.0.1", :vmware) environment.box3("foo", "2.0.2.dev", :vmware) environment.box3("foo", "2.0.2", :vmware) environment.box3("bar", "20161203.2", :ec2) environment.box3("bar", "20161203.2.3", :ec2) environment.box3("bar", "20151102.0.0", :ec2) environment.box3("foo-VAGRANTSLASH-bar", "1.0", :virtualbox) environment.box3("foo-VAGRANTCOLON-colon", "1.0", :virtualbox) end it "should return expected latest version" do result = subject.find("foo", :virtualbox, "> 2, < 3") expect(result.version).to eq("2.0.4") end it "should sort boxes with pre-release versions" do result = subject.find("foo", :vmware, "> 2, < 3") expect(result.version).to eq("2.0.2") end end end describe "#add" do it "should add a valid box to the system" do box_path = environment.box2_file(:virtualbox) # Add the box box = subject.add(box_path, "foo", "1.0", providers: :virtualbox) expect(box).to be_kind_of(box_class) expect(box.name).to eq("foo") expect(box.provider).to eq(:virtualbox) # Verify we can find it as well expect(subject.find("foo", :virtualbox, "1.0")).to_not be_nil end it "should add a box with a name with '/' in it" do box_path = environment.box2_file(:virtualbox) # Add the box box = subject.add(box_path, "foo/bar", "1.0") expect(box).to be_kind_of(box_class) expect(box.name).to eq("foo/bar") expect(box.provider).to eq(:virtualbox) # Verify we can find it as well expect(subject.find("foo/bar", :virtualbox, "1.0")).to_not be_nil end it "should add a box without specifying a provider" do box_path = environment.box2_file(:vmware) # Add the box box = subject.add(box_path, "foo", "1.0") expect(box).to be_kind_of(box_class) expect(box.name).to eq("foo") expect(box.provider).to eq(:vmware) end it "should store a metadata URL" do box_path = environment.box2_file(:virtualbox) subject.add( box_path, "foo", "1.0", metadata_url: "bar") box = subject.find("foo", :virtualbox, "1.0") expect(box.metadata_url).to eq("bar") end it "should add a V1 box" do # Create a V1 box. box_path = environment.box1_file # Add the box box = subject.add(box_path, "foo", "1.0") expect(box).to be_kind_of(box_class) expect(box.name).to eq("foo") expect(box.provider).to eq(:virtualbox) end it "should raise an exception if the box already exists" do prev_box_name = "foo" prev_box_provider = :virtualbox prev_box_version = "1.0" # Create the box we're adding environment.box3(prev_box_name, "1.0", prev_box_provider) # Attempt to add the box with the same name box_path = environment.box2_file(prev_box_provider) expect { subject.add(box_path, prev_box_name, prev_box_version, providers: prev_box_provider) }.to raise_error(Vagrant::Errors::BoxAlreadyExists) end it "should replace the box if force is specified" do prev_box_name = "foo" prev_box_provider = :vmware prev_box_version = "1.0" # Setup the environment with the box pre-added environment.box3(prev_box_name, prev_box_version, prev_box_provider) # Attempt to add the box with the same name box_path = environment.box2_file(prev_box_provider, metadata: { "replaced" => "yes" }) box = subject.add(box_path, prev_box_name, prev_box_version, force: true) expect(box.metadata["replaced"]).to eq("yes") end it "should raise an exception if the box already exists and no provider is given" do # Create some box file box_name = "foo" box_path = environment.box2_file(:vmware) # Add it once, successfully expect { subject.add(box_path, box_name, "1.0") }.to_not raise_error # Add it again, and fail! expect { subject.add(box_path, box_name, "1.0") }. to raise_error(Vagrant::Errors::BoxAlreadyExists) end it "should raise an exception and not add the box if the provider doesn't match" do box_name = "foo" good_provider = :virtualbox bad_provider = :vmware # Create a VirtualBox box file box_path = environment.box2_file(good_provider) # Add the box but with an invalid provider, verify we get the proper # error. expect { subject.add(box_path, box_name, "1.0", providers: bad_provider) }. to raise_error(Vagrant::Errors::BoxProviderDoesntMatch) # Verify the box doesn't exist expect(subject.find(box_name, bad_provider, "1.0")).to be_nil end it "should raise an exception if you add an invalid box file" do # Tar Header information CHECKSUM_OFFSET = 148 CHECKSUM_LENGTH = 8 Tempfile.open(['vagrant-testing', '.tar']) do |f| f.binmode # Corrupt the tar by writing over the checksum field f.seek(CHECKSUM_OFFSET) f.write("\0"*CHECKSUM_LENGTH) f.close expect { subject.add(f.path, "foo", "1.0") }. to raise_error(Vagrant::Errors::BoxUnpackageFailure) end end end describe "#upgrade_v1_1_v1_5" do let(:boxes_dir) { environment.boxes_dir } before do # Create all the various box directories @foo_path = environment.box2("foo", "virtualbox") @vbox_path = environment.box2("precise64", "virtualbox") @vmware_path = environment.box2("precise64", "vmware") @v1_path = environment.box("v1box") end it "upgrades the boxes" do subject.upgrade_v1_1_v1_5 # The old paths should not exist anymore expect(@foo_path).to_not exist expect(@vbox_path).to_not exist expect(@vmware_path).to_not exist expect(@v1_path.join("box.ovf")).to_not exist # New paths should exist foo_path = boxes_dir.join("foo", "0", "virtualbox") vbox_path = boxes_dir.join("precise64", "0", "virtualbox") vmware_path = boxes_dir.join("precise64", "0", "vmware") v1_path = boxes_dir.join("v1box", "0", "virtualbox") expect(foo_path).to exist expect(vbox_path).to exist expect(vmware_path).to exist expect(v1_path).to exist end end end vagrant-2.0.2/test/unit/vagrant/box_metadata_test.rb000066400000000000000000000105661323370221500225770ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) require "vagrant/box_metadata" describe Vagrant::BoxMetadata do include_context "unit" let(:raw) do <<-RAW { "name": "foo", "description": "bar", "versions": [ { "version": "1.0.0", "providers": [ { "name": "virtualbox" }, { "name": "vmware" } ] }, { "version": "1.1.5", "providers": [ { "name": "virtualbox" } ] }, { "version": "1.1.0", "providers": [ { "name": "virtualbox" }, { "name": "vmware" } ] } ] } RAW end subject { described_class.new(raw) } describe '#name' do subject { super().name } it { should eq("foo") } end describe '#description' do subject { super().description } it { should eq("bar") } end context "with poorly formatted JSON" do let(:raw) { <<-RAW { "name": "foo", } RAW } it "raises an exception" do expect { subject }. to raise_error(Vagrant::Errors::BoxMetadataMalformed) end end context "with poorly formatted version" do let(:raw) { <<-RAW { "name": "foo", "versions": [ { "version": "I AM NOT VALID" } ] } RAW } it "raises an exception" do expect { subject }. to raise_error(Vagrant::Errors::BoxMetadataMalformedVersion) end end describe "#version" do it "matches an exact version" do result = subject.version("1.0.0") expect(result).to_not be_nil expect(result).to be_kind_of(Vagrant::BoxMetadata::Version) expect(result.version).to eq("1.0.0") end it "matches a constraint with latest matching version" do result = subject.version(">= 1.0") expect(result).to_not be_nil expect(result).to be_kind_of(Vagrant::BoxMetadata::Version) expect(result.version).to eq("1.1.5") end it "matches complex constraints" do result = subject.version(">= 0.9, ~> 1.0.0") expect(result).to_not be_nil expect(result).to be_kind_of(Vagrant::BoxMetadata::Version) expect(result.version).to eq("1.0.0") end it "matches the constraint that has the given provider" do result = subject.version(">= 0", provider: :vmware) expect(result).to_not be_nil expect(result).to be_kind_of(Vagrant::BoxMetadata::Version) expect(result.version).to eq("1.1.0") end end describe "#versions" do it "returns the versions it contained" do expect(subject.versions).to eq( ["1.0.0", "1.1.0", "1.1.5"]) end end end describe Vagrant::BoxMetadata::Version do let(:raw) { {} } subject { described_class.new(raw) } before do raw["providers"] = [ { "name" => "virtualbox", }, { "name" => "vmware", } ] end describe "#version" do it "is the version in the raw data" do v = "1.0" raw["version"] = v expect(subject.version).to eq(v) end end describe "#provider" do it "returns nil if a provider isn't supported" do expect(subject.provider("foo")).to be_nil end it "returns the provider specified" do result = subject.provider("virtualbox") expect(result).to_not be_nil expect(result).to be_kind_of(Vagrant::BoxMetadata::Provider) end end describe "#providers" do it "returns the providers available" do expect(subject.providers.sort).to eq( [:virtualbox, :vmware]) end end end describe Vagrant::BoxMetadata::Provider do let(:raw) { {} } subject { described_class.new(raw) } describe "#name" do it "is the name specified" do raw["name"] = "foo" expect(subject.name).to eq("foo") end end describe "#url" do it "is the URL specified" do raw["url"] = "bar" expect(subject.url).to eq("bar") end end describe "#checksum and #checksum_type" do it "is set properly" do raw["checksum"] = "foo" raw["checksum_type"] = "bar" expect(subject.checksum).to eq("foo") expect(subject.checksum_type).to eq("bar") end it "is nil if not set" do expect(subject.checksum).to be_nil expect(subject.checksum_type).to be_nil end end end vagrant-2.0.2/test/unit/vagrant/box_test.rb000066400000000000000000000222621323370221500207330ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) require "pathname" require "stringio" require "tempfile" require "vagrant/box_metadata" describe Vagrant::Box, :skip_windows do include_context "unit" let(:environment) { isolated_environment } let(:box_collection) { Vagrant::BoxCollection.new(environment.boxes_dir) } let(:name) { "foo" } let(:provider) { :virtualbox } let(:version) { "1.0" } let(:directory) { environment.box3("foo", "1.0", :virtualbox) } subject { described_class.new(name, provider, version, directory) } describe '#metadata_url' do subject { super().metadata_url } it { should be_nil } end it "provides the name" do expect(subject.name).to eq(name) end it "provides the provider" do expect(subject.provider).to eq(provider) end it "provides the directory" do expect(subject.directory).to eq(directory) end it "provides the metadata associated with a box" do data = { "foo" => "bar" } # Write the metadata directory.join("metadata.json").open("w") do |f| f.write(JSON.generate(data)) end # Verify the metadata expect(subject.metadata).to eq(data) end context "with a metadata URL" do subject do described_class.new( name, provider, version, directory, metadata_url: "foo") end describe '#metadata_url' do subject { super().metadata_url } it { should eq("foo") } end end context "with a corrupt metadata file" do before do directory.join("metadata.json").open("w") do |f| f.write("") end end it "should raise an exception" do expect { subject }. to raise_error(Vagrant::Errors::BoxMetadataCorrupted) end end context "without a metadata file" do before :each do directory.join("metadata.json").delete end it "should raise an exception" do expect { subject }. to raise_error(Vagrant::Errors::BoxMetadataFileNotFound) end end context "#has_update?" do subject do described_class.new( name, provider, version, directory, metadata_url: "foo") end it "raises an exception if no metadata_url is set" do subject = described_class.new( name, provider, version, directory) expect { subject.has_update?("> 0") }. to raise_error(Vagrant::Errors::BoxUpdateNoMetadata) end it "returns nil if there is no update" do metadata = Vagrant::BoxMetadata.new(StringIO.new(<<-RAW)) { "name": "foo", "versions": [ { "version": "1.0" } ] } RAW allow(subject).to receive(:load_metadata).and_return(metadata) expect(subject.has_update?).to be_nil end it "returns the updated box info if there is an update available" do metadata = Vagrant::BoxMetadata.new(StringIO.new(<<-RAW)) { "name": "foo", "versions": [ { "version": "1.0" }, { "version": "1.1", "providers": [ { "name": "virtualbox", "url": "bar" } ] } ] } RAW allow(subject).to receive(:load_metadata).and_return(metadata) result = subject.has_update? expect(result).to_not be_nil expect(result[0]).to be_kind_of(Vagrant::BoxMetadata) expect(result[1]).to be_kind_of(Vagrant::BoxMetadata::Version) expect(result[2]).to be_kind_of(Vagrant::BoxMetadata::Provider) expect(result[0].name).to eq("foo") expect(result[1].version).to eq("1.1") expect(result[2].url).to eq("bar") end it "returns the updated box info within constraints" do metadata = Vagrant::BoxMetadata.new(StringIO.new(<<-RAW)) { "name": "foo", "versions": [ { "version": "1.0" }, { "version": "1.1", "providers": [ { "name": "virtualbox", "url": "bar" } ] }, { "version": "1.4", "providers": [ { "name": "virtualbox", "url": "bar" } ] } ] } RAW allow(subject).to receive(:load_metadata).and_return(metadata) result = subject.has_update?(">= 1.1, < 1.4") expect(result).to_not be_nil expect(result[0]).to be_kind_of(Vagrant::BoxMetadata) expect(result[1]).to be_kind_of(Vagrant::BoxMetadata::Version) expect(result[2]).to be_kind_of(Vagrant::BoxMetadata::Provider) expect(result[0].name).to eq("foo") expect(result[1].version).to eq("1.1") expect(result[2].url).to eq("bar") end end context "#in_use?" do let(:index) { [] } def new_entry(name, provider, version) Vagrant::MachineIndex::Entry.new.tap do |entry| entry.extra_data["box"] = { "name" => name, "provider" => provider, "version" => version, } end end it "returns nil if the index has no matching entries" do index << new_entry("foo", "bar", "1.0") index << new_entry("foo", "baz", "1.2") expect(subject).to_not be_in_use(index) end it "returns matching entries if they exist" do matching = new_entry(name, provider.to_s, version) index << new_entry("foo", "bar", "1.0") index << matching index << new_entry("foo", "baz", "1.2") expect(subject.in_use?(index)).to eq([matching]) end end context "#load_metadata" do let(:metadata_url) do Tempfile.new("vagrant-test-box-test").tap do |f| f.write(<<-RAW) { "name": "foo", "description": "bar" } RAW f.close end end subject do described_class.new( name, provider, version, directory, metadata_url: metadata_url.path) end after do metadata_url.unlink end it "loads the url and returns the data" do result = subject.load_metadata expect(result.name).to eq("foo") expect(result.description).to eq("bar") end it "raises an error if the download failed" do dl = double("downloader") allow(Vagrant::Util::Downloader).to receive(:new).and_return(dl) expect(dl).to receive(:download!).and_raise( Vagrant::Errors::DownloaderError.new(message: "foo")) expect { subject.load_metadata }. to raise_error(Vagrant::Errors::BoxMetadataDownloadError) end end describe "destroying" do it "should destroy an existing box" do # Verify that our "box" exists expect(directory.exist?).to be # Destroy it expect(subject.destroy!).to be # Verify that it is "destroyed" expect(directory.exist?).not_to be end it "should not error destroying a non-existent box" do # Get the subject so that it is instantiated box = subject # Delete the directory directory.rmtree # Destroy it expect(box.destroy!).to be end end describe "repackaging" do let(:scratch) { Dir.mktmpdir("vagrant-test-box-repackaging") } let(:box_output_path) { File.join(scratch, "package.box") } after do FileUtils.rm_rf(scratch) end it "should repackage the box" do test_file_contents = "hello, world!" # Put a file in the box directory to verify it is packaged properly # later. directory.join("test_file").open("w") do |f| f.write(test_file_contents) end # Repackage our box to some temporary directory expect(subject.repackage(box_output_path)).to be(true) # Let's now add this box again under a different name, and then # verify that we get the proper result back. new_box = box_collection.add(box_output_path, "foo2", "1.0") expect(new_box.directory.join("test_file").read).to eq(test_file_contents) end end describe "comparison and ordering" do it "should be equal if the name, provider, version match" do a = described_class.new("a", :foo, "1.0", directory) b = described_class.new("a", :foo, "1.0", directory) expect(a).to eq(b) end it "should not be equal if name doesn't match" do a = described_class.new("a", :foo, "1.0", directory) b = described_class.new("b", :foo, "1.0", directory) expect(a).to_not eq(b) end it "should not be equal if provider doesn't match" do a = described_class.new("a", :foo, "1.0", directory) b = described_class.new("a", :bar, "1.0", directory) expect(a).to_not eq(b) end it "should not be equal if version doesn't match" do a = described_class.new("a", :foo, "1.0", directory) b = described_class.new("a", :foo, "1.1", directory) expect(a).to_not eq(b) end it "should sort them in order of name, version, provider" do a = described_class.new("a", :foo, "1.0", directory) b = described_class.new("a", :foo2, "1.0", directory) c = described_class.new("a", :foo2, "1.1", directory) d = described_class.new("b", :foo2, "1.0", directory) expect([d, c, a, b].sort).to eq([a, b, c, d]) end end end vagrant-2.0.2/test/unit/vagrant/capability_host_test.rb000066400000000000000000000102671323370221500233230ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) require "vagrant/capability_host" describe Vagrant::CapabilityHost do include_context "capability_helpers" subject do Class.new do extend Vagrant::CapabilityHost end end describe "#initialize_capabilities! and #capability_host_chain" do it "raises an error if an explicit host is not found" do expect { subject.initialize_capabilities!(:foo, {}, {}) }. to raise_error(Vagrant::Errors::CapabilityHostExplicitNotDetected) end it "raises an error if a host can't be detected" do hosts = { foo: [detect_class(false), nil], bar: [detect_class(false), :foo], } expect { subject.initialize_capabilities!(nil, hosts, {}) }. to raise_error(Vagrant::Errors::CapabilityHostNotDetected) end it "passes on extra args to the detect method" do klass = Class.new do def detect?(*args) raise "detect: #{args.inspect}" end end hosts = { foo: [klass, nil], } expect { subject.initialize_capabilities!(nil, hosts, {}, 1, 2) }. to raise_error(RuntimeError, "detect: [1, 2]") end it "detects a basic child" do hosts = { foo: [detect_class(false), nil], bar: [detect_class(true), nil], baz: [detect_class(false), nil], } subject.initialize_capabilities!(nil, hosts, {}) chain = subject.capability_host_chain expect(chain.length).to eql(1) expect(chain[0][0]).to eql(:bar) end it "detects the host with the most parents (deepest) first" do hosts = { foo: [detect_class(true), nil], bar: [detect_class(true), :foo], baz: [detect_class(true), :bar], foo2: [detect_class(true), nil], bar2: [detect_class(true), :foo2], } subject.initialize_capabilities!(nil, hosts, {}) chain = subject.capability_host_chain expect(chain.length).to eql(3) expect(chain.map(&:first)).to eql([:baz, :bar, :foo]) end it "detects a forced host" do hosts = { foo: [detect_class(false), nil], bar: [detect_class(false), nil], baz: [detect_class(false), nil], } subject.initialize_capabilities!(:bar, hosts, {}) chain = subject.capability_host_chain expect(chain.length).to eql(1) expect(chain[0][0]).to eql(:bar) end end describe "#capability?" do before do host = nil hosts = { foo: [detect_class(true), nil], bar: [detect_class(true), :foo], } caps = { foo: { parent: Class.new }, bar: { self: Class.new }, } subject.initialize_capabilities!(host, hosts, caps) end it "does not have a non-existent capability" do expect(subject.capability?(:foo)).to be(false) end it "has capabilities of itself" do expect(subject.capability?(:self)).to be(true) end it "has capabilities of parent" do expect(subject.capability?(:parent)).to be(true) end end describe "capability" do let(:caps) { {} } def init host = nil hosts = { foo: [detect_class(true), nil], bar: [detect_class(true), :foo], } subject.initialize_capabilities!(host, hosts, caps) end it "executes the capability" do caps[:bar] = { test: cap_instance(:test) } init expect { subject.capability(:test) }. to raise_error(RuntimeError, "cap: test []") end it "executes the capability with arguments" do caps[:bar] = { test: cap_instance(:test) } init expect { subject.capability(:test, 1) }. to raise_error(RuntimeError, "cap: test [1]") end it "raises an exception if the capability doesn't exist" do init expect { subject.capability(:what_is_this_i_dont_even) }. to raise_error(Vagrant::Errors::CapabilityNotFound) end it "raises an exception if the method doesn't exist on the module" do caps[:bar] = { test_is_corrupt: cap_instance(:test_is_corrupt, corrupt: true) } init expect { subject.capability(:test_is_corrupt) }. to raise_error(Vagrant::Errors::CapabilityInvalid) end end end vagrant-2.0.2/test/unit/vagrant/cli_test.rb000066400000000000000000000045351323370221500207150ustar00rootroot00000000000000require_relative "../base" require "vagrant/cli" require "vagrant/util" describe Vagrant::CLI do include_context "unit" include_context "command plugin helpers" let(:commands) { {} } let(:iso_env) { isolated_environment } let(:env) { iso_env.create_vagrant_env } let(:checkpoint) { double("checkpoint") } before do allow(Vagrant.plugin("2").manager).to receive(:commands).and_return(commands) allow(Vagrant::Util::CheckpointClient).to receive(:instance).and_return(checkpoint) allow(checkpoint).to receive(:setup).and_return(checkpoint) allow(checkpoint).to receive(:check) allow(checkpoint).to receive(:display) end describe "#initialize" do it "should setup checkpoint" do expect(checkpoint).to receive(:check) described_class.new(["destroy"], env) end end describe "#execute" do 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 expect(subject.execute).to eql(1) end it "invokes command and returns its exit status if the command is valid" do commands[:destroy] = [command_lambda("destroy", 42), {}] subject = described_class.new(["destroy"], env) expect(subject).not_to receive(:help) expect(subject.execute).to eql(42) end it "returns exit code 1 if interrupted" do commands[:destroy] = [command_lambda("destroy", 42, exception: Interrupt), {}] subject = described_class.new(["destroy"], env) expect(subject.execute).to eql(1) end it "displays any checkpoint information" do commands[:destroy] = [command_lambda("destroy", 42), {}] expect(checkpoint).to receive(:display) described_class.new(["destroy"], env).execute end end describe "#help" do subject { described_class.new([], env) } it "includes all primary subcommands" do commands[:foo] = [command_lambda("foo", 0), { primary: true }] commands[:bar] = [command_lambda("bar", 0), { primary: true }] commands[:baz] = [command_lambda("baz", 0), { primary: false }] expect(env.ui).to receive(:info).with(any_args) { |message, opts| expect(message).to include("foo") expect(message).to include("bar") expect(message.include?("baz")).to be(false) } subject.help end end end vagrant-2.0.2/test/unit/vagrant/config/000077500000000000000000000000001323370221500200205ustar00rootroot00000000000000vagrant-2.0.2/test/unit/vagrant/config/loader_test.rb000066400000000000000000000152741323370221500226630ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/registry" describe Vagrant::Config::Loader do include_context "unit" # This is the current version of configuration for the tests. let(:current_version) { version_order.last } # This is just a dummy implementation of a configuraiton loader which # simply acts on hashes. let(:test_loader) do Class.new(Vagrant::Config::VersionBase) do def self.init {} end def self.load(proc) init.tap do |obj| proc.call(obj) end end def self.merge(old, new) old.merge(new) {|key, oldval, newval| oldval.concat(newval)} end end end let(:versions) do Vagrant::Registry.new.tap do |r| r.register("1") { test_loader } end end let(:version_order) { ["1"] } let(:instance) { described_class.new(versions, version_order) } describe "#set" do context "with an object that cannot be inspected" do # This represents the euro symbol in UTF-16LE. pack("c*") returns an ASCII # string and so we have to force the encoding UTF_16LE_STRING_THAT_CANNOT_BE_DOWNCAST_TO_ASCII = [0x20, 0xAC].pack("c*").force_encoding("UTF-16LE") let(:klass_with_bad_inspect_string) do Class.new do def inspect UTF_16LE_STRING_THAT_CANNOT_BE_DOWNCAST_TO_ASCII end end end let(:test_source) { Class.new do def initialize(collaborator) @foo = collaborator.new end end.new(klass_with_bad_inspect_string) } it "does not raise the ascii encoding exception" do expect { instance.set(:arbitrary, test_source) }.to raise_error(ArgumentError, /Unknown configuration source/) end end end describe "basic loading" do it "should ignore non-existent load order keys" do instance.load([:foo]) end it "should load and return the configuration" do proc = Proc.new do |config| config[:foo] = "yep" end instance.set(:proc, [[current_version, proc]]) config, warnings, errors = instance.load([:proc]) expect(config[:foo]).to eq("yep") expect(warnings).to eq([]) expect(errors).to eq([]) end it "should throw a NameError exception if invalid or undefined variable is used" do vagrantfile = <<-VF Vagrant.configure("2") do |config| config.ssh.port = variable end VF instance.set(:foo, temporary_file(vagrantfile)) expect { instance.load([:foo]) }.to raise_error(Vagrant::Errors::VagrantfileNameError, /invalid or undefined variable/) end end describe "finalization" do it "should finalize the configuration" do # Create the finalize method on our loader def test_loader.finalize(obj) obj[:finalized] = true obj end # Basic configuration proc proc = lambda do |config| config[:foo] = "yep" end # Run the actual configuration and assert that we get the proper result instance.set(:proc, [[current_version, proc]]) config, _ = instance.load([:proc]) expect(config[:foo]).to eq("yep") expect(config[:finalized]).to eq(true) end end describe "upgrading" do it "should do an upgrade to the latest version" do test_loader_v2 = Class.new(test_loader) do def self.upgrade(old) new = old.dup new[:v2] = true [new, [], []] end end versions.register("2") { test_loader_v2 } version_order << "2" # Load a version 1 proc, and verify it is upgraded to version 2 proc = lambda { |config| config[:foo] = "yep" } instance.set(:proc, [["1", proc]]) config, _ = instance.load([:proc]) expect(config[:foo]).to eq("yep") expect(config[:v2]).to eq(true) end it "should keep track of warnings and errors" do test_loader_v2 = Class.new(test_loader) do def self.upgrade(old) new = old.dup new[:v2] = true [new, ["foo!"], ["bar!"]] end end versions.register("2") { test_loader_v2 } version_order << "2" # Load a version 1 proc, and verify it is upgraded to version 2 proc = lambda { |config| config[:foo] = "yep" } instance.set(:proc, [["1", proc]]) config, warnings, errors = instance.load([:proc]) expect(config[:foo]).to eq("yep") expect(config[:v2]).to eq(true) expect(warnings).to eq(["foo!"]) expect(errors).to eq(["bar!"]) end end describe "loading edge cases" do it "should only run the same proc once" do count = 0 proc = Proc.new do |config| config[:foo] = "yep" count += 1 end instance.set(:proc, [[current_version, proc]]) 5.times do result, _ = instance.load([:proc]) # Verify the config result expect(result[:foo]).to eq("yep") # Verify the count is only one expect(count).to eq(1) end end it "should discard duplicate configs if :home and :root are the same" do proc = Proc.new do |config| config[:foo] = ["yep"] end order = [:root, :home] instance.set(:root, [[current_version, proc]]) instance.set(:home, [[current_version, proc]]) result, warnings, errors = instance.load(order) # Verify the config result expect(result[:foo]).to eq(["yep"]) expect(result[:foo].size).to eq(1) expect(warnings).to eq([]) expect(errors).to eq([]) end it "should only load configuration files once" do $_config_data = 0 # We test both setting a file multiple times as well as multiple # loads, since both should not cache the data. file = temporary_file("$_config_data += 1") 5.times { instance.set(:file, file) } 5.times { instance.load([:file]) } expect($_config_data).to eq(1) end it "should not clear the cache if setting to the same value multiple times" do $_config_data = 0 file = temporary_file("$_config_data += 1") instance.set(:proc, file) 5.times { instance.load([:proc]) } instance.set(:proc, file) 5.times { instance.load([:proc]) } expect($_config_data).to eq(1) end it "should raise proper error if there is a syntax error in a Vagrantfile" do expect { instance.set(:file, temporary_file("Vagrant:^Config")) }. to raise_exception(Vagrant::Errors::VagrantfileSyntaxError) end it "should raise a proper error if there is a problem with the Vagrantfile" do expect { instance.set(:file, temporary_file("foo")) }. to raise_exception(Vagrant::Errors::VagrantfileLoadError) end end end vagrant-2.0.2/test/unit/vagrant/config/v1/000077500000000000000000000000001323370221500203465ustar00rootroot00000000000000vagrant-2.0.2/test/unit/vagrant/config/v1/dummy_config_test.rb000066400000000000000000000011371323370221500244140ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Config::V1::DummyConfig do it "should allow attribute setting" do expect { subject.foo = :bar }. to_not raise_error end it "should allow method calls that return more DummyConfigs" do expect(subject.foo).to be_kind_of(described_class) end it "should allow hash access" do expect { subject[:foo] }. to_not raise_error expect(subject[:foo]).to be_kind_of(described_class) end it "should allow setting hash values" do expect { subject[:foo] = :bar }. to_not raise_error end end vagrant-2.0.2/test/unit/vagrant/config/v1/loader_test.rb000066400000000000000000000076671323370221500232200ustar00rootroot00000000000000require "ostruct" require File.expand_path("../../../../base", __FILE__) describe Vagrant::Config::V1::Loader do include_context "unit" before(:each) do # Force the V1 loader to believe that we are in V1 stub_const("Vagrant::Config::CURRENT_VERSION", "1") end describe "empty" do it "returns an empty configuration object" do result = described_class.init expect(result).to be_kind_of(Vagrant::Config::V1::Root) end it "returns an object with all configuration keys loaded if V1" do # Make sure we're version 1 stub_const("Vagrant::Config::CURRENT_VERSION", "1") # Register some config classes. register_plugin("1") do |p| p.config("foo") { OpenStruct } p.config("bar", true) { OpenStruct } end # Test that we have all keys result = described_class.init expect(result.foo).to be_kind_of(OpenStruct) expect(result.bar).to be_kind_of(OpenStruct) end it "returns only upgradable config objects if not V1" do # Make sure we're NOT version 1 stub_const("Vagrant::Config::CURRENT_VERSION", "2") # Register some config classes. register_plugin("1") do |p| p.config("foo") { OpenStruct } p.config("bar", true) { OpenStruct } end # Test that we have all keys result = described_class.init expect(result.bar).to be_kind_of(OpenStruct) end end describe "finalizing" do it "should call `#finalize` on the configuration object" do # Register a plugin for our test register_plugin("1") do |plugin| plugin.config "foo" do Class.new do attr_accessor :bar def finalize! @bar = "finalized" end end end end # Create the proc we're testing config_proc = Proc.new do |config| config.foo.bar = "value" end # Test that it works properly config = described_class.load(config_proc) expect(config.foo.bar).to eq("value") # Finalize it described_class.finalize(config) expect(config.foo.bar).to eq("finalized") end end describe "loading" do it "should configure with all plugin config keys loaded" do # Register a plugin for our test register_plugin("1") do |plugin| plugin.config("foo") { OpenStruct } end # Create the proc we're testing config_proc = Proc.new do |config| config.foo.bar = "value" end # Test that it works properly config = described_class.load(config_proc) expect(config.foo.bar).to eq("value") end end describe "merging" do it "should merge available configuration keys" do old = Vagrant::Config::V1::Root.new({ foo: Object }) new = Vagrant::Config::V1::Root.new({ bar: Object }) result = described_class.merge(old, new) expect(result.foo).to be_kind_of(Object) expect(result.bar).to be_kind_of(Object) end it "should merge instantiated objects" do config_class = Class.new do attr_accessor :value end old = Vagrant::Config::V1::Root.new({ foo: config_class }) old.foo.value = "old" new = Vagrant::Config::V1::Root.new({ bar: config_class }) new.bar.value = "new" result = described_class.merge(old, new) expect(result.foo.value).to eq("old") expect(result.bar.value).to eq("new") end it "should merge conflicting classes by calling `merge`" do config_class = Class.new do attr_accessor :value def merge(new) result = self.class.new result.value = @value + new.value result end end old = Vagrant::Config::V1::Root.new({ foo: config_class }) old.foo.value = 10 new = Vagrant::Config::V1::Root.new({ foo: config_class }) new.foo.value = 15 result = described_class.merge(old, new) expect(result.foo.value).to eq(25) end end end vagrant-2.0.2/test/unit/vagrant/config/v1/root_test.rb000066400000000000000000000021131323370221500227120ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Config::V1::Root do include_context "unit" it "should provide access to config objects" do foo_class = Class.new map = { foo: foo_class } instance = described_class.new(map) foo = instance.foo expect(foo).to be_kind_of(foo_class) expect(instance.foo).to eql(foo) end it "can be created with initial state" do instance = described_class.new({}, { foo: "bar" }) expect(instance.foo).to eq("bar") end it "should return internal state" do map = { "foo" => Object, "bar" => Object } instance = described_class.new(map) expect(instance.__internal_state).to eq({ "config_map" => map, "keys" => {}, "missing_key_calls" => Set.new }) end it "should record missing key calls" do instance = described_class.new({}) instance.foo.bar = false keys = instance.__internal_state["missing_key_calls"] expect(keys).to be_kind_of(Set) expect(keys.length).to eq(1) expect(keys.include?("foo")).to be end end vagrant-2.0.2/test/unit/vagrant/config/v2/000077500000000000000000000000001323370221500203475ustar00rootroot00000000000000vagrant-2.0.2/test/unit/vagrant/config/v2/dummy_config_test.rb000066400000000000000000000011371323370221500244150ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Config::V2::DummyConfig do it "should allow attribute setting" do expect { subject.foo = :bar }. to_not raise_error end it "should allow method calls that return more DummyConfigs" do expect(subject.foo).to be_kind_of(described_class) end it "should allow hash access" do expect { subject[:foo] }. to_not raise_error expect(subject[:foo]).to be_kind_of(described_class) end it "should allow setting hash values" do expect { subject[:foo] = :bar }. to_not raise_error end end vagrant-2.0.2/test/unit/vagrant/config/v2/loader_test.rb000066400000000000000000000100221323370221500231740ustar00rootroot00000000000000require "ostruct" require File.expand_path("../../../../base", __FILE__) describe Vagrant::Config::V2::Loader do include_context "unit" before(:each) do # Force the V2 loader to believe that we are in V2 stub_const("Vagrant::Config::CURRENT_VERSION", "2") end describe "empty" do it "returns an empty configuration object" do result = described_class.init expect(result).to be_kind_of(Vagrant::Config::V2::Root) end end describe "finalizing" do it "should call `#finalize` on the configuration object" do # Register a plugin for our test register_plugin("2") do |plugin| plugin.config "foo" do Class.new(Vagrant.plugin("2", "config")) do attr_accessor :bar def finalize! @bar = "finalized" end end end end # Create the proc we're testing config_proc = Proc.new do |config| config.foo.bar = "value" end # Test that it works properly config = described_class.load(config_proc) expect(config.foo.bar).to eq("value") # Finalize it described_class.finalize(config) expect(config.foo.bar).to eq("finalized") end end describe "loading" do it "should configure with all plugin config keys loaded" do # Register a plugin for our test register_plugin("2") do |plugin| plugin.config("foo") { OpenStruct } end # Create the proc we're testing config_proc = Proc.new do |config| config.foo.bar = "value" end # Test that it works properly config = described_class.load(config_proc) expect(config.foo.bar).to eq("value") end end describe "merging" do it "should merge available configuration keys" do old = Vagrant::Config::V2::Root.new({ foo: Object }) new = Vagrant::Config::V2::Root.new({ bar: Object }) result = described_class.merge(old, new) expect(result.foo).to be_kind_of(Object) expect(result.bar).to be_kind_of(Object) end it "should merge instantiated objects" do config_class = Class.new do attr_accessor :value end old = Vagrant::Config::V2::Root.new({ foo: config_class }) old.foo.value = "old" new = Vagrant::Config::V2::Root.new({ bar: config_class }) new.bar.value = "new" result = described_class.merge(old, new) expect(result.foo.value).to eq("old") expect(result.bar.value).to eq("new") end it "should merge conflicting classes by calling `merge`" do config_class = Class.new do attr_accessor :value def merge(new) result = self.class.new result.value = @value + new.value result end end old = Vagrant::Config::V2::Root.new({ foo: config_class }) old.foo.value = 10 new = Vagrant::Config::V2::Root.new({ foo: config_class }) new.foo.value = 15 result = described_class.merge(old, new) expect(result.foo.value).to eq(25) end end describe "upgrading" do it "should continue fine if the key doesn't implement upgrade" do # Make an old V1 root object old = Vagrant::Config::V1::Root.new({ foo: Class.new }) # It should work fine expect { result = described_class.upgrade(old) }.to_not raise_error end it "should upgrade the config if it implements the upgrade method" do # Create the old V1 class that will be upgraded config_class = Class.new do attr_accessor :value def upgrade(new) new.foo.value = value * 2 [["foo"], ["bar"]] end end # Create the new V2 plugin it is writing to register_plugin("2") do |p| p.config("foo") { OpenStruct } end # Test it out! old = Vagrant::Config::V1::Root.new({ foo: config_class }) old.foo.value = 5 data = described_class.upgrade(old) expect(data[0].foo.value).to eq(10) expect(data[1]).to eq(["foo"]) expect(data[2]).to eq(["bar"]) end end end vagrant-2.0.2/test/unit/vagrant/config/v2/root_test.rb000066400000000000000000000061751323370221500227270ustar00rootroot00000000000000require "set" require File.expand_path("../../../../base", __FILE__) describe Vagrant::Config::V2::Root do include_context "unit" it "should provide access to config objects" do foo_class = Class.new map = { foo: foo_class } instance = described_class.new(map) foo = instance.foo expect(foo).to be_kind_of(foo_class) expect(instance.foo).to eql(foo) end it "record a missing key call if invalid key used" do instance = described_class.new({}) expect { instance.foo }.to_not raise_error expect(instance.__internal_state["missing_key_calls"].include?("foo")).to be end it "returns a dummy config for a missing key" do instance = described_class.new({}) expect { instance.foo.foo = "bar" }.to_not raise_error end it "can be created with initial state" do instance = described_class.new({}, { foo: "bar" }) expect(instance.foo).to eq("bar") end it "should return internal state" do map = { "foo" => Object, "bar" => Object } instance = described_class.new(map) expect(instance.__internal_state).to eq({ "config_map" => map, "keys" => {}, "missing_key_calls" => Set.new }) end describe "#finalize!" do it "should call #finalize!" do foo_class = Class.new(Vagrant.plugin("2", "config")) do attr_accessor :foo def finalize! @foo = "SET" end end map = { foo: foo_class } instance = described_class.new(map) instance.finalize! expect(instance.foo.foo).to eq("SET") end it "should call #_finalize!" do klass = Class.new(Vagrant.plugin("2", "config")) expect_any_instance_of(klass).to receive(:finalize!) expect_any_instance_of(klass).to receive(:_finalize!) map = { foo: klass } instance = described_class.new(map) instance.finalize! end end describe "validation" do let(:instance) do map = { foo: Object, bar: Object } described_class.new(map) end it "should return nil if valid" do expect(instance.validate({})).to eq({}) end it "should return errors if invalid" do errors = { "foo" => ["errors!"] } env = { "errors" => errors } foo = instance.foo def foo.validate(env) env["errors"] end expect(instance.validate(env)).to eq(errors) end it "should merge errors via array concat if matching keys" do errors = { "foo" => ["errors!"] } env = { "errors" => errors } foo = instance.foo bar = instance.bar def foo.validate(env) env["errors"] end def bar.validate(env) env["errors"].merge({ "bar" => ["bar"] }) end expected_errors = { "foo" => ["errors!", "errors!"], "bar" => ["bar"] } expect(instance.validate(env)).to eq(expected_errors) end it "shouldn't count empty keys" do errors = { "foo" => [] } env = { "errors" => errors } foo = instance.foo def foo.validate(env) env["errors"] end expect(instance.validate(env)).to eq({}) end end end vagrant-2.0.2/test/unit/vagrant/config/v2/util_test.rb000066400000000000000000000010661323370221500227130ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require "vagrant/config/v2/util" describe Vagrant::Config::V2::Util do describe "merging errors" do it "should merge matching keys and leave the rest alone" do first = { "one" => ["foo"], "two" => ["two"] } second = { "one" => ["bar"], "three" => ["three"] } expected = { "one" => ["foo", "bar"], "two" => ["two"], "three" => ["three"] } result = described_class.merge_errors(first, second) expect(result).to eq(expected) end end end vagrant-2.0.2/test/unit/vagrant/config_test.rb000066400000000000000000000042771323370221500214160ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) describe Vagrant::Config do it "should not execute the proc on configuration" do described_class.run do raise Exception, "Failure." end end it "should capture calls to `Vagrant.configure`" do receiver = double() procs = described_class.capture_configures do Vagrant.configure("1") do receiver.one end Vagrant.configure("2") do receiver.two end end expect(procs).to be_kind_of(Array) expect(procs.length).to eq(2) expect(procs[0][0]).to eq("1") expect(procs[1][0]).to eq("2") # Verify the proper procs were captured expect(receiver).to receive(:one).once.ordered expect(receiver).to receive(:two).once.ordered procs[0][1].call procs[1][1].call end it "should work with integer configurations "do receiver = double() procs = described_class.capture_configures do Vagrant.configure(1) do receiver.one end Vagrant.configure("2") do receiver.two end end expect(procs).to be_kind_of(Array) expect(procs.length).to eq(2) expect(procs[0][0]).to eq("1") expect(procs[1][0]).to eq("2") # Verify the proper procs were captured expect(receiver).to receive(:one).once.ordered expect(receiver).to receive(:two).once.ordered procs[0][1].call procs[1][1].call end it "should capture configuration procs" do receiver = double() procs = described_class.capture_configures do described_class.run do receiver.hello! end end # Verify the structure of the result expect(procs).to be_kind_of(Array) expect(procs.length).to eq(1) # Verify that the proper proc was captured expect(receiver).to receive(:hello!).once expect(procs[0][0]).to eq("1") procs[0][1].call end it "should capture the proper version" do procs = described_class.capture_configures do described_class.run("1") {} described_class.run("2") {} end # Verify the structure of the result expect(procs).to be_kind_of(Array) expect(procs.length).to eq(2) expect(procs[0][0]).to eq("1") expect(procs[1][0]).to eq("2") end end vagrant-2.0.2/test/unit/vagrant/environment_test.rb000066400000000000000000001266501323370221500225150ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) require "json" require "pathname" require "tempfile" require "tmpdir" require "vagrant/util/file_mode" require "vagrant/util/platform" describe Vagrant::Environment do include_context "unit" include_context "capability_helpers" let(:env) do isolated_environment.tap do |e| e.box3("base", "1.0", :virtualbox) e.vagrantfile <<-VF Vagrant.configure("2") do |config| config.vm.box = "base" end VF end end let(:instance) { env.create_vagrant_env } subject { instance } describe "#can_install_provider?" do let(:plugin_hosts) { {} } let(:plugin_host_caps) { {} } before do m = Vagrant.plugin("2").manager allow(m).to receive(:hosts).and_return(plugin_hosts) allow(m).to receive(:host_capabilities).and_return(plugin_host_caps) # Detect the host env.vagrantfile <<-VF Vagrant.configure("2") do |config| config.vagrant.host = nil end VF # Setup the foo host by default plugin_hosts[:foo] = [detect_class(true), nil] end it "should return whether it can install or not" do plugin_host_caps[:foo] = { provider_install_foo: Class } expect(subject.can_install_provider?(:foo)).to be(true) expect(subject.can_install_provider?(:bar)).to be(false) end end describe "#install_provider" do let(:host) { double(:host) } before do allow(subject).to receive(:host).and_return(host) end it "should install the correct provider" do expect(host).to receive(:capability).with(:provider_install_foo) subject.install_provider(:foo) end end describe "#gems_path" do it "is set to Vagrant::Bundler defined path" do instance = described_class.new expect(instance.gems_path).to eq(Vagrant::Bundler.instance.plugin_gem_path) end end describe "#home_path" do it "is set to the home path given" do Dir.mktmpdir("vagrant-test-env-home-path-given") do |dir| instance = described_class.new(home_path: dir) expect(instance.home_path).to eq(Pathname.new(dir)) end end it "is set to the environmental variable VAGRANT_HOME" do Dir.mktmpdir("vagrant-test-env-home-env-var") do |dir| instance = with_temp_env("VAGRANT_HOME" => dir.to_s) do described_class.new end expect(instance.home_path).to eq(Pathname.new(dir)) end end it "throws an exception if inaccessible", skip_windows: true do expect { described_class.new(home_path: "/") }.to raise_error(Vagrant::Errors::HomeDirectoryNotAccessible) end context "with setup version file" do it "creates a setup version flie" do path = subject.home_path.join("setup_version") expect(path).to be_file expect(path.read).to eq(Vagrant::Environment::CURRENT_SETUP_VERSION) end it "is okay if it has the current version" do Dir.mktmpdir("vagrant-test-env-current-version") do |dir| Pathname.new(dir).join("setup_version").open("w") do |f| f.write(Vagrant::Environment::CURRENT_SETUP_VERSION) end instance = described_class.new(home_path: dir) path = instance.home_path.join("setup_version") expect(path).to be_file expect(path.read).to eq(Vagrant::Environment::CURRENT_SETUP_VERSION) end end it "raises an exception if the version is newer than ours" do Dir.mktmpdir("vagrant-test-env-newer-version") do |dir| Pathname.new(dir).join("setup_version").open("w") do |f| f.write("100.5") end expect { described_class.new(home_path: dir) }. to raise_error(Vagrant::Errors::HomeDirectoryLaterVersion) end end it "raises an exception if there is an unknown home directory version" do Dir.mktmpdir("vagrant-test-env-unknown-home") do |dir| Pathname.new(dir).join("setup_version").open("w") do |f| f.write("0.7") end expect { described_class.new(home_path: dir) }. to raise_error(Vagrant::Errors::HomeDirectoryUnknownVersion) end end end context "upgrading a v1.1 directory structure" do let(:env) { isolated_environment } before do env.homedir.join("setup_version").open("w") do |f| f.write("1.1") end allow_any_instance_of(Vagrant::UI::Silent). to receive(:ask) end it "replaces the setup version with the new version" do expect(subject.home_path.join("setup_version").read). to eq(Vagrant::Environment::CURRENT_SETUP_VERSION) end it "moves the boxes into the new directory structure" do # Kind of hacky but avoids two instantiations of BoxCollection allow(Vagrant::Environment).to receive(:boxes) .and_return(double("boxes")) collection = double("collection") expect(Vagrant::BoxCollection).to receive(:new).with( env.homedir.join("boxes"), anything).and_return(collection) expect(collection).to receive(:upgrade_v1_1_v1_5).once subject end end end describe "#host" do let(:plugin_hosts) { {} } let(:plugin_host_caps) { {} } before do m = Vagrant.plugin("2").manager allow(m).to receive(:hosts).and_return(plugin_hosts) allow(m).to receive(:host_capabilities).and_return(plugin_host_caps) end it "should default to some host even if there are none" do env.vagrantfile <<-VF Vagrant.configure("2") do |config| config.vagrant.host = nil end VF expect(subject.host).to be end it "should attempt to detect a host if no host is set" do env.vagrantfile <<-VF Vagrant.configure("2") do |config| config.vagrant.host = nil end VF plugin_hosts[:foo] = [detect_class(true), nil] plugin_host_caps[:foo] = { bar: Class } result = subject.host expect(result.capability?(:bar)).to be(true) end it "should attempt to detect a host if host is :detect" do env.vagrantfile <<-VF Vagrant.configure("2") do |config| config.vagrant.host = :detect end VF plugin_hosts[:foo] = [detect_class(true), nil] plugin_host_caps[:foo] = { bar: Class } result = subject.host expect(result.capability?(:bar)).to be(true) end it "should use an exact host if specified" do env.vagrantfile <<-VF Vagrant.configure("2") do |config| config.vagrant.host = "foo" end VF plugin_hosts[:foo] = [detect_class(false), nil] plugin_hosts[:bar] = [detect_class(true), nil] plugin_host_caps[:foo] = { bar: Class } result = subject.host expect(result.capability?(:bar)).to be(true) end it "should raise an error if an exact match was specified but not found" do env.vagrantfile <<-VF Vagrant.configure("2") do |config| config.vagrant.host = "bar" end VF expect { subject.host }. to raise_error(Vagrant::Errors::HostExplicitNotDetected) end end describe "#lock" do def lock_count subject.data_dir. children. find_all { |c| c.to_s.end_with?("lock") }. length end it "does nothing if no block is given" do subject.lock end it "locks the environment" do another = env.create_vagrant_env raised = false subject.lock do begin another.lock {} rescue Vagrant::Errors::EnvironmentLockedError raised = true end end expect(raised).to be(true) end it "allows nested locks on the same environment" do success = false subject.lock do subject.lock do success = true end end expect(success).to be(true) end it "cleans up all lock files" do inner_count = nil expect(lock_count).to eq(0) subject.lock do inner_count = lock_count end expect(inner_count).to_not be_nil expect(inner_count).to eq(2) expect(lock_count).to eq(1) end end describe "#machine" do # A helper to register a provider for use in tests. def register_provider(name, config_class=nil, options=nil) provider_cls = Class.new(VagrantTests::DummyProvider) register_plugin("2") do |p| p.provider(name, options) { provider_cls } if config_class p.config(name, :provider) { config_class } end end provider_cls end it "should return a machine object with the correct provider" do # Create a provider foo_provider = register_provider("foo") # Create the configuration isolated_env = isolated_environment do |e| e.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" config.vm.define "foo" end VF e.box3("base", "1.0", :foo) end # Verify that we can get the machine env = isolated_env.create_vagrant_env machine = env.machine(:foo, :foo) expect(machine).to be_kind_of(Vagrant::Machine) expect(machine.name).to eq(:foo) expect(machine.provider).to be_kind_of(foo_provider) expect(machine.provider_config).to be_nil end it "should return a machine object with the machine configuration" do # Create a provider foo_config = Class.new(Vagrant.plugin("2", :config)) do attr_accessor :value end foo_provider = register_provider("foo", foo_config) # Create the configuration isolated_env = isolated_environment do |e| e.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" config.vm.define "foo" config.vm.provider :foo do |fooconfig| fooconfig.value = 100 end end VF e.box3("base", "1.0", :foo) end # Verify that we can get the machine env = isolated_env.create_vagrant_env machine = env.machine(:foo, :foo) expect(machine).to be_kind_of(Vagrant::Machine) expect(machine.name).to eq(:foo) expect(machine.provider).to be_kind_of(foo_provider) expect(machine.provider_config.value).to eq(100) end it "should cache the machine objects by name and provider" do # Create a provider foo_provider = register_provider("foo") bar_provider = register_provider("bar") # Create the configuration isolated_env = isolated_environment do |e| e.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" config.vm.define "vm1" config.vm.define "vm2" end VF e.box3("base", "1.0", :foo) e.box3("base", "1.0", :bar) end env = isolated_env.create_vagrant_env vm1_foo = env.machine(:vm1, :foo) vm1_bar = env.machine(:vm1, :bar) vm2_foo = env.machine(:vm2, :foo) expect(vm1_foo).to eql(env.machine(:vm1, :foo)) expect(vm1_bar).to eql(env.machine(:vm1, :bar)) expect(vm1_foo).not_to eql(vm1_bar) expect(vm2_foo).to eql(env.machine(:vm2, :foo)) end it "should load a machine without a box" do register_provider("foo") environment = isolated_environment do |env| env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "i-dont-exist" end VF end env = environment.create_vagrant_env machine = env.machine(:default, :foo) expect(machine.box).to be_nil end it "should load the machine configuration" do register_provider("foo") environment = isolated_environment do |env| env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.ssh.port = 1 config.vm.box = "base" config.vm.define "vm1" do |inner| inner.ssh.port = 100 end end VF env.box3("base", "1.0", :foo) end env = environment.create_vagrant_env machine = env.machine(:vm1, :foo) expect(machine.config.ssh.port).to eq(100) expect(machine.config.vm.box).to eq("base") end it "should load the box configuration for a box" do register_provider("foo") environment = isolated_environment do |env| env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" end VF env.box3("base", "1.0", :foo, vagrantfile: <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 100 end VF end env = environment.create_vagrant_env machine = env.machine(:default, :foo) expect(machine.config.ssh.port).to eq(100) end it "should load the box configuration for a box and custom Vagrantfile name" do register_provider("foo") environment = isolated_environment do |env| env.file("some_other_name", <<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" end VF env.box3("base", "1.0", :foo, vagrantfile: <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 100 end VF end env = with_temp_env("VAGRANT_VAGRANTFILE" => "some_other_name") do environment.create_vagrant_env end machine = env.machine(:default, :foo) expect(machine.config.ssh.port).to eq(100) end it "should load the box configuration for other formats for a box" do register_provider("foo", nil, box_format: "bar") environment = isolated_environment do |env| env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" end VF env.box3("base", "1.0", :bar, vagrantfile: <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 100 end VF end env = environment.create_vagrant_env machine = env.machine(:default, :foo) expect(machine.config.ssh.port).to eq(100) end it "prefer sooner formats when multiple box formats are available" do register_provider("foo", nil, box_format: ["fA", "fB"]) environment = isolated_environment do |env| env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" end VF env.box3("base", "1.0", :fA, vagrantfile: <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 100 end VF env.box3("base", "1.0", :fB, vagrantfile: <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 200 end VF end env = environment.create_vagrant_env machine = env.machine(:default, :foo) expect(machine.config.ssh.port).to eq(100) end it "should load the proper version of a box" do register_provider("foo") environment = isolated_environment do |env| env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" config.vm.box_version = "~> 1.2" end VF env.box3("base", "1.0", :foo, vagrantfile: <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 100 end VF env.box3("base", "1.5", :foo, vagrantfile: <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 200 end VF end env = environment.create_vagrant_env machine = env.machine(:default, :foo) expect(machine.config.ssh.port).to eq(200) end it "should load the provider override if set" do register_provider("bar") register_provider("foo") isolated_env = isolated_environment do |e| e.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "foo" config.vm.provider :foo do |_, c| c.vm.box = "bar" end end VF end env = isolated_env.create_vagrant_env foo_vm = env.machine(:default, :foo) bar_vm = env.machine(:default, :bar) expect(foo_vm.config.vm.box).to eq("bar") expect(bar_vm.config.vm.box).to eq("foo") end it "should reload the cache if refresh is set" do # Create a provider foo_provider = register_provider("foo") # Create the configuration isolated_env = isolated_environment do |e| e.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" end VF e.box3("base", "1.0", :foo) end env = isolated_env.create_vagrant_env vm1 = env.machine(:default, :foo) vm2 = env.machine(:default, :foo, true) vm3 = env.machine(:default, :foo) expect(vm1).not_to eql(vm2) expect(vm2).to eql(vm3) end it "should raise an error if the VM is not found" do expect { instance.machine("i-definitely-dont-exist", :virtualbox) }. to raise_error(Vagrant::Errors::MachineNotFound) end it "should raise an error if the provider is not found" do expect { instance.machine(:default, :lol_no) }. to raise_error(Vagrant::Errors::ProviderNotFound) end end describe "#machine_index" do it "returns a machine index" do expect(subject.machine_index).to be_kind_of(Vagrant::MachineIndex) end it "caches the result" do result = subject.machine_index expect(subject.machine_index).to equal(result) end it "uses a directory within the home directory by default" do klass = double("machine_index") stub_const("Vagrant::MachineIndex", klass) expect(klass).to receive(:new).with(any_args) do |path| expect(path.to_s.start_with?(subject.home_path.to_s)).to be(true) true end subject.machine_index end end describe "active machines" do it "should be empty if there is no root path" do Dir.mktmpdir("vagrant-test-env-no-root-path") do |temp_dir| instance = described_class.new(cwd: temp_dir) expect(instance.active_machines).to be_empty end end it "should be empty if the machines folder doesn't exist" do folder = instance.local_data_path.join("machines") expect(folder).not_to be_exist expect(instance.active_machines).to be_empty end it "should return the name and provider of active machines" do machines = instance.local_data_path.join("machines") # Valid machine, with "foo" and virtualbox machine_foo = machines.join("foo/virtualbox") machine_foo.mkpath machine_foo.join("id").open("w+") { |f| f.write("") } # Invalid machine (no ID) machine_bar = machines.join("bar/virtualbox") machine_bar.mkpath expect(instance.active_machines).to eq([[:foo, :virtualbox]]) end end describe "batching" do let(:batch) do double("batch") do |b| allow(b).to receive(:run) end end context "without the disabling env var" do it "should run without disabling parallelization" do with_temp_env("VAGRANT_NO_PARALLEL" => nil) do expect(Vagrant::BatchAction).to receive(:new).with(true).and_return(batch) expect(batch).to receive(:run) instance.batch {} end end it "should run with disabling parallelization if explicit" do with_temp_env("VAGRANT_NO_PARALLEL" => nil) do expect(Vagrant::BatchAction).to receive(:new).with(false).and_return(batch) expect(batch).to receive(:run) instance.batch(false) {} end end end context "with the disabling env var" do it "should run with disabling parallelization" do with_temp_env("VAGRANT_NO_PARALLEL" => "yes") do expect(Vagrant::BatchAction).to receive(:new).with(false).and_return(batch) expect(batch).to receive(:run) instance.batch {} end end end end describe "current working directory" do it "is the cwd by default" do Dir.mktmpdir("vagrant-test-env-cwd-default") do |temp_dir| Dir.chdir(temp_dir) do with_temp_env("VAGRANT_CWD" => nil) do expect(described_class.new.cwd).to eq(Pathname.new(Dir.pwd)) end end end end it "is set to the cwd given" do Dir.mktmpdir("vagrant-test-env-set-cwd") do |directory| instance = described_class.new(cwd: directory) expect(instance.cwd).to eq(Pathname.new(directory)) end end it "is set to the environmental variable VAGRANT_CWD" do Dir.mktmpdir("vagrant-test-env-set-vagrant-cwd") do |directory| instance = with_temp_env("VAGRANT_CWD" => directory) do described_class.new end expect(instance.cwd).to eq(Pathname.new(directory)) end end it "raises an exception if the CWD doesn't exist" do expect { described_class.new(cwd: "doesntexist") }. to raise_error(Vagrant::Errors::EnvironmentNonExistentCWD) end end describe "default provider" do let(:plugin_providers) { {} } before do m = Vagrant.plugin("2").manager allow(m).to receive(:providers).and_return(plugin_providers) end it "is the highest matching usable provider" do plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }] plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }] plugin_providers[:baz] = [provider_usable_class(true), { priority: 2 }] plugin_providers[:boom] = [provider_usable_class(true), { priority: 3 }] with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil, "VAGRANT_PREFERRED_PROVIDERS" => nil) do expect(subject.default_provider).to eq(:bar) end end it "is the highest matching usable provider that is defaultable" do plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }] plugin_providers[:bar] = [ provider_usable_class(true), { defaultable: false, priority: 7 }] plugin_providers[:baz] = [provider_usable_class(true), { priority: 2 }] with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil, "VAGRANT_PREFERRED_PROVIDERS" => nil) do expect(subject.default_provider).to eq(:foo) end end it "is the highest matching usable provider that isn't excluded" do plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }] plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }] plugin_providers[:baz] = [provider_usable_class(true), { priority: 2 }] plugin_providers[:boom] = [provider_usable_class(true), { priority: 3 }] with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil, "VAGRANT_PREFERRED_PROVIDERS" => nil) do expect(subject.default_provider(exclude: [:bar, :foo])).to eq(:boom) end end it "is the default provider set if usable" do plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }] plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }] plugin_providers[:baz] = [provider_usable_class(true), { priority: 2 }] plugin_providers[:boom] = [provider_usable_class(true), { priority: 3 }] with_temp_env("VAGRANT_DEFAULT_PROVIDER" => "baz", "VAGRANT_PREFERRED_PROVIDERS" => nil) do expect(subject.default_provider).to eq(:baz) end end it "is the default provider set even if unusable" do plugin_providers[:baz] = [provider_usable_class(false), { priority: 5 }] plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }] plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }] with_temp_env("VAGRANT_DEFAULT_PROVIDER" => "baz", "VAGRANT_PREFERRED_PROVIDERS" => nil) do expect(subject.default_provider).to eq(:baz) end end it "is the usable despite default if not forced" do plugin_providers[:baz] = [provider_usable_class(false), { priority: 5 }] plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }] plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }] with_temp_env("VAGRANT_DEFAULT_PROVIDER" => "baz", "VAGRANT_PREFERRED_PROVIDERS" => nil) do expect(subject.default_provider(force_default: false)).to eq(:bar) end end it "prefers the default even if not forced" do plugin_providers[:baz] = [provider_usable_class(true), { priority: 5 }] plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }] plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }] with_temp_env("VAGRANT_DEFAULT_PROVIDER" => "baz", "VAGRANT_PREFERRED_PROVIDERS" => nil) do expect(subject.default_provider(force_default: false)).to eq(:baz) end end it "uses the first usable provider that isn't the default if excluded" do plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }] plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }] plugin_providers[:baz] = [provider_usable_class(true), { priority: 8 }] with_temp_env("VAGRANT_DEFAULT_PROVIDER" => "baz", "VAGRANT_PREFERRED_PROVIDERS" => nil) do expect(subject.default_provider( exclude: [:baz], force_default: false)).to eq(:bar) end end it "raise an error if nothing else is usable" do plugin_providers[:foo] = [provider_usable_class(false), { priority: 5 }] plugin_providers[:bar] = [provider_usable_class(false), { priority: 5 }] plugin_providers[:baz] = [provider_usable_class(false), { priority: 5 }] with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil, "VAGRANT_PREFERRED_PROVIDERS" => nil) do expect { subject.default_provider }.to raise_error( Vagrant::Errors::NoDefaultProvider) end end it "is the provider in the Vagrantfile that is preferred and usable" do subject.vagrantfile.config.vm.provider "foo" subject.vagrantfile.config.vm.provider "bar" subject.vagrantfile.config.vm.finalize! plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }] plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }] plugin_providers[:baz] = [provider_usable_class(true), { priority: 2 }] plugin_providers[:boom] = [provider_usable_class(true), { priority: 3 }] with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil, "VAGRANT_PREFERRED_PROVIDERS" => 'baz,bar') do expect(subject.default_provider).to eq(:bar) end end it "is the provider in the Vagrantfile that is usable" do subject.vagrantfile.config.vm.provider "foo" subject.vagrantfile.config.vm.provider "bar" subject.vagrantfile.config.vm.finalize! plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }] plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }] plugin_providers[:baz] = [provider_usable_class(true), { priority: 2 }] plugin_providers[:boom] = [provider_usable_class(true), { priority: 3 }] with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil, "VAGRANT_PREFERRED_PROVIDERS" => nil) do expect(subject.default_provider).to eq(:foo) end end it "is the provider in the Vagrantfile that is usable even if only one specified (1)" do subject.vagrantfile.config.vm.provider "foo" subject.vagrantfile.config.vm.finalize! plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }] plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }] with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil, "VAGRANT_PREFERRED_PROVIDERS" => nil) do expect(subject.default_provider).to eq(:foo) end end it "is the provider in the Vagrantfile that is usable even if only one specified (2)" do subject.vagrantfile.config.vm.provider "bar" subject.vagrantfile.config.vm.finalize! plugin_providers[:foo] = [provider_usable_class(true), { priority: 7 }] plugin_providers[:bar] = [provider_usable_class(true), { priority: 5 }] with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil, "VAGRANT_PREFERRED_PROVIDERS" => nil) do expect(subject.default_provider).to eq(:bar) end end it "is the preferred usable provider outside the Vagrantfile" do subject.vagrantfile.config.vm.provider "foo" subject.vagrantfile.config.vm.finalize! plugin_providers[:foo] = [provider_usable_class(false), { priority: 5 }] plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }] plugin_providers[:baz] = [provider_usable_class(true), { priority: 2 }] plugin_providers[:boom] = [provider_usable_class(true), { priority: 3 }] with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil, "VAGRANT_PREFERRED_PROVIDERS" => 'boom,baz') do expect(subject.default_provider).to eq(:boom) end end it "is the highest usable provider outside the Vagrantfile" do subject.vagrantfile.config.vm.provider "foo" subject.vagrantfile.config.vm.finalize! plugin_providers[:foo] = [provider_usable_class(false), { priority: 5 }] plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }] plugin_providers[:baz] = [provider_usable_class(true), { priority: 2 }] plugin_providers[:boom] = [provider_usable_class(true), { priority: 3 }] with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil, "VAGRANT_PREFERRED_PROVIDERS" => nil) do expect(subject.default_provider).to eq(:bar) end end it "is the provider in the Vagrantfile that is usable for a machine" do subject.vagrantfile.config.vm.provider "foo" subject.vagrantfile.config.vm.define "sub" do |v| v.vm.provider "bar" end subject.vagrantfile.config.vm.finalize! plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }] plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }] plugin_providers[:baz] = [provider_usable_class(true), { priority: 2 }] plugin_providers[:boom] = [provider_usable_class(true), { priority: 3 }] with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil, "VAGRANT_PREFERRED_PROVIDERS" => nil) do expect(subject.default_provider(machine: :sub)).to eq(:bar) end end end describe "local data path" do it "is set to the proper default" do default = instance.root_path.join(described_class::DEFAULT_LOCAL_DATA) expect(instance.local_data_path).to eq(default) end it "is expanded relative to the cwd" do Dir.mktmpdir("vagrant-test-env-relative-cwd") do |temp_dir| Dir.chdir(temp_dir) do instance = described_class.new(local_data_path: "foo") expect(instance.local_data_path).to eq(instance.cwd.join("foo")) expect(File.exist?(instance.local_data_path)).to be(false) end end end it "is set to the given value" do Dir.mktmpdir("vagrant-test-env-set-given") do |dir| instance = described_class.new(local_data_path: dir) expect(instance.local_data_path.to_s).to eq(dir) end end context "with environmental variable VAGRANT_DOTFILE_PATH set to the empty string" do it "is set to the default, from the work directory" do with_temp_env("VAGRANT_DOTFILE_PATH" => "") do instance = env.create_vagrant_env expect(instance.cwd).to eq(env.workdir) expect(instance.local_data_path.to_s).to eq(File.join(env.workdir, ".vagrant")) end end it "is set to the default, from a sub-directory of the work directory" do Dir.mktmpdir("sub-directory", env.workdir) do |temp_dir| with_temp_env("VAGRANT_DOTFILE_PATH" => "") do instance = env.create_vagrant_env(cwd: temp_dir) expect(instance.cwd.to_s).to eq(temp_dir) expect(instance.local_data_path.to_s).to eq(File.join(env.workdir, ".vagrant")) end end end end context "with environmental variable VAGRANT_DOTFILE_PATH set to an absolute path" do it "is set to VAGRANT_DOTFILE_PATH from the work directory" do Dir.mktmpdir("sub-directory", env.workdir) do |temp_dir| dotfile_path = File.join(temp_dir, ".vagrant-custom") with_temp_env("VAGRANT_DOTFILE_PATH" => dotfile_path) do instance = env.create_vagrant_env expect(instance.cwd).to eq(env.workdir) expect(instance.local_data_path.to_s).to eq(dotfile_path) end end end it "is set to VAGRANT_DOTFILE_PATH from a sub-directory of the work directory" do Dir.mktmpdir("sub-directory", env.workdir) do |temp_dir| dotfile_path = File.join(temp_dir, ".vagrant-custom") with_temp_env("VAGRANT_DOTFILE_PATH" => dotfile_path) do instance = env.create_vagrant_env(cwd: temp_dir) expect(instance.cwd.to_s).to eq(temp_dir) expect(instance.local_data_path.to_s).to eq(dotfile_path) end end end end context "with environmental variable VAGRANT_DOTFILE_PATH set to a relative path" do it "is set relative to the the work directory, from the work directory" do Dir.mktmpdir("sub-directory", env.workdir) do |temp_dir| with_temp_env("VAGRANT_DOTFILE_PATH" => ".vagrant-custom") do instance = env.create_vagrant_env expect(instance.cwd).to eq(env.workdir) expect(instance.local_data_path.to_s).to eq(File.join(env.workdir, ".vagrant-custom")) end end end it "is set relative to the the work directory, from a sub-directory of the work directory" do Dir.mktmpdir("sub-directory", env.workdir) do |temp_dir| with_temp_env("VAGRANT_DOTFILE_PATH" => ".vagrant-custom") do instance = env.create_vagrant_env(cwd: temp_dir) expect(instance.cwd.to_s).to eq(temp_dir) expect(instance.local_data_path.to_s).to eq(File.join(env.workdir, ".vagrant-custom")) end end end it "is set to the empty string when there is no valid work directory" do Dir.mktmpdir("out-of-tree-directory") do |temp_dir| with_temp_env("VAGRANT_DOTFILE_PATH" => ".vagrant-custom") do instance = env.create_vagrant_env(cwd: temp_dir) expect(instance.cwd.to_s).to eq(temp_dir) expect(instance.local_data_path.to_s).to eq("") end end end end context "with environmental variable VAGRANT_DOTFILE_PATH set with tilde" do it "is set relative to the user's home directory" do with_temp_env("VAGRANT_DOTFILE_PATH" => "~/.vagrant") do instance = env.create_vagrant_env expect(instance.cwd).to eq(env.workdir) expect(instance.local_data_path.to_s).to eq(File.join(Dir.home, ".vagrant")) end end end describe "upgrading V1 dotfiles" do let(:v1_dotfile_tempfile) do Tempfile.new("vagrant-upgrade-dotfile").tap do |f| f.close end end let(:v1_dotfile) { Pathname.new(v1_dotfile_tempfile.path) } let(:local_data_path) { v1_dotfile_tempfile.path } let(:instance) { described_class.new(local_data_path: local_data_path) } after do FileUtils.rm_rf(local_data_path) end it "should be fine if dotfile is empty" do v1_dotfile.open("w+") do |f| f.write("") end expect { instance }.to_not raise_error expect(Pathname.new(local_data_path)).to_not be_exist end it "should upgrade all active VMs" do active_vms = { "foo" => "foo_id", "bar" => "bar_id" } v1_dotfile.open("w+") do |f| f.write(JSON.dump({ "active" => active_vms })) end expect { instance }.to_not raise_error local_data_pathname = Pathname.new(local_data_path) foo_id_file = local_data_pathname.join("machines/foo/virtualbox/id") expect(foo_id_file).to be_file expect(foo_id_file.read).to eq("foo_id") bar_id_file = local_data_pathname.join("machines/bar/virtualbox/id") expect(bar_id_file).to be_file expect(bar_id_file.read).to eq("bar_id") end it "should raise an error if invalid JSON" do v1_dotfile.open("w+") do |f| f.write("bad") end expect { instance }. to raise_error(Vagrant::Errors::DotfileUpgradeJSONError) end end end describe "copying the private SSH key" do it "copies the SSH key into the home directory" do env = isolated_environment instance = described_class.new(home_path: env.homedir) pk = env.homedir.join("insecure_private_key") expect(pk).to be_exist if !Vagrant::Util::Platform.windows? expect(Vagrant::Util::FileMode.from_octal(pk.stat.mode)).to eq("600") end end end it "has a box collection pointed to the proper directory" do collection = instance.boxes expect(collection).to be_kind_of(Vagrant::BoxCollection) expect(collection.directory).to eq(instance.boxes_path) # Reach into some internal state here but not sure how else # to test this at the moment. expect(collection.instance_variable_get(:@hook)). to eq(instance.method(:hook)) end describe "action runner" do it "has an action runner" do expect(instance.action_runner).to be_kind_of(Vagrant::Action::Runner) end it "has a `ui` in the globals" do result = nil callable = lambda { |env| result = env[:ui] } instance.action_runner.run(callable) expect(result).to eql(instance.ui) end end describe "#pushes" do it "returns the pushes from the Vagrantfile config" do environment = isolated_environment do |env| env.vagrantfile(<<-VF.gsub(/^ {10}/, '')) Vagrant.configure("2") do |config| config.push.define "noop" end VF end env = environment.create_vagrant_env expect(env.pushes).to eq([:noop]) end end describe "#push" do let(:push_class) do Class.new(Vagrant.plugin("2", :push)) do def self.pushed? !!class_variable_get(:@@pushed) end def push self.class.class_variable_set(:@@pushed, true) end end end it "raises an exception when the push does not exist" do expect { instance.push("lolwatbacon") } .to raise_error(Vagrant::Errors::PushStrategyNotDefined) end it "raises an exception if the strategy does not exist" do environment = isolated_environment do |env| env.vagrantfile(<<-VF.gsub(/^ {10}/, '')) Vagrant.configure("2") do |config| config.push.define "lolwatbacon" end VF end env = environment.create_vagrant_env expect { env.push("lolwatbacon") } .to raise_error(Vagrant::Errors::PushStrategyNotLoaded) end it "executes the push action" do register_plugin("2") do |plugin| plugin.name "foo" plugin.push(:foo) do push_class end end environment = isolated_environment do |env| env.vagrantfile(<<-VF.gsub(/^ {10}/, '')) Vagrant.configure("2") do |config| config.push.define "foo" end VF end env = environment.create_vagrant_env env.push("foo") expect(push_class.pushed?).to be(true) end end describe "#hook" do it "should call the action runner with the proper hook" do hook_name = :foo expect(instance.action_runner).to receive(:run).with(any_args) { |callable, env| expect(env[:action_name]).to eq(hook_name) } instance.hook(hook_name) end it "should return the result of the action runner run" do expect(instance.action_runner).to receive(:run).and_return(:foo) expect(instance.hook(:bar)).to eq(:foo) end it "should allow passing in a custom action runner" do expect(instance.action_runner).not_to receive(:run) other_runner = double("runner") expect(other_runner).to receive(:run).and_return(:foo) expect(instance.hook(:bar, runner: other_runner)).to eq(:foo) end it "should allow passing in custom data" do expect(instance.action_runner).to receive(:run).with(any_args) { |callable, env| expect(env[:foo]).to eq(:bar) } instance.hook(:foo, foo: :bar) end it "should allow passing a custom callable" do expect(instance.action_runner).to receive(:run).with(any_args) { |callable, env| expect(callable).to eq(:what) } instance.hook(:foo, callable: :what) end end describe "primary machine name" do it "should be the only machine if not a multi-machine environment" do expect(instance.primary_machine_name).to eq(instance.machine_names.first) end it "should be the machine marked as the primary" do environment = isolated_environment do |env| env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" config.vm.define :foo config.vm.define :bar, primary: true end VF env.box3("base", "1.0", :virtualbox) end env = environment.create_vagrant_env expect(env.primary_machine_name).to eq(:bar) end it "should be nil if no primary is specified in a multi-machine environment" do environment = isolated_environment do |env| env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" config.vm.define :foo config.vm.define :bar end VF env.box3("base", "1.0", :virtualbox) end env = environment.create_vagrant_env expect(env.primary_machine_name).to be_nil end end describe "loading configuration" do it "should load global configuration" do environment = isolated_environment do |env| env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.ssh.port = 200 end VF end env = environment.create_vagrant_env expect(env.vagrantfile.config.ssh.port).to eq(200) end it "should load from a custom Vagrantfile" do environment = isolated_environment do |env| env.file("non_standard_name", <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 200 end VF end env = environment.create_vagrant_env(vagrantfile_name: "non_standard_name") expect(env.vagrantfile.config.ssh.port).to eq(200) end it "should load from a custom Vagrantfile specified by env var" do environment = isolated_environment do |env| env.file("some_other_name", <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 400 end VF end env = with_temp_env("VAGRANT_VAGRANTFILE" => "some_other_name") do environment.create_vagrant_env end expect(env.vagrantfile.config.ssh.port).to eq(400) end end describe "ui" do it "should be a silent UI by default" do expect(described_class.new.ui).to be_kind_of(Vagrant::UI::Silent) end it "should be a UI given in the constructor" do # Create a custom UI for our test class CustomUI < Vagrant::UI::Interface; end instance = described_class.new(ui_class: CustomUI) expect(instance.ui).to be_kind_of(CustomUI) end end describe "#unload" do it "should run the unload hook" do expect(instance).to receive(:hook).with(:environment_unload).once instance.unload end end describe "getting machine names" do it "should return the default machine if no multi-VM is used" do # Create the config isolated_env = isolated_environment do |e| e.vagrantfile(<<-VF) Vagrant.configure("2") do |config| end VF end env = isolated_env.create_vagrant_env expect(env.machine_names).to eq([:default]) end it "should return the machine names in order" do # Create the config isolated_env = isolated_environment do |e| e.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.define "foo" config.vm.define "bar" end VF end env = isolated_env.create_vagrant_env expect(env.machine_names).to eq([:foo, :bar]) end end end vagrant-2.0.2/test/unit/vagrant/errors_test.rb000066400000000000000000000026121323370221500214540ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) describe Vagrant::Errors::VagrantError do describe "subclass with error key" do let(:klass) do Class.new(described_class) do error_key("test_key") end end subject { klass.new } it "should use the translation for the message" do expect(subject.to_s).to eq("test value") end describe '#status_code' do subject { super().status_code } it { should eq(1) } end end describe "passing error key through options" do subject { described_class.new(_key: "test_key") } it "should use the translation for the message" do expect(subject.to_s).to eq("test value") end end describe "subclass with error message" do let(:klass) do Class.new(described_class) do error_message("foo") end end subject { klass.new(data: "yep") } it "should use the translation for the message" do expect(subject.to_s).to eq("foo") end it "should expose translation keys to the user" do expect(subject.extra_data.length).to eql(1) expect(subject.extra_data).to have_key(:data) expect(subject.extra_data[:data]).to eql("yep") end it "should use a symbol initializer as a key" do subject = klass.new(:test_key) expect(subject.extra_data).to be_empty expect(subject.to_s).to eql("test value") end end end vagrant-2.0.2/test/unit/vagrant/guest_test.rb000066400000000000000000000055511323370221500212740ustar00rootroot00000000000000require "pathname" require File.expand_path("../../base", __FILE__) describe Vagrant::Guest do include_context "capability_helpers" let(:capabilities) { {} } let(:guests) { {} } let(:machine) do double("machine").tap do |m| allow(m).to receive(:inspect).and_return("machine") allow(m).to receive(:config).and_return(double("config")) allow(m.config).to receive(:vm).and_return(double("vm_config")) allow(m.config.vm).to receive(:guest).and_return(nil) end end subject { described_class.new(machine, guests, capabilities) } describe "#capability" do before(:each) do guests[:foo] = [detect_class(true), nil] capabilities[:foo] = { foo_cap: cap_instance(:foo_cap), corrupt_cap: cap_instance(:corrupt_cap, corrupt: true), } subject.detect! end it "executes existing capabilities" do expect { subject.capability(:foo_cap) }. to raise_error(RuntimeError, "cap: foo_cap [machine]") end it "raises user-friendly error for non-existing caps" do expect { subject.capability(:what_cap) }. to raise_error(Vagrant::Errors::GuestCapabilityNotFound) end it "raises a user-friendly error for corrupt caps" do expect { subject.capability(:corrupt_cap) }. to raise_error(Vagrant::Errors::GuestCapabilityInvalid) end end describe "#detect!" do it "auto-detects if no explicit guest name given" do allow(machine.config.vm).to receive(:guest).and_return(nil) expect(subject).to receive(:initialize_capabilities!). with(nil, guests, capabilities, machine) subject.detect! end it "uses the explicit guest name if specified" do allow(machine.config.vm).to receive(:guest).and_return(:foo) expect(subject).to receive(:initialize_capabilities!). with(:foo, guests, capabilities, machine) subject.detect! end it "raises a user-friendly error if specified guest doesn't exist" do allow(machine.config.vm).to receive(:guest).and_return(:foo) expect { subject.detect! }. to raise_error(Vagrant::Errors::GuestExplicitNotDetected) end it "raises a user-friendly error if auto-detected guest not found" do expect { subject.detect! }. to raise_error(Vagrant::Errors::GuestNotDetected) end end describe "#name" do it "should be the name of the detected guest" do guests[:foo] = [detect_class(true), nil] guests[:bar] = [detect_class(false), nil] subject.detect! expect(subject.name).to eql(:foo) end end describe "#ready?" do before(:each) do guests[:foo] = [detect_class(true), nil] end it "should not be ready by default" do expect(subject.ready?).not_to be end it "should be ready after detecting" do subject.detect! expect(subject.ready?).to be end end end vagrant-2.0.2/test/unit/vagrant/host_test.rb000066400000000000000000000006761323370221500211250ustar00rootroot00000000000000require "pathname" require File.expand_path("../../base", __FILE__) describe Vagrant::Host do include_context "capability_helpers" let(:capabilities) { {} } let(:hosts) { {} } let(:env) { Object.new } it "initializes the capabilities" do expect_any_instance_of(described_class).to receive(:initialize_capabilities!). with(:foo, hosts, capabilities, env) described_class.new(:foo, hosts, capabilities, env) end end vagrant-2.0.2/test/unit/vagrant/machine_index_test.rb000066400000000000000000000215471323370221500227430ustar00rootroot00000000000000require "json" require "pathname" require "tempfile" require File.expand_path("../../base", __FILE__) require "vagrant/machine_index" describe Vagrant::MachineIndex do include_context "unit" let(:data_dir) { Pathname.new(Dir.mktmpdir("vagrant-test-machine-index-data-dir")) } let(:entry_klass) { Vagrant::MachineIndex::Entry } let(:new_entry) do entry_klass.new.tap do |e| e.name = "foo" e.vagrantfile_path = "/bar" end end subject { described_class.new(data_dir) } after do FileUtils.rm_rf(data_dir) end it "raises an exception if the data file is corrupt" do data_dir.join("index").open("w") do |f| f.write(JSON.dump({})) end expect { subject }. to raise_error(Vagrant::Errors::CorruptMachineIndex) end it "raises an exception if the JSON is invalid" do data_dir.join("index").open("w") do |f| f.write("foo") end expect { subject }. to raise_error(Vagrant::Errors::CorruptMachineIndex) end describe "#each" do before do 5.times do |i| e = entry_klass.new e.name = "entry-#{i}" e.vagrantfile_path = "/foo" subject.release(subject.set(e)) end end it "should iterate over all the elements" do items = [] subject = described_class.new(data_dir) subject.each do |entry| items << entry.name end items.sort! expect(items).to eq([ "entry-0", "entry-1", "entry-2", "entry-3", "entry-4", ]) end end describe "#get and #release" do before do data = { "version" => 1, "machines" => { "bar" => { "name" => "default", "provider" => "vmware", "local_data_path" => "/foo", "vagrantfile_path" => "/foo/bar/baz", "state" => "running", "updated_at" => "foo", }, "baz" => { "name" => "default", "provider" => "vmware", "vagrantfile_path" => "/foo/bar/baz", "state" => "running", "updated_at" => "foo", "extra_data" => { "foo" => "bar", }, }, } } data_dir.join("index").open("w") do |f| f.write(JSON.dump(data)) end end it "returns nil if the machine doesn't exist" do expect(subject.get("foo")).to be_nil end it "returns a valid entry if the machine exists" do result = subject.get("bar") expect(result.id).to eq("bar") expect(result.name).to eq("default") expect(result.provider).to eq("vmware") expect(result.local_data_path).to eq(Pathname.new("/foo")) expect(result.vagrantfile_path).to eq(Pathname.new("/foo/bar/baz")) expect(result.state).to eq("running") expect(result.updated_at).to eq("foo") expect(result.extra_data).to eq({}) end it "returns a valid entry with extra data" do result = subject.get("baz") expect(result.id).to eq("baz") expect(result.extra_data).to eq({ "foo" => "bar", }) end it "returns a valid entry by unique prefix" do result = subject.get("b") expect(result).to_not be_nil expect(result.id).to eq("bar") end it "should include? by prefix" do expect(subject.include?("b")).to be(true) end it "locks the entry so subsequent gets fail" do result = subject.get("bar") expect(result).to_not be_nil expect { subject.get("bar") }. to raise_error(Vagrant::Errors::MachineLocked) end it "can unlock a machine" do result = subject.get("bar") expect(result).to_not be_nil subject.release(result) result = subject.get("bar") expect(result).to_not be_nil end end describe "#include" do it "should not include non-existent things" do expect(subject.include?("foo")).to be(false) end it "should include created entries" do result = subject.set(new_entry) expect(result.id).to_not be_empty subject.release(result) subject = described_class.new(data_dir) expect(subject.include?(result.id)).to be(true) end end describe "#set and #get and #delete" do it "adds a new entry" do result = subject.set(new_entry) expect(result.id).to_not be_empty # It should be locked expect { subject.get(result.id) }. to raise_error(Vagrant::Errors::MachineLocked) # Get it froma new class and check the results subject.release(result) subject = described_class.new(data_dir) entry = subject.get(result.id) expect(entry).to_not be_nil expect(entry.name).to eq("foo") # TODO: test that updated_at is set end it "can delete an entry" do result = subject.set(new_entry) expect(result.id).to_not be_empty subject.delete(result) # Get it from a new class and check the results subject = described_class.new(data_dir) entry = subject.get(result.id) expect(entry).to be_nil end it "can delete an entry that doesn't exist" do e = entry_klass.new expect(subject.delete(e)).to be(true) end it "updates an existing entry" do entry = entry_klass.new entry.name = "foo" entry.vagrantfile_path = "/bar" result = subject.set(entry) expect(result.id).to_not be_empty result.name = "bar" result.extra_data["foo"] = "bar" nextresult = subject.set(result) expect(nextresult.id).to eq(result.id) # Release it so we can test the contents subject.release(nextresult) # Get it froma new class and check the results subject = described_class.new(data_dir) entry = subject.get(result.id) expect(entry).to_not be_nil expect(entry.name).to eq("bar") expect(entry.extra_data).to eq({ "foo" => "bar", }) end it "updates an existing directory if the name, provider, and path are the same" do entry = entry_klass.new entry.name = "foo" entry.provider = "bar" entry.vagrantfile_path = "/bar" entry.state = "foo" result = subject.set(entry) expect(result.id).to_not be_empty # Release it so we can modify it subject.release(result) entry2 = entry_klass.new entry2.name = entry.name entry2.provider = entry.provider entry2.vagrantfile_path = entry.vagrantfile_path entry2.state = "bar" expect(entry2.id).to be_nil nextresult = subject.set(entry2) expect(nextresult.id).to eq(result.id) # Release it so we can test the contents subject.release(nextresult) # Get it from a new class and check the results subject = described_class.new(data_dir) entry = subject.get(result.id) expect(entry).to_not be_nil expect(entry.name).to eq(entry2.name) expect(entry.state).to eq(entry2.state) end end end describe Vagrant::MachineIndex::Entry do include_context "unit" let(:env) { iso_env = isolated_environment iso_env.vagrantfile(vagrantfile) iso_env.create_vagrant_env } let(:vagrantfile) { "" } describe "#valid?" do let(:machine) { env.machine(:default, :dummy) } subject do described_class.new.tap do |e| e.name = "default" e.provider = "dummy" e.vagrantfile_path = env.root_path end end it "should be valid with a valid entry" do machine.id = "foo" expect(subject).to be_valid(env.home_path) end it "should be invalid if no Vagrantfile path is set" do subject.vagrantfile_path = nil expect(subject).to_not be_valid(env.home_path) end it "should be invalid if the Vagrantfile path does not exist" do subject.vagrantfile_path = Pathname.new("/i/should/not/exist") expect(subject).to_not be_valid(env.home_path) end it "should be invalid if the machine is inactive" do machine.id = nil expect(subject).to_not be_valid(env.home_path) end it "should be invalid if machine is not created" do machine.id = "foo" machine.provider.state = Vagrant::MachineState::NOT_CREATED_ID expect(subject).to_not be_valid(env.home_path) end context "with another active machine" do let(:vagrantfile) do <<-VF Vagrant.configure("2") do |config| config.vm.define "web" config.vm.define "db" end VF end it "should be invalid if the wrong machine is active only" do m = env.machine(:web, :dummy) m.id = "foo" subject.name = "db" expect(subject).to_not be_valid(env.home_path) end it "should be valid if the correct machine is active" do env.machine(:web, :dummy).id = "foo" env.machine(:db, :dummy).id = "foo" subject.name = "db" expect(subject).to be_valid(env.home_path) end end end end vagrant-2.0.2/test/unit/vagrant/machine_state_test.rb000066400000000000000000000012511323370221500227420ustar00rootroot00000000000000require "pathname" require File.expand_path("../../base", __FILE__) describe Vagrant::MachineState do include_context "unit" let(:id) { :some_state } let(:short) { "foo" } let(:long) { "I am a longer foo" } it "should give access to the id" do instance = described_class.new(id, short, long) expect(instance.id).to eq(id) end it "should give access to the short description" do instance = described_class.new(id, short, long) expect(instance.short_description).to eq(short) end it "should give access to the long description" do instance = described_class.new(id, short, long) expect(instance.long_description).to eq(long) end end vagrant-2.0.2/test/unit/vagrant/machine_test.rb000066400000000000000000000631171323370221500215530ustar00rootroot00000000000000require "pathname" require "tmpdir" require File.expand_path("../../base", __FILE__) require "vagrant/util/platform" describe Vagrant::Machine do include_context "unit" let(:name) { "foo" } let(:provider) { new_provider_mock } let(:provider_cls) do obj = double("provider_cls") allow(obj).to receive(:new).and_return(provider) obj end let(:provider_config) { Object.new } let(:provider_name) { :test } let(:provider_options) { {} } let(:base) { false } let(:box) do double("box").tap do |b| allow(b).to receive(:name).and_return("foo") allow(b).to receive(:provider).and_return(:dummy) allow(b).to receive(:version).and_return("1.0") end end let(:config) { env.vagrantfile.config } let(:data_dir) { Pathname.new(Dir.mktmpdir("vagrant-machine-data-dir")) } let(:env) do # We need to create a Vagrantfile so that this test environment # has a proper root path test_env.vagrantfile("") # Create the Vagrant::Environment instance test_env.create_vagrant_env end let(:test_env) { isolated_environment } let(:instance) { new_instance } after do FileUtils.rm_rf(data_dir) if data_dir end subject { instance } def new_provider_mock double("provider").tap do |obj| allow(obj).to receive(:_initialize).and_return(nil) allow(obj).to receive(:machine_id_changed).and_return(nil) allow(obj).to receive(:state).and_return(Vagrant::MachineState.new( :created, "", "")) end end # Returns a new instance with the test data def new_instance described_class.new(name, provider_name, provider_cls, provider_config, provider_options, config, data_dir, box, env, env.vagrantfile, base) end describe "initialization" do it "should set the ID to nil if the state is not created" do subject.id = "foo" allow(provider).to receive(:state).and_return(Vagrant::MachineState.new( Vagrant::MachineState::NOT_CREATED_ID, "short", "long")) subject = new_instance expect(subject.state.id).to eq(Vagrant::MachineState::NOT_CREATED_ID) expect(subject.id).to be_nil end describe "as a base" do let(:base) { true} it "should not insert key" do subject = new_instance expect(subject.config.ssh.insert_key).to be(false) end end describe "communicator loading" do it "doesn't eager load SSH" do config.vm.communicator = :ssh klass = Vagrant.plugin("2").manager.communicators[:ssh] expect(klass).to_not receive(:new) subject end it "eager loads WinRM" do config.vm.communicator = :winrm klass = Vagrant.plugin("2").manager.communicators[:winrm] instance = double("instance") expect(klass).to receive(:new).and_return(instance) subject end end describe "provider initialization" do # This is a helper that generates a test for provider initialization. # This is a separate helper method because it takes a block that can # be used to have additional tests on the received machine. # # @yield [machine] Yields the machine that the provider initialization # method received so you can run additional tests on it. def provider_init_test(instance=nil) received_machine = nil if !instance instance = new_provider_mock end provider_cls = double("provider_cls") expect(provider_cls).to receive(:new) { |machine| # Store this for later so we can verify that it is the # one we expected to receive. received_machine = machine # Sanity check expect(machine).to be # Yield our machine if we want to do additional tests yield machine if block_given? true }.and_return(instance) # Initialize a new machine and verify that we properly receive # the machine we expect. instance = described_class.new(name, provider_name, provider_cls, provider_config, provider_options, config, data_dir, box, env, env.vagrantfile) expect(received_machine).to eql(instance) end it "should initialize with the machine object" do # Just run the blank test provider_init_test end it "should have the machine name setup" do provider_init_test do |machine| expect(machine.name).to eq(name) end end it "should have the machine configuration" do provider_init_test do |machine| expect(machine.config).to eql(config) end end it "should have the box" do provider_init_test do |machine| expect(machine.box).to eql(box) end end it "should have the environment" do provider_init_test do |machine| expect(machine.env).to eql(env) end end it "should have the vagrantfile" do provider_init_test do |machine| expect(machine.vagrantfile).to equal(env.vagrantfile) end end it "should have access to the ID" do # Stub this because #id= calls it. allow(provider).to receive(:machine_id_changed) # Set the ID on the previous instance so that it is persisted instance.id = "foo" provider_init_test do |machine| expect(machine.id).to eq("foo") end end it "should NOT have access to the provider" do provider_init_test do |machine| expect(machine.provider).to be_nil end end it "should initialize the capabilities" do instance = new_provider_mock expect(instance).to receive(:_initialize).with(any_args) { |p, m| expect(p).to eq(provider_name) expect(m.name).to eq(name) true } provider_init_test(instance) end end end describe "attributes" do describe '#name' do subject { super().name } it { should eq(name) } end describe '#config' do subject { super().config } it { should eql(config) } end describe '#box' do subject { super().box } it { should eql(box) } end describe '#env' do subject { super().env } it { should eql(env) } end describe '#provider' do subject { super().provider } it { should eql(provider) } end describe '#provider_config' do subject { super().provider_config } it { should eql(provider_config) } end describe '#provider_options' do subject { super().provider_options } it { should eq(provider_options) } end end describe "#action" do it "should be able to run an action that exists" do action_name = :up called = false callable = lambda { |_env| called = true } expect(provider).to receive(:action).with(action_name).and_return(callable) instance.action(:up) expect(called).to be end it "should provide the machine in the environment" do action_name = :up machine = nil callable = lambda { |env| machine = env[:machine] } allow(provider).to receive(:action).with(action_name).and_return(callable) instance.action(:up) expect(machine).to eql(instance) end it "should pass any extra options to the environment" do action_name = :up foo = nil callable = lambda { |env| foo = env[:foo] } allow(provider).to receive(:action).with(action_name).and_return(callable) instance.action(:up, foo: :bar) expect(foo).to eq(:bar) end it "should pass any extra options to the environment as strings" do action_name = :up foo = nil callable = lambda { |env| foo = env["foo"] } allow(provider).to receive(:action).with(action_name).and_return(callable) instance.action(:up, "foo" => :bar) expect(foo).to eq(:bar) end it "should return the environment as a result" do action_name = :up callable = lambda { |env| env[:result] = "FOO" } allow(provider).to receive(:action).with(action_name).and_return(callable) result = instance.action(action_name) expect(result[:result]).to eq("FOO") end it "should raise an exception if the action is not implemented" do action_name = :up allow(provider).to receive(:action).with(action_name).and_return(nil) expect { instance.action(action_name) }. to raise_error(Vagrant::Errors::UnimplementedProviderAction) end it 'should not warn if the machines cwd has not changed' do initial_action_name = :up second_action_name = :reload callable = lambda { |_env| } original_cwd = env.cwd.to_s allow(provider).to receive(:action).with(initial_action_name).and_return(callable) allow(provider).to receive(:action).with(second_action_name).and_return(callable) allow(subject.ui).to receive(:warn) instance.action(initial_action_name) expect(subject.ui).to_not have_received(:warn) instance.action(second_action_name) expect(subject.ui).to_not have_received(:warn) end it 'should warn if the machine was last run under a different directory' do action_name = :up callable = lambda { |_env| } original_cwd = env.cwd.to_s allow(provider).to receive(:action).with(action_name).and_return(callable) allow(subject.ui).to receive(:warn) instance.action(action_name) expect(subject.ui).to_not have_received(:warn) # Whenever the machine is run on a different directory, the user is warned allow(env).to receive(:root_path).and_return('/a/new/path') instance.action(action_name) expect(subject.ui).to have_received(:warn) do |warn_msg| expect(warn_msg).to include(original_cwd) expect(warn_msg).to include('/a/new/path') end end context "if in a subdir" do let (:data_dir) { env.cwd } it 'should not warn if vagrant is run in subdirectory' do action_name = :up callable = lambda { |_env| } original_cwd = env.cwd.to_s allow(provider).to receive(:action).with(action_name).and_return(callable) allow(subject.ui).to receive(:warn) instance.action(action_name) expect(subject.ui).to_not have_received(:warn) # mock out cwd to be subdir and ensure no warn is printed allow(env).to receive(:cwd).and_return("#{original_cwd}/a/new/path") instance.action(action_name) expect(subject.ui).to_not have_received(:warn) end end end describe "#action_raw" do let(:callable) {lambda { |e| e[:called] = true @env = e }} before do @env = {} end it "should run the callable with the proper env" do subject.action_raw(:foo, callable) expect(@env[:called]).to be(true) expect(@env[:action_name]).to eq(:machine_action_foo) expect(@env[:machine]).to equal(subject) expect(@env[:machine_action]).to eq(:foo) expect(@env[:ui]).to equal(subject.ui) end it "should return the environment as a result" do result = subject.action_raw(:foo, callable) expect(result).to equal(@env) end it "should merge in any extra env" do subject.action_raw(:bar, callable, foo: :bar) expect(@env[:called]).to be(true) expect(@env[:foo]).to eq(:bar) end end describe "#communicate" do it "should return the SSH communicator by default" do expect(subject.communicate). to be_kind_of(VagrantPlugins::CommunicatorSSH::Communicator) end it "should return the specified communicator if given" do subject.config.vm.communicator = :winrm expect(subject.communicate). to be_kind_of(VagrantPlugins::CommunicatorWinRM::Communicator) end it "should memoize the result" do obj = subject.communicate expect(subject.communicate).to equal(obj) end it "raises an exception if an invalid communicator is given" do subject.config.vm.communicator = :foo expect { subject.communicate }. to raise_error(Vagrant::Errors::CommunicatorNotFound) end end describe "guest implementation" do let(:communicator) do result = double("communicator") allow(result).to receive(:ready?).and_return(true) allow(result).to receive(:test).and_return(false) result end before(:each) do test_guest = Class.new(Vagrant.plugin("2", :guest)) do def detect?(machine) true end end register_plugin do |p| p.guest(:test) { test_guest } end allow(instance).to receive(:communicate).and_return(communicator) end it "should raise an exception if communication is not ready" do expect(communicator).to receive(:ready?).and_return(false) expect { instance.guest }. to raise_error(Vagrant::Errors::MachineGuestNotReady) end it "should return the configured guest" do result = instance.guest expect(result).to be_kind_of(Vagrant::Guest) expect(result).to be_ready expect(result.capability_host_chain[0][0]).to eql(:test) end end describe "setting the ID" do before(:each) do allow(provider).to receive(:machine_id_changed) end it "should not have an ID by default" do expect(instance.id).to be_nil end it "should set an ID" do instance.id = "bar" expect(instance.id).to eq("bar") end it "should notify the machine that the ID changed" do expect(provider).to receive(:machine_id_changed).once instance.id = "bar" end it "should persist the ID" do instance.id = "foo" expect(new_instance.id).to eq("foo") end it "should delete the ID" do instance.id = "foo" second = new_instance expect(second.id).to eq("foo") second.id = nil expect(second.id).to be_nil third = new_instance expect(third.id).to be_nil end it "should set the UID that created the machine" do instance.id = "foo" second = new_instance expect(second.uid).to eq(Process.uid.to_s) end it "should delete the UID when the id is nil" do instance.id = "foo" instance.id = nil second = new_instance expect(second.uid).to be_nil end end describe "#index_uuid" do before(:each) do allow(provider).to receive(:machine_id_changed) end it "should not have an index UUID by default" do expect(subject.index_uuid).to be_nil end it "is set one when setting an ID" do # Stub the message we want allow(provider).to receive(:state).and_return(Vagrant::MachineState.new( :preparing, "preparing", "preparing")) # Setup the box information box = double("box") allow(box).to receive(:name).and_return("foo") allow(box).to receive(:provider).and_return(:bar) allow(box).to receive(:version).and_return("1.2.3") subject.box = box subject.id = "foo" uuid = subject.index_uuid expect(uuid).to_not be_nil expect(new_instance.index_uuid).to eq(uuid) # Test the entry itself entry = env.machine_index.get(uuid) expect(entry.name).to eq(subject.name) expect(entry.provider).to eq(subject.provider_name.to_s) expect(entry.state).to eq("preparing") expect(entry.vagrantfile_path).to eq(env.root_path) expect(entry.vagrantfile_name).to eq(env.vagrantfile_name) expect(entry.extra_data["box"]).to eq({ "name" => box.name, "provider" => box.provider.to_s, "version" => box.version, }) env.machine_index.release(entry) end it "deletes the UUID when setting to nil" do subject.id = "foo" uuid = subject.index_uuid subject.id = nil expect(subject.index_uuid).to be_nil expect(env.machine_index.get(uuid)).to be_nil end end describe "#reload" do before do allow(provider).to receive(:machine_id_changed) subject.id = "foo" end it "should read the ID" do expect(provider).to_not receive(:machine_id_changed) subject.reload expect(subject.id).to eq("foo") end it "should read the updated ID" do new_instance.id = "bar" expect(provider).to receive(:machine_id_changed) subject.reload expect(subject.id).to eq("bar") end end describe "#ssh_info" do describe "with the provider returning nil" do it "should return nil if the provider returns nil" do expect(provider).to receive(:ssh_info).and_return(nil) expect(instance.ssh_info).to be_nil end end describe "with the provider returning data" do let(:provider_ssh_info) { {} } let(:ssh_klass) { Vagrant::Util::SSH } before(:each) do allow(provider).to receive(:ssh_info).and_return(provider_ssh_info) # Stub the check_key_permissions method so that even if we test incorrectly, # no side effects actually happen. allow(ssh_klass).to receive(:check_key_permissions) end [:host, :port, :username].each do |type| it "should return the provider data if not configured in Vagrantfile" do provider_ssh_info[type] = "foo" instance.config.ssh.send("#{type}=", nil) expect(instance.ssh_info[type]).to eq("foo") end it "should return the Vagrantfile value if provider data not given" do provider_ssh_info[type] = nil instance.config.ssh.send("#{type}=", "bar") expect(instance.ssh_info[type]).to eq("bar") end it "should use the default if no override and no provider" do provider_ssh_info[type] = nil instance.config.ssh.send("#{type}=", nil) instance.config.ssh.default.send("#{type}=", "foo") expect(instance.ssh_info[type]).to eq("foo") end it "should use the override if set even with a provider" do provider_ssh_info[type] = "baz" instance.config.ssh.send("#{type}=", "bar") instance.config.ssh.default.send("#{type}=", "foo") expect(instance.ssh_info[type]).to eq("bar") end end it "should set the configured forward agent settings" do provider_ssh_info[:forward_agent] = true instance.config.ssh.forward_agent = false expect(instance.ssh_info[:forward_agent]).to eq(false) end it "should set the configured forward X11 settings" do provider_ssh_info[:forward_x11] = true instance.config.ssh.forward_x11 = false expect(instance.ssh_info[:forward_x11]).to eq(false) end it "should return the provider private key if given" do provider_ssh_info[:private_key_path] = "/foo" expect(instance.ssh_info[:private_key_path]).to eq([File.expand_path("/foo", env.root_path)]) end it "should return the configured SSH key path if set" do provider_ssh_info[:private_key_path] = nil instance.config.ssh.private_key_path = "/bar" expect(instance.ssh_info[:private_key_path]).to eq([File.expand_path("/bar", env.root_path)]) end it "should return the array of SSH keys if set" do provider_ssh_info[:private_key_path] = nil instance.config.ssh.private_key_path = ["/foo", "/bar"] expect(instance.ssh_info[:private_key_path]).to eq([ File.expand_path("/foo", env.root_path), File.expand_path("/bar", env.root_path), ]) end it "should check and try to fix the permissions of the default private key file" do provider_ssh_info[:private_key_path] = nil instance.config.ssh.private_key_path = nil expect(ssh_klass).to receive(:check_key_permissions).once.with(Pathname.new(instance.env.default_private_key_path.to_s)) instance.ssh_info end it "should check and try to fix the permissions of given private key files" do provider_ssh_info[:private_key_path] = nil # Use __FILE__ to provide an existing file instance.config.ssh.private_key_path = [File.expand_path(__FILE__), File.expand_path(__FILE__)] expect(ssh_klass).to receive(:check_key_permissions).twice.with(Pathname.new(File.expand_path(__FILE__))) instance.ssh_info end it "should not check the permissions of a private key file that does not exist" do provider_ssh_info[:private_key_path] = "/foo" expect(ssh_klass).to_not receive(:check_key_permissions) instance.ssh_info end context "expanding path relative to the root path" do it "should with the provider key path" do provider_ssh_info[:private_key_path] = "~/foo" expect(instance.ssh_info[:private_key_path]).to eq( [File.expand_path("~/foo", env.root_path)] ) end it "should with the config private key path" do provider_ssh_info[:private_key_path] = nil instance.config.ssh.private_key_path = "~/bar" expect(instance.ssh_info[:private_key_path]).to eq( [File.expand_path("~/bar", env.root_path)] ) end end it "should return the default private key path if provider and config doesn't have one" do provider_ssh_info[:private_key_path] = nil instance.config.ssh.private_key_path = nil expect(instance.ssh_info[:private_key_path]).to eq( [instance.env.default_private_key_path.to_s] ) end it "should not set any default private keys if a password is specified" do provider_ssh_info[:private_key_path] = nil instance.config.ssh.private_key_path = nil instance.config.ssh.password = "" expect(instance.ssh_info[:private_key_path]).to be_empty expect(instance.ssh_info[:password]).to eql("") end it "should return the private key in the data dir above all else" do provider_ssh_info[:private_key_path] = nil instance.config.ssh.private_key_path = nil instance.config.ssh.password = "" instance.data_dir.join("private_key").open("w+") do |f| f.write("hey") end expect(instance.ssh_info[:private_key_path]).to eql( [instance.data_dir.join("private_key").to_s]) expect(instance.ssh_info[:password]).to eql("") end it "should return the private key in the Vagrantfile if the data dir exists" do path = "/foo" path = "C:/foo" if Vagrant::Util::Platform.windows? provider_ssh_info[:private_key_path] = nil instance.config.ssh.private_key_path = path instance.data_dir.join("private_key").open("w+") do |f| f.write("hey") end expect(instance.ssh_info[:private_key_path]).to eql([path]) end context "with no data dir" do let(:base) { true } let(:data_dir) { nil } it "returns nil as the private key path" do provider_ssh_info[:private_key_path] = nil instance.config.ssh.private_key_path = nil instance.config.ssh.password = "" expect(instance.ssh_info[:private_key_path]).to be_empty expect(instance.ssh_info[:password]).to eql("") end end context "with custom ssh_info" do it "keys_only should be default" do expect(instance.ssh_info[:keys_only]).to be(true) end it "verify_host_key should be default" do expect(instance.ssh_info[:verify_host_key]).to be(false) end it "extra_args should be nil" do expect(instance.ssh_info[:extra_args]).to be(nil) end it "extra_args should be set" do instance.config.ssh.extra_args = ["-L", "127.1.2.7:8008:127.1.2.7:8008"] expect(instance.ssh_info[:extra_args]).to eq(["-L", "127.1.2.7:8008:127.1.2.7:8008"]) end it "extra_args should be set as an array" do instance.config.ssh.extra_args = "-6" expect(instance.ssh_info[:extra_args]).to eq("-6") end it "keys_only should be overridden" do instance.config.ssh.keys_only = false expect(instance.ssh_info[:keys_only]).to be(false) end it "verify_host_key should be overridden" do instance.config.ssh.verify_host_key = true expect(instance.ssh_info[:verify_host_key]).to be(true) end end end end describe "#state" do it "should query state from the provider" do state = Vagrant::MachineState.new(:id, "short", "long") allow(provider).to receive(:state).and_return(state) expect(instance.state.id).to eq(:id) end it "should raise an exception if a MachineState is not returned" do expect(provider).to receive(:state).and_return(:old_school) expect { instance.state }. to raise_error(Vagrant::Errors::MachineStateInvalid) end it "should save the state with the index" do allow(provider).to receive(:machine_id_changed) subject.id = "foo" state = Vagrant::MachineState.new(:id, "short", "long") expect(provider).to receive(:state).and_return(state) subject.state entry = env.machine_index.get(subject.index_uuid) expect(entry).to_not be_nil expect(entry.state).to eq("short") env.machine_index.release(entry) end end describe "#with_ui" do it "temporarily changes the UI" do ui = Object.new changed_ui = nil subject.with_ui(ui) do changed_ui = subject.ui end expect(changed_ui).to equal(ui) expect(subject.ui).to_not equal(ui) end end end vagrant-2.0.2/test/unit/vagrant/plugin/000077500000000000000000000000001323370221500200515ustar00rootroot00000000000000vagrant-2.0.2/test/unit/vagrant/plugin/manager_test.rb000066400000000000000000000163271323370221500230600ustar00rootroot00000000000000require "json" require "pathname" require "vagrant/plugin" require "vagrant/plugin/manager" require "vagrant/plugin/state_file" require "vagrant/util/deep_merge" require File.expand_path("../../../base", __FILE__) describe Vagrant::Plugin::Manager do include_context "unit" let(:path) do Pathname.new(Dir::Tmpname.create("vagrant-test-plugin-manager") {}) end let(:bundler) { double("bundler") } after do path.unlink if path.file? end before do allow(Vagrant::Bundler).to receive(:instance).and_return(bundler) end subject { described_class.new(path) } describe "#install_plugin" do it "installs the plugin and adds it to the state file" do specs = Array.new(5) { Gem::Specification.new } specs[3].name = "foo" expect(bundler).to receive(:install).once.with(any_args) { |plugins, local| expect(plugins).to have_key("foo") expect(local).to be(false) }.and_return(specs) expect(bundler).to receive(:clean) result = subject.install_plugin("foo") # It should return the spec of the installed plugin expect(result).to eql(specs[3]) # It should've added the plugin to the state expect(subject.installed_plugins).to have_key("foo") end it "masks GemNotFound with our error" do expect(bundler).to receive(:install).and_raise(Gem::GemNotFoundException) expect { subject.install_plugin("foo") }. to raise_error(Vagrant::Errors::PluginGemNotFound) end it "masks bundler errors with our own error" do expect(bundler).to receive(:install).and_raise(Gem::InstallError) expect { subject.install_plugin("foo") }. to raise_error(Vagrant::Errors::BundlerError) end it "can install a local gem" do name = "foo.gem" version = "1.0" local_spec = Gem::Specification.new local_spec.name = "bar" local_spec.version = version expect(bundler).to receive(:install_local).with(name, {}). ordered.and_return(local_spec) expect(bundler).not_to receive(:install) expect(bundler).to receive(:clean) subject.install_plugin(name) plugins = subject.installed_plugins expect(plugins).to have_key("bar") expect(plugins["bar"]["gem_version"]).to eql("1.0") end describe "installation options" do let(:specs) do specs = Array.new(5) { Gem::Specification.new } specs[3].name = "foo" specs end before do allow(bundler).to receive(:install).and_return(specs) end it "installs a version with constraints" do expect(bundler).to receive(:install).once.with(any_args) { |plugins, local| expect(plugins).to have_key("foo") expect(plugins["foo"]["gem_version"]).to eql(">= 0.1.0") expect(local).to be(false) }.and_return(specs) expect(bundler).to receive(:clean) subject.install_plugin("foo", version: ">= 0.1.0") plugins = subject.installed_plugins expect(plugins).to have_key("foo") expect(plugins["foo"]["gem_version"]).to eql(">= 0.1.0") end it "installs with an exact version but doesn't constrain" do expect(bundler).to receive(:install).once.with(any_args) { |plugins, local| expect(plugins).to have_key("foo") expect(plugins["foo"]["gem_version"]).to eql("0.1.0") expect(local).to be(false) }.and_return(specs) expect(bundler).to receive(:clean) subject.install_plugin("foo", version: "0.1.0") plugins = subject.installed_plugins expect(plugins).to have_key("foo") expect(plugins["foo"]["gem_version"]).to eql("0.1.0") end end end describe "#uninstall_plugin" do it "removes the plugin from the state" do sf = Vagrant::Plugin::StateFile.new(path) sf.add_plugin("foo") # Sanity expect(subject.installed_plugins).to have_key("foo") # Test expect(bundler).to receive(:clean).once.with({}) # Remove it subject.uninstall_plugin("foo") expect(subject.installed_plugins).to_not have_key("foo") end it "masks bundler errors with our own error" do expect(bundler).to receive(:clean).and_raise(Gem::InstallError) expect { subject.uninstall_plugin("foo") }. to raise_error(Vagrant::Errors::BundlerError) end context "with a system file" do let(:systems_path) { temporary_file } before do systems_path.unlink allow(described_class).to receive(:system_plugins_file).and_return(systems_path) sf = Vagrant::Plugin::StateFile.new(systems_path) sf.add_plugin("foo", version: "0.2.0") sf.add_plugin("bar") end it "uninstalls the user plugin if it exists" do sf = Vagrant::Plugin::StateFile.new(path) sf.add_plugin("bar") # Test expect(bundler).to receive(:clean).once.with(anything) # Remove it subject.uninstall_plugin("bar") plugins = subject.installed_plugins expect(plugins["foo"]["system"]).to be(true) end it "raises an error if uninstalling a system gem" do expect { subject.uninstall_plugin("bar") }. to raise_error(Vagrant::Errors::PluginUninstallSystem) end end end describe "#update_plugins" do it "masks bundler errors with our own error" do expect(bundler).to receive(:update).and_raise(Gem::InstallError) expect { subject.update_plugins([]) }. to raise_error(Vagrant::Errors::BundlerError) end end context "without state" do describe "#installed_plugins" do it "is empty initially" do expect(subject.installed_plugins).to be_empty end end end context "with state" do before do sf = Vagrant::Plugin::StateFile.new(path) sf.add_plugin("foo", version: "0.1.0") end describe "#installed_plugins" do it "has the plugins" do plugins = subject.installed_plugins expect(plugins.length).to eql(1) expect(plugins).to have_key("foo") end end describe "#installed_specs" do it "has the plugins" do # We just add "i18n" because it is a dependency of Vagrant and # we know it will be there. sf = Vagrant::Plugin::StateFile.new(path) sf.add_plugin("i18n") specs = subject.installed_specs expect(specs.length).to eql(1) expect(specs.first.name).to eql("i18n") end end context "with system plugins" do let(:systems_path) { temporary_file } before do systems_path.unlink allow(described_class).to receive(:system_plugins_file).and_return(systems_path) sf = Vagrant::Plugin::StateFile.new(systems_path) sf.add_plugin("foo", version: "0.2.0") sf.add_plugin("bar") end describe "#installed_plugins" do it "has the plugins" do plugins = subject.installed_plugins expect(plugins.length).to eql(2) expect(plugins).to have_key("foo") expect(plugins["foo"]["gem_version"]).to eq("0.1.0") expect(plugins["foo"]["system"]).to be_truthy expect(plugins).to have_key("bar") expect(plugins["bar"]["system"]).to be(true) end end end end end vagrant-2.0.2/test/unit/vagrant/plugin/state_file_test.rb000066400000000000000000000057701323370221500235650ustar00rootroot00000000000000require "json" require "pathname" require File.expand_path("../../../base", __FILE__) describe Vagrant::Plugin::StateFile do let(:path) do Pathname.new(Dir::Tmpname.create("vagrant-test-statefile") {}) end after do path.unlink if path.file? end subject { described_class.new(path) } context "new usage" do it "should have no plugins without saving some" do expect(subject.installed_plugins).to be_empty end it "should have plugins when saving" do subject.add_plugin("foo") instance = described_class.new(path) plugins = instance.installed_plugins expect(plugins.length).to eql(1) expect(plugins["foo"]).to eql({ "ruby_version" => RUBY_VERSION, "vagrant_version" => Vagrant::VERSION, "gem_version" => "", "require" => "", "sources" => [], "installed_gem_version" => nil, }) end it "should check for plugins" do expect(subject.has_plugin?("foo")).to be(false) subject.add_plugin("foo") expect(subject.has_plugin?("foo")).to be(true) end it "should remove plugins" do subject.add_plugin("foo") subject.remove_plugin("foo") instance = described_class.new(path) expect(instance.installed_plugins).to be_empty end it "should store plugins uniquely" do subject.add_plugin("foo") subject.add_plugin("foo") instance = described_class.new(path) expect(instance.installed_plugins.keys).to eql(["foo"]) end it "should store metadata" do subject.add_plugin("foo", version: "1.2.3") expect(subject.installed_plugins["foo"]["gem_version"]).to eql("1.2.3") end describe "sources" do it "should have no sources" do expect(subject.sources).to be_empty end it "should add sources" do subject.add_source("foo") expect(subject.sources).to eql(["foo"]) end it "should de-dup sources" do subject.add_source("foo") subject.add_source("foo") expect(subject.sources).to eql(["foo"]) end it "can remove sources" do subject.add_source("foo") subject.remove_source("foo") expect(subject.sources).to be_empty end end end context "with an old-style file" do before do data = { "installed" => ["foo"], } path.open("w+") do |f| f.write(JSON.dump(data)) end end it "should have the right installed plugins" do plugins = subject.installed_plugins expect(plugins.keys).to eql(["foo"]) expect(plugins["foo"]["ruby_version"]).to eql("0") expect(plugins["foo"]["vagrant_version"]).to eql("0") end end context "with parse errors" do before do path.open("w+") do |f| f.write("I'm not json") end end it "should raise a VagrantError" do expect { subject }. to raise_error(Vagrant::Errors::PluginStateFileParseError) end end end vagrant-2.0.2/test/unit/vagrant/plugin/v1/000077500000000000000000000000001323370221500203775ustar00rootroot00000000000000vagrant-2.0.2/test/unit/vagrant/plugin/v1/command_test.rb000066400000000000000000000106351323370221500234060ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require 'optparse' describe Vagrant::Plugin::V1::Command do describe "parsing options" do let(:klass) do Class.new(described_class) do # Make the method public since it is normally protected public :parse_options end end it "returns the remaining arguments" do options = {} opts = OptionParser.new do |opts| opts.on("-f") do |f| options[:f] = f end end result = klass.new(["-f", "foo"], nil).parse_options(opts) # Check the results expect(options[:f]).to be expect(result).to eq(["foo"]) end it "creates an option parser if none is given" do result = klass.new(["foo"], nil).parse_options(nil) expect(result).to eq(["foo"]) end ["-h", "--help"].each do |help_string| it "returns nil and prints the help if '#{help_string}' is given" do instance = klass.new([help_string], nil) expect(instance).to receive(:safe_puts) expect(instance.parse_options(OptionParser.new)).to be_nil end end it "raises an error if invalid options are given" do instance = klass.new(["-f"], nil) expect { instance.parse_options(OptionParser.new) }. to raise_error(Vagrant::Errors::CLIInvalidOptions) end end describe "target VMs" do let(:klass) do Class.new(described_class) do # Make the method public since it is normally protected public :with_target_vms end end let(:environment) do env = double("environment") allow(env).to receive(:root_path).and_return("foo") env end let(:instance) { klass.new([], environment) } it "should raise an exception if a root_path is not available" do allow(environment).to receive(:root_path).and_return(nil) expect { instance.with_target_vms }. to raise_error(Vagrant::Errors::NoEnvironmentError) end it "should yield every VM in order is no name is given" do foo_vm = double("foo") allow(foo_vm).to receive(:name).and_return("foo") bar_vm = double("bar") allow(bar_vm).to receive(:name).and_return("bar") allow(environment).to receive(:multivm?).and_return(true) allow(environment).to receive(:vms).and_return({ "foo" => foo_vm, "bar" => bar_vm }) allow(environment).to receive(:vms_ordered).and_return([foo_vm, bar_vm]) vms = [] instance.with_target_vms do |vm| vms << vm end expect(vms).to eq([foo_vm, bar_vm]) end it "raises an exception if the named VM doesn't exist" do allow(environment).to receive(:multivm?).and_return(true) allow(environment).to receive(:vms).and_return({}) expect { instance.with_target_vms("foo") }. to raise_error(Vagrant::Errors::VMNotFoundError) end it "yields the given VM if a name is given" do foo_vm = double("foo") allow(foo_vm).to receive(:name).and_return(:foo) allow(environment).to receive(:multivm?).and_return(true) allow(environment).to receive(:vms).and_return({ foo: foo_vm, bar: nil }) vms = [] instance.with_target_vms("foo") { |vm| vms << vm } expect(vms).to eq([foo_vm]) end end describe "splitting the main and subcommand args" do let(:instance) do Class.new(described_class) do # Make the method public since it is normally protected public :split_main_and_subcommand end.new(nil, nil) end it "should work when given all 3 parts" do result = instance.split_main_and_subcommand(["-v", "status", "-h", "-v"]) expect(result).to eq([["-v"], "status", ["-h", "-v"]]) end it "should work when given only a subcommand and args" do result = instance.split_main_and_subcommand(["status", "-h"]) expect(result).to eq([[], "status", ["-h"]]) end it "should work when given only main flags" do result = instance.split_main_and_subcommand(["-v", "-h"]) expect(result).to eq([["-v", "-h"], nil, []]) end it "should work when given only a subcommand" do result = instance.split_main_and_subcommand(["status"]) expect(result).to eq([[], "status", []]) end it "works when there are other non-flag args after the subcommand" do result = instance.split_main_and_subcommand(["-v", "box", "add", "-h"]) expect(result).to eq([["-v"], "box", ["add", "-h"]]) end end end vagrant-2.0.2/test/unit/vagrant/plugin/v1/communicator_test.rb000066400000000000000000000003551323370221500244660ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V1::Communicator do let(:machine) { Object.new } it "should not match by default" do expect(described_class.match?(machine)).not_to be end end vagrant-2.0.2/test/unit/vagrant/plugin/v1/config_test.rb000066400000000000000000000023261323370221500232330ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V1::Config do include_context "unit" let(:foo_class) do Class.new(described_class) do attr_accessor :one attr_accessor :two end end it "has an UNSET_VALUE constant" do value = described_class.const_get("UNSET_VALUE") expect(value).to be_kind_of Object expect(value).to eql(described_class.const_get("UNSET_VALUE")) end describe "merging" do it "should merge by default by simply copying each instance variable" do one = foo_class.new one.one = 2 one.two = 1 two = foo_class.new two.two = 5 result = one.merge(two) expect(result.one).to eq(2) expect(result.two).to eq(5) end it "doesn't merge values that start with a double underscore" do one = foo_class.new one.one = 1 one.two = 1 one.instance_variable_set(:@__bar, "one") two = foo_class.new two.two = 2 two.instance_variable_set(:@__bar, "two") # Merge and verify result = one.merge(two) expect(result.one).to eq(1) expect(result.two).to eq(2) expect(result.instance_variable_get(:@__bar)).to be_nil end end end vagrant-2.0.2/test/unit/vagrant/plugin/v1/host_test.rb000066400000000000000000000001601323370221500227350ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V1::Host do # No tests. end vagrant-2.0.2/test/unit/vagrant/plugin/v1/manager_test.rb000066400000000000000000000050061323370221500233760ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V1::Manager do include_context "unit" let(:instance) { described_class.new } def plugin p = Class.new(Vagrant.plugin("1")) yield p p end it "should enumerate registered communicator classes" do pA = plugin do |p| p.communicator("foo") { "bar" } end pB = plugin do |p| p.communicator("bar") { "baz" } end instance.register(pA) instance.register(pB) expect(instance.communicators.length).to eq(2) expect(instance.communicators[:foo]).to eq("bar") expect(instance.communicators[:bar]).to eq("baz") end it "should enumerate registered configuration classes" do pA = plugin do |p| p.config("foo") { "bar" } end pB = plugin do |p| p.config("bar") { "baz" } end instance.register(pA) instance.register(pB) expect(instance.config.length).to eq(2) expect(instance.config[:foo]).to eq("bar") expect(instance.config[:bar]).to eq("baz") end it "should enumerate registered upgrade safe config classes" do pA = plugin do |p| p.config("foo", true) { "bar" } end pB = plugin do |p| p.config("bar") { "baz" } end instance.register(pA) instance.register(pB) expect(instance.config_upgrade_safe.length).to eq(1) expect(instance.config_upgrade_safe[:foo]).to eq("bar") end it "should enumerate registered guest classes" do pA = plugin do |p| p.guest("foo") { "bar" } end pB = plugin do |p| p.guest("bar") { "baz" } end instance.register(pA) instance.register(pB) expect(instance.guests.length).to eq(2) expect(instance.guests[:foo]).to eq("bar") expect(instance.guests[:bar]).to eq("baz") end it "should enumerate registered host classes" do pA = plugin do |p| p.host("foo") { "bar" } end pB = plugin do |p| p.host("bar") { "baz" } end instance.register(pA) instance.register(pB) expect(instance.hosts.length).to eq(2) expect(instance.hosts[:foo]).to eq("bar") expect(instance.hosts[:bar]).to eq("baz") end it "should enumerate registered provider classes" do pA = plugin do |p| p.provider("foo") { "bar" } end pB = plugin do |p| p.provider("bar") { "baz" } end instance.register(pA) instance.register(pB) expect(instance.providers.length).to eq(2) expect(instance.providers[:foo]).to eq("bar") expect(instance.providers[:bar]).to eq("baz") end end vagrant-2.0.2/test/unit/vagrant/plugin/v1/plugin_test.rb000066400000000000000000000170531323370221500232670ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V1::Plugin do after(:each) do # We want to make sure that the registered plugins remains empty # after each test. described_class.manager.reset! end it "should be able to set and get the name" do plugin = Class.new(described_class) do name "foo" end expect(plugin.name).to eq("foo") end it "should be able to set and get the description" do plugin = Class.new(described_class) do description "bar" end expect(plugin.description).to eq("bar") end describe "action hooks" do it "should register action hooks" do plugin = Class.new(described_class) do action_hook("foo") { "bar" } end hooks = plugin.action_hook("foo") expect(hooks.length).to eq(1) expect(hooks[0].call).to eq("bar") end end describe "commands" do it "should register command classes" do plugin = Class.new(described_class) do command("foo") { "bar" } end expect(plugin.command[:foo]).to eq("bar") end ["spaces bad", "sym^bols"].each do |bad| it "should not allow bad command name: #{bad}" do plugin = Class.new(described_class) expect { plugin.command(bad) {} }. to raise_error(Vagrant::Plugin::V1::InvalidCommandName) end end it "should lazily register command classes" do # Below would raise an error if the value of the command class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do command("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the command key that # a proper error is raised. expect { plugin.command[:foo] }.to raise_error(StandardError) end end describe "communicators" do it "should register communicator classes" do plugin = Class.new(described_class) do communicator("foo") { "bar" } end expect(plugin.communicator[:foo]).to eq("bar") end it "should lazily register communicator classes" do # Below would raise an error if the value of the class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do communicator("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the configuration key that # a proper error is raised. expect { plugin.communicator[:foo] }.to raise_error(StandardError) end end describe "configuration" do it "should register configuration classes" do plugin = Class.new(described_class) do config("foo") { "bar" } end expect(plugin.config[:foo]).to eq("bar") end it "should lazily register configuration classes" do # Below would raise an error if the value of the config class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do config("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the configuration key that # a proper error is raised. expect { plugin.config[:foo] }.to raise_error(StandardError) end end describe "guests" do it "should register guest classes" do plugin = Class.new(described_class) do guest("foo") { "bar" } end expect(plugin.guest[:foo]).to eq("bar") end it "should lazily register guest classes" do # Below would raise an error if the value of the guest class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do guest("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the guest key that # a proper error is raised. expect { plugin.guest[:foo] }.to raise_error(StandardError) end end describe "hosts" do it "should register host classes" do plugin = Class.new(described_class) do host("foo") { "bar" } end expect(plugin.host[:foo]).to eq("bar") end it "should lazily register host classes" do # Below would raise an error if the value of the host class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do host("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the host key that # a proper error is raised. expect { plugin.host[:foo] }.to raise_error(StandardError) end end describe "providers" do it "should register provider classes" do plugin = Class.new(described_class) do provider("foo") { "bar" } end expect(plugin.provider[:foo]).to eq("bar") end it "should lazily register provider classes" do # Below would raise an error if the value of the config class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do provider("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the configuration key that # a proper error is raised. expect { plugin.provider[:foo] }.to raise_error(StandardError) end end describe "provisioners" do it "should register provisioner classes" do plugin = Class.new(described_class) do provisioner("foo") { "bar" } end expect(plugin.provisioner[:foo]).to eq("bar") end it "should lazily register provisioner classes" do # Below would raise an error if the value of the config class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do provisioner("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the configuration key that # a proper error is raised. expect { plugin.provisioner[:foo] }.to raise_error(StandardError) end end describe "plugin registration" do let(:manager) { described_class.manager } it "should have no registered plugins" do expect(manager.registered).to be_empty end it "should register a plugin when a name is set" do plugin = Class.new(described_class) do name "foo" end expect(manager.registered).to eq([plugin]) end it "should register a plugin only once" do plugin = Class.new(described_class) do name "foo" name "bar" end expect(manager.registered).to eq([plugin]) end end end vagrant-2.0.2/test/unit/vagrant/plugin/v1/provider_test.rb000066400000000000000000000007431323370221500236210ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V1::Provider do let(:machine) { Object.new } let(:instance) { described_class.new(machine) } it "should return nil by default for actions" do expect(instance.action(:whatever)).to be_nil end it "should return nil by default for ssh info" do expect(instance.ssh_info).to be_nil end it "should return nil by default for state" do expect(instance.state).to be_nil end end vagrant-2.0.2/test/unit/vagrant/plugin/v2/000077500000000000000000000000001323370221500204005ustar00rootroot00000000000000vagrant-2.0.2/test/unit/vagrant/plugin/v2/command_test.rb000066400000000000000000000306141323370221500234060ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require 'optparse' describe Vagrant::Plugin::V2::Command do include_context "unit" describe "parsing options" do let(:klass) do Class.new(described_class) do # Make the method public since it is normally protected public :parse_options end end it "returns the remaining arguments" do options = {} opts = OptionParser.new do |o| o.on("-f") do |f| options[:f] = f end end result = klass.new(["-f", "foo"], nil).parse_options(opts) # Check the results expect(options[:f]).to be expect(result).to eq(["foo"]) end it "creates an option parser if none is given" do result = klass.new(["foo"], nil).parse_options(nil) expect(result).to eq(["foo"]) end ["-h", "--help"].each do |help_string| it "returns nil and prints the help if '#{help_string}' is given" do instance = klass.new([help_string], nil) expect(instance).to receive(:safe_puts) expect(instance.parse_options(OptionParser.new)).to be_nil end end it "raises an error if invalid options are given" do instance = klass.new(["-f"], nil) expect { instance.parse_options(OptionParser.new) }. to raise_error(Vagrant::Errors::CLIInvalidOptions) end it "raises an error if options without a value are given" do opts = OptionParser.new do |o| o.on("--provision-with x,y,z", Array, "Example") { |f| } end instance = klass.new(["--provision-with"], nil) expect { instance.parse_options(opts) }. to raise_error(Vagrant::Errors::CLIInvalidOptions) end end describe "target VMs" do let(:klass) do Class.new(described_class) do # Make the method public since it is normally protected public :with_target_vms end end let(:environment) do # We have to create a Vagrantfile so there is a root path test_iso_env.vagrantfile("") test_iso_env.create_vagrant_env end let(:test_iso_env) { isolated_environment } let(:instance) { klass.new([], environment) } subject { instance } it "should raise an exception if a root_path is not available" do allow(environment).to receive(:root_path).and_return(nil) expect { instance.with_target_vms }. to raise_error(Vagrant::Errors::NoEnvironmentError) end it "should yield every VM in order if no name is given" do foo_vm = double("foo") allow(foo_vm).to receive(:name).and_return("foo") allow(foo_vm).to receive(:provider).and_return(:foobarbaz) allow(foo_vm).to receive(:ui).and_return(Vagrant::UI::Silent.new) allow(foo_vm).to receive(:state).and_return(nil) bar_vm = double("bar") allow(bar_vm).to receive(:name).and_return("bar") allow(bar_vm).to receive(:provider).and_return(:foobarbaz) allow(bar_vm).to receive(:ui).and_return(Vagrant::UI::Silent.new) allow(bar_vm).to receive(:state).and_return(nil) allow(environment).to receive(:machine_names).and_return([:foo, :bar]) allow(environment).to receive(:machine).with(:foo, environment.default_provider).and_return(foo_vm) allow(environment).to receive(:machine).with(:bar, environment.default_provider).and_return(bar_vm) vms = [] instance.with_target_vms do |vm| vms << vm end expect(vms).to eq([foo_vm, bar_vm]) end it "raises an exception if the named VM doesn't exist" do allow(environment).to receive(:machine_names).and_return([:default]) allow(environment).to receive(:machine).with(:foo, anything).and_return(nil) expect { instance.with_target_vms("foo") }. to raise_error(Vagrant::Errors::VMNotFoundError) end it "yields the given VM if a name is given" do foo_vm = double("foo") allow(foo_vm).to receive(:name).and_return("foo") allow(foo_vm).to receive(:provider).and_return(:foobarbaz) allow(foo_vm).to receive(:ui).and_return(Vagrant::UI::Silent.new) allow(foo_vm).to receive(:state).and_return(nil) allow(environment).to receive(:machine).with(:foo, environment.default_provider).and_return(foo_vm) vms = [] instance.with_target_vms("foo") { |vm| vms << vm } expect(vms).to eq([foo_vm]) end it "calls state after yielding the vm to update the machine index" do foo_vm = double("foo") allow(foo_vm).to receive(:name).and_return("foo") allow(foo_vm).to receive(:provider).and_return(:foobarbaz) allow(foo_vm).to receive(:ui).and_return(Vagrant::UI::Silent.new) allow(foo_vm).to receive(:state).and_return(nil) allow(environment).to receive(:machine).with(:foo, environment.default_provider).and_return(foo_vm) vms = [] expect(foo_vm).to receive(:state) instance.with_target_vms("foo") { |vm| vms << vm } end it "yields the given VM with proper provider if given" do foo_vm = double("foo") provider = :foobarbaz allow(foo_vm).to receive(:name).and_return("foo") allow(foo_vm).to receive(:provider).and_return(provider) allow(foo_vm).to receive(:ui).and_return(Vagrant::UI::Silent.new) allow(foo_vm).to receive(:state).and_return(nil) allow(environment).to receive(:machine).with(:foo, provider).and_return(foo_vm) vms = [] instance.with_target_vms("foo", provider: provider) { |vm| vms << vm } expect(vms).to eq([foo_vm]) end it "should raise an exception if an active machine exists with a different provider" do name = :foo allow(environment).to receive(:active_machines).and_return([[name, :vmware]]) expect { instance.with_target_vms(name.to_s, provider: :foo) }. to raise_error Vagrant::Errors::ActiveMachineWithDifferentProvider end it "should default to the active machine provider if no explicit provider requested" do name = :foo provider = :vmware vmware_vm = double("vmware_vm") allow(environment).to receive(:active_machines).and_return([[name, provider]]) allow(environment).to receive(:machine).with(name, provider).and_return(vmware_vm) allow(vmware_vm).to receive(:name).and_return(name) allow(vmware_vm).to receive(:provider).and_return(provider) allow(vmware_vm).to receive(:ui).and_return(Vagrant::UI::Silent.new) allow(vmware_vm).to receive(:state).and_return(nil) vms = [] instance.with_target_vms(name.to_s) { |vm| vms << vm } expect(vms).to eq([vmware_vm]) end it "should use the explicit provider if it maches the active machine" do name = :foo provider = :vmware vmware_vm = double("vmware_vm") allow(environment).to receive(:active_machines).and_return([[name, provider]]) allow(environment).to receive(:machine).with(name, provider).and_return(vmware_vm) allow(vmware_vm).to receive(:name).and_return(name) allow(vmware_vm).to receive(:provider).and_return(provider) allow(vmware_vm).to receive(:ui).and_return(Vagrant::UI::Silent.new) allow(vmware_vm).to receive(:state).and_return(nil) vms = [] instance.with_target_vms(name.to_s, provider: provider) { |vm| vms << vm } expect(vms).to eq([vmware_vm]) end it "should use the default provider if none is given and none are active" do name = :foo machine = double("machine") allow(environment).to receive(:machine).with(name, environment.default_provider).and_return(machine) allow(machine).to receive(:name).and_return(name) allow(machine).to receive(:provider).and_return(environment.default_provider) allow(machine).to receive(:ui).and_return(Vagrant::UI::Silent.new) allow(machine).to receive(:state).and_return(nil) results = [] instance.with_target_vms(name.to_s) { |m| results << m } expect(results).to eq([machine]) end it "should use the primary machine with the active provider" do name = :foo provider = :vmware vmware_vm = double("vmware_vm") allow(environment).to receive(:active_machines).and_return([[name, provider]]) allow(environment).to receive(:machine).with(name, provider).and_return(vmware_vm) allow(environment).to receive(:machine_names).and_return([]) allow(environment).to receive(:primary_machine_name).and_return(name) allow(vmware_vm).to receive(:name).and_return(name) allow(vmware_vm).to receive(:provider).and_return(provider) allow(vmware_vm).to receive(:ui).and_return(Vagrant::UI::Silent.new) allow(vmware_vm).to receive(:state).and_return(nil) vms = [] instance.with_target_vms(nil, single_target: true) { |vm| vms << vm } expect(vms).to eq([vmware_vm]) end it "should use the primary machine with the default provider" do name = :foo machine = double("machine") allow(environment).to receive(:active_machines).and_return([]) allow(environment).to receive(:machine).with(name, environment.default_provider).and_return(machine) allow(environment).to receive(:machine_names).and_return([]) allow(environment).to receive(:primary_machine_name).and_return(name) allow(machine).to receive(:name).and_return(name) allow(machine).to receive(:provider).and_return(environment.default_provider) allow(machine).to receive(:ui).and_return(Vagrant::UI::Silent.new) allow(machine).to receive(:state).and_return(nil) vms = [] instance.with_target_vms(nil, single_target: true) { |vm| vms << machine } expect(vms).to eq([machine]) end it "should yield machines from another environment" do iso_env = isolated_environment iso_env.vagrantfile("") other_env = iso_env.create_vagrant_env( home_path: environment.home_path) other_machine = other_env.machine( other_env.machine_names[0], other_env.default_provider) # Set an ID on it so that it is "created" in the index other_machine.id = "foo" # Make sure we don't have a root path, to test allow(environment).to receive(:root_path).and_return(nil) results = [] subject.with_target_vms(other_machine.index_uuid) { |m| results << m } expect(results.length).to eq(1) expect(results[0].id).to eq(other_machine.id) end it "should yield machines from another environment" do iso_env = isolated_environment iso_env.vagrantfile("") other_env = iso_env.create_vagrant_env( home_path: environment.home_path) other_machine = other_env.machine( other_env.machine_names[0], other_env.default_provider) # Set an ID on it so that it is "created" in the index other_machine.id = "foo" # Grab the uuid so we know what it is index_uuid = other_machine.index_uuid # Remove the working directory FileUtils.rm_rf(iso_env.workdir) # Make sure we don't have a root path, to test allow(environment).to receive(:root_path).and_return(nil) # Run the command expect { subject.with_target_vms(index_uuid) { |*args| } }.to raise_error(Vagrant::Errors::EnvironmentNonExistentCWD) # Verify that it no longer exists in the index expect(other_env.machine_index.get(index_uuid)).to be_nil end end describe "splitting the main and subcommand args" do let(:instance) do Class.new(described_class) do # Make the method public since it is normally protected public :split_main_and_subcommand end.new(nil, nil) end it "should work when given all 3 parts" do result = instance.split_main_and_subcommand(["-v", "status", "-h", "-v"]) expect(result).to eq([["-v"], "status", ["-h", "-v"]]) end it "should work when given only a subcommand and args" do result = instance.split_main_and_subcommand(["status", "-h"]) expect(result).to eq([[], "status", ["-h"]]) end it "should work when given only main flags" do result = instance.split_main_and_subcommand(["-v", "-h"]) expect(result).to eq([["-v", "-h"], nil, []]) end it "should work when given only a subcommand" do result = instance.split_main_and_subcommand(["status"]) expect(result).to eq([[], "status", []]) end it "works when there are other non-flag args after the subcommand" do result = instance.split_main_and_subcommand(["-v", "box", "add", "-h"]) expect(result).to eq([["-v"], "box", ["add", "-h"]]) end end end vagrant-2.0.2/test/unit/vagrant/plugin/v2/communicator_test.rb000066400000000000000000000001521323370221500244620ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V2::Communicator do end vagrant-2.0.2/test/unit/vagrant/plugin/v2/components_test.rb000066400000000000000000000010431323370221500241470ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require "vagrant/registry" describe Vagrant::Plugin::V2::Components do subject { described_class.new } it "should have synced folders" do expect(subject.synced_folders).to be_kind_of(Vagrant::Registry) end describe "configs" do it "should have configs" do expect(subject.configs).to be_kind_of(Hash) end it "should default the values to registries" do expect(subject.configs[:i_probably_dont_exist]).to be_kind_of(Vagrant::Registry) end end end vagrant-2.0.2/test/unit/vagrant/plugin/v2/config_test.rb000066400000000000000000000033671323370221500232420ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V2::Config do include_context "unit" let(:foo_class) do Class.new(described_class) do attr_accessor :one attr_accessor :two end end let(:unset_value) { described_class.const_get("UNSET_VALUE") } subject { foo_class.new } describe "#merge" do it "should merge by default by simply copying each instance variable" do one = foo_class.new one.one = 2 one.two = 1 two = foo_class.new two.two = 5 result = one.merge(two) expect(result.one).to eq(2) expect(result.two).to eq(5) end it "prefers any set value over an UNSET_VALUE" do one = foo_class.new one.one = 1 one.two = 2 two = foo_class.new two.one = unset_value two.two = 5 result = one.merge(two) expect(result.one).to eq(1) expect(result.two).to eq(5) end it "doesn't merge values that start with a double underscore" do one = foo_class.new one.one = 1 one.two = 1 one.instance_variable_set(:@__bar, "one") two = foo_class.new two.two = 2 two.instance_variable_set(:@__bar, "two") # Merge and verify result = one.merge(two) expect(result.one).to eq(1) expect(result.two).to eq(2) expect(result.instance_variable_get(:@__bar)).to be_nil end end describe "#method_missing" do it "returns a DummyConfig object" do expect(subject.i_should_not_exist). to be_kind_of(Vagrant::Config::V2::DummyConfig) end it "raises an error if finalized (internally)" do subject._finalize! expect { subject.i_should_not_exist }. to raise_error(NoMethodError) end end end vagrant-2.0.2/test/unit/vagrant/plugin/v2/host_test.rb000066400000000000000000000001601323370221500227360ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V2::Host do # No tests. end vagrant-2.0.2/test/unit/vagrant/plugin/v2/manager_test.rb000066400000000000000000000140011323370221500233720ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V2::Manager do include_context "unit" let(:instance) { described_class.new } def plugin p = Class.new(Vagrant.plugin("2")) yield p p end describe "#action_hooks" do it "should contain globally registered hooks" do pA = plugin do |p| p.action_hook("foo") { "bar" } end pB = plugin do |p| p.action_hook("bar") { "baz" } end instance.register(pA) instance.register(pB) result = instance.action_hooks(nil) expect(result.length).to eq(2) expect(result[0].call).to eq("bar") expect(result[1].call).to eq("baz") end it "should contain specific hooks with globally registered hooks" do pA = plugin do |p| p.action_hook("foo") { "bar" } p.action_hook("foo", :foo) { "bar_foo" } p.action_hook("foo", :bar) { "bar_bar" } end pB = plugin do |p| p.action_hook("bar") { "baz" } end instance.register(pA) instance.register(pB) result = instance.action_hooks(:foo) expect(result.length).to eq(3) expect(result[0].call).to eq("bar") expect(result[1].call).to eq("bar_foo") expect(result[2].call).to eq("baz") end end it "should enumerate registered communicator classes" do pA = plugin do |p| p.communicator("foo") { "bar" } end pB = plugin do |p| p.communicator("bar") { "baz" } end instance.register(pA) instance.register(pB) expect(instance.communicators.to_hash.length).to eq(2) expect(instance.communicators[:foo]).to eq("bar") expect(instance.communicators[:bar]).to eq("baz") end it "should enumerate registered configuration classes" do pA = plugin do |p| p.config("foo") { "bar" } end pB = plugin do |p| p.config("bar") { "baz" } end instance.register(pA) instance.register(pB) expect(instance.config.to_hash.length).to eq(2) expect(instance.config[:foo]).to eq("bar") expect(instance.config[:bar]).to eq("baz") end it "should enumerate registered guest classes" do pA = plugin do |p| p.guest("foo") { "bar" } end pB = plugin do |p| p.guest("bar", "foo") { "baz" } end instance.register(pA) instance.register(pB) expect(instance.guests.to_hash.length).to eq(2) expect(instance.guests[:foo]).to eq(["bar", nil]) expect(instance.guests[:bar]).to eq(["baz", :foo]) end it "should enumerate registered guest capabilities" do pA = plugin do |p| p.guest_capability("foo", "foo") { "bar" } end pB = plugin do |p| p.guest_capability("bar", "foo") { "baz" } end instance.register(pA) instance.register(pB) expect(instance.guest_capabilities.length).to eq(2) expect(instance.guest_capabilities[:foo][:foo]).to eq("bar") expect(instance.guest_capabilities[:bar][:foo]).to eq("baz") end it "should enumerate registered host classes" do pA = plugin do |p| p.host("foo") { "bar" } end pB = plugin do |p| p.host("bar", "foo") { "baz" } end instance.register(pA) instance.register(pB) expect(instance.hosts.to_hash.length).to eq(2) expect(instance.hosts[:foo]).to eq(["bar", nil]) expect(instance.hosts[:bar]).to eq(["baz", :foo]) end it "should enumerate registered host capabilities" do pA = plugin do |p| p.host_capability("foo", "foo") { "bar" } end pB = plugin do |p| p.host_capability("bar", "foo") { "baz" } end instance.register(pA) instance.register(pB) expect(instance.host_capabilities.length).to eq(2) expect(instance.host_capabilities[:foo][:foo]).to eq("bar") expect(instance.host_capabilities[:bar][:foo]).to eq("baz") end it "should enumerate registered provider classes" do pA = plugin do |p| p.provider("foo") { "bar" } end pB = plugin do |p| p.provider("bar", foo: "bar") { "baz" } end instance.register(pA) instance.register(pB) expect(instance.providers.to_hash.length).to eq(2) expect(instance.providers[:foo]).to eq(["bar", { priority: 5 }]) expect(instance.providers[:bar]).to eq(["baz", { foo: "bar", priority: 5 }]) end it "provides the collection of registered provider configs" do pA = plugin do |p| p.config("foo", :provider) { "foo" } end pB = plugin do |p| p.config("bar", :provider) { "bar" } p.config("baz") { "baz" } end instance.register(pA) instance.register(pB) expect(instance.provider_configs.to_hash.length).to eq(2) expect(instance.provider_configs[:foo]).to eq("foo") expect(instance.provider_configs[:bar]).to eq("bar") end it "should enumerate registered push classes" do pA = plugin do |p| p.push("foo") { "bar" } end pB = plugin do |p| p.push("bar", foo: "bar") { "baz" } end instance.register(pA) instance.register(pB) expect(instance.pushes.to_hash.length).to eq(2) expect(instance.pushes[:foo]).to eq(["bar", nil]) expect(instance.pushes[:bar]).to eq(["baz", { foo: "bar" }]) end it "provides the collection of registered push configs" do pA = plugin do |p| p.config("foo", :push) { "foo" } end pB = plugin do |p| p.config("bar", :push) { "bar" } p.config("baz") { "baz" } end instance.register(pA) instance.register(pB) expect(instance.push_configs.to_hash.length).to eq(2) expect(instance.push_configs[:foo]).to eq("foo") expect(instance.push_configs[:bar]).to eq("bar") end it "should enumerate all registered synced folder implementations" do pA = plugin do |p| p.synced_folder("foo") { "bar" } end pB = plugin do |p| p.synced_folder("bar", 50) { "baz" } end instance.register(pA) instance.register(pB) expect(instance.synced_folders.to_hash.length).to eq(2) expect(instance.synced_folders[:foo]).to eq(["bar", 10]) expect(instance.synced_folders[:bar]).to eq(["baz", 50]) end end vagrant-2.0.2/test/unit/vagrant/plugin/v2/plugin_test.rb000066400000000000000000000307571323370221500232760ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V2::Plugin do before do allow(described_class).to receive(:manager) .and_return(Vagrant::Plugin::V2::Manager.new) end it "should be able to set and get the name" do plugin = Class.new(described_class) do name "foo" end expect(plugin.name).to eq("foo") end it "should be able to set and get the description" do plugin = Class.new(described_class) do description "bar" end expect(plugin.description).to eq("bar") end describe "action hooks" do it "should register on all actions by default" do plugin = Class.new(described_class) do action_hook("foo") { "bar" } end hooks_registry = plugin.components.action_hooks hooks = hooks_registry[described_class.const_get("ALL_ACTIONS")] expect(hooks.length).to eq(1) expect(hooks[0].call).to eq("bar") end it "should register for a specific action by default" do plugin = Class.new(described_class) do action_hook("foo", :bar) { "bar" } end hooks_registry = plugin.components.action_hooks hooks = hooks_registry[:bar] expect(hooks.length).to eq(1) expect(hooks[0].call).to eq("bar") end end describe "commands" do it "should register command classes" do plugin = Class.new(described_class) do command("foo") { "bar" } end expect(plugin.components.commands.keys).to be_include(:foo) expect(plugin.components.commands[:foo][0].call).to eql("bar") end it "should register command classes with options" do plugin = Class.new(described_class) do command("foo", opt: :bar) { "bar" } end expect(plugin.components.commands.keys).to be_include(:foo) expect(plugin.components.commands[:foo][0].call).to eql("bar") expect(plugin.components.commands[:foo][1][:opt]).to eql(:bar) end it "should register commands as primary by default" do plugin = Class.new(described_class) do command("foo") { "bar" } command("bar", primary: false) { "bar" } end expect(plugin.components.commands[:foo][1][:primary]).to be(true) expect(plugin.components.commands[:bar][1][:primary]).to be(false) end ["spaces bad", "sym^bols"].each do |bad| it "should not allow bad command name: #{bad}" do plugin = Class.new(described_class) expect { plugin.command(bad) {} }. to raise_error(Vagrant::Plugin::V2::InvalidCommandName) end end it "should lazily register command classes" do # Below would raise an error if the value of the command class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do command("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the command key that # a proper error is raised. expect { plugin.components.commands[:foo][0].call }.to raise_error(StandardError, "FAIL!") end end describe "communicators" do it "should register communicator classes" do plugin = Class.new(described_class) do communicator("foo") { "bar" } end expect(plugin.communicator[:foo]).to eq("bar") end it "should lazily register communicator classes" do # Below would raise an error if the value of the class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do communicator("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the configuration key that # a proper error is raised. expect { plugin.communicator[:foo] }.to raise_error(StandardError) end end describe "configuration" do it "should register configuration classes" do plugin = Class.new(described_class) do config("foo") { "bar" } end expect(plugin.components.configs[:top][:foo]).to eq("bar") end it "should lazily register configuration classes" do # Below would raise an error if the value of the config class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do config("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the configuration key that # a proper error is raised. expect { plugin.components.configs[:top][:foo] }.to raise_error(StandardError) end it "should register configuration classes for providers" do plugin = Class.new(described_class) do config("foo", :provider) { "bar" } end expect(plugin.components.configs[:provider][:foo]).to eq("bar") end end describe "guests" do it "should register guest classes" do plugin = Class.new(described_class) do guest("foo") { "bar" } end expect(plugin.components.guests[:foo]).to eq(["bar", nil]) end it "should lazily register guest classes" do # Below would raise an error if the value of the guest class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do guest("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the guest key that # a proper error is raised. expect { plugin.guest[:foo] }.to raise_error(StandardError) end end describe "guest capabilities" do it "should register guest capabilities" do plugin = Class.new(described_class) do guest_capability("foo", "bar") { "baz" } end expect(plugin.components.guest_capabilities[:foo][:bar]).to eq("baz") end end describe "hosts" do it "should register host classes" do plugin = Class.new(described_class) do host("foo") { "bar" } end expect(plugin.components.hosts[:foo]).to eq(["bar", nil]) end it "should lazily register host classes" do # Below would raise an error if the value of the host class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do host("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the host key that # a proper error is raised. expect { plugin.host[:foo] }.to raise_error(StandardError) end end describe "host capabilities" do it "should register host capabilities" do plugin = Class.new(described_class) do host_capability("foo", "bar") { "baz" } end expect(plugin.components.host_capabilities[:foo][:bar]).to eq("baz") end end describe "providers" do it "should register provider classes" do plugin = Class.new(described_class) do provider("foo") { "bar" } end result = plugin.components.providers[:foo] expect(result[0]).to eq("bar") expect(result[1][:priority]).to eq(5) end it "should register provider classes with options" do plugin = Class.new(described_class) do provider("foo", foo: "yep") { "bar" } end result = plugin.components.providers[:foo] expect(result[0]).to eq("bar") expect(result[1][:priority]).to eq(5) expect(result[1][:foo]).to eq("yep") end it "should lazily register provider classes" do # Below would raise an error if the value of the config class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do provider("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the configuration key that # a proper error is raised. expect { plugin.components.providers[:foo] }.to raise_error(StandardError) end end describe "provider capabilities" do it "should register host capabilities" do plugin = Class.new(described_class) do provider_capability("foo", "bar") { "baz" } end expect(plugin.components.provider_capabilities[:foo][:bar]).to eq("baz") end end describe "provisioners" do it "should register provisioner classes" do plugin = Class.new(described_class) do provisioner("foo") { "bar" } end expect(plugin.provisioner[:foo]).to eq("bar") end it "should lazily register provisioner classes" do # Below would raise an error if the value of the config class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do provisioner("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the configuration key that # a proper error is raised. expect { plugin.provisioner[:foo] }.to raise_error(StandardError) end end describe "pushes" do it "should register implementations" do plugin = Class.new(described_class) do push("foo") { "bar" } end expect(plugin.components.pushes[:foo]).to eq(["bar", nil]) end it "should be able to specify priorities" do plugin = Class.new(described_class) do push("foo", bar: 1) { "bar" } end expect(plugin.components.pushes[:foo]).to eq(["bar", bar: 1]) end it "should lazily register implementations" do # Below would raise an error if the value of the config class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do push("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the configuration key that # a proper error is raised. expect { plugin.components.pushes[:foo] }.to raise_error(StandardError) end end describe "synced folders" do it "should register implementations" do plugin = Class.new(described_class) do synced_folder("foo") { "bar" } end expect(plugin.components.synced_folders[:foo]).to eq(["bar", 10]) end it "should be able to specify priorities" do plugin = Class.new(described_class) do synced_folder("foo", 50) { "bar" } end expect(plugin.components.synced_folders[:foo]).to eq(["bar", 50]) end it "should lazily register implementations" do # Below would raise an error if the value of the config class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do synced_folder("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the configuration key that # a proper error is raised. expect { plugin.components.synced_folders[:foo] }.to raise_error(StandardError) end end describe "plugin registration" do let(:manager) { described_class.manager } it "should have no registered plugins" do expect(manager.registered).to be_empty end it "should register a plugin when a name is set" do plugin = Class.new(described_class) do name "foo" end expect(manager.registered).to eq([plugin]) end it "should register a plugin only once" do plugin = Class.new(described_class) do name "foo" name "bar" end expect(manager.registered).to eq([plugin]) end end end vagrant-2.0.2/test/unit/vagrant/plugin/v2/provider_test.rb000066400000000000000000000025411323370221500236200ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V2::Provider do include_context "unit" let(:machine) { Object.new } let(:instance) { described_class.new(machine) } subject { instance } it "should be usable by default" do expect(described_class).to be_usable end it "should be installed by default" do expect(described_class).to be_installed end it "should return nil by default for actions" do expect(instance.action(:whatever)).to be_nil end it "should return nil by default for ssh info" do expect(instance.ssh_info).to be_nil end it "should return nil by default for state" do expect(instance.state).to be_nil end context "capabilities" do before do register_plugin("2") do |p| p.provider_capability("bar", "foo") {} p.provider_capability("foo", "bar") do Class.new do def self.bar(machine) raise "bar #{machine.id}" end end end end allow(machine).to receive(:id).and_return("YEAH") instance._initialize("foo", machine) end it "can execute capabilities" do expect(subject.capability?(:foo)).to be(false) expect(subject.capability?(:bar)).to be(true) expect { subject.capability(:bar) }. to raise_error("bar YEAH") end end end vagrant-2.0.2/test/unit/vagrant/registry_test.rb000066400000000000000000000103321323370221500220060ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) describe Vagrant::Registry do let(:instance) { described_class.new } it "should return nil for nonexistent items" do expect(instance.get("foo")).to be_nil end it "should register a simple key/value" do instance.register("foo") { "value" } expect(instance.get("foo")).to eq("value") end it "should register an item without calling the block yet" do expect do instance.register("foo") do raise Exception, "BOOM!" end end.to_not raise_error end it "should raise an error if no block is given" do expect { instance.register("foo") }. to raise_error(ArgumentError) end it "should call and return the result of a block when asking for the item" do object = Object.new instance.register("foo") do object end expect(instance.get("foo")).to eql(object) end it "should be able to get the item with []" do object = Object.new instance.register("foo") { object } expect(instance["foo"]).to eql(object) end it "should be able to get keys with #keys" do instance.register("foo") { "bar" } instance.register("baz") { raise "BOOM" } expect(instance.keys.sort).to eq([ 'baz', 'foo' ]) end it "should cache the result of the item so they can be modified" do # Make the proc generate a NEW array each time instance.register("foo") { [] } # Test that modifying the result modifies the actual cached # value. This verifies we're caching. expect(instance.get("foo")).to eq([]) instance.get("foo") << "value" expect(instance.get("foo")).to eq(["value"]) end it "should be able to check if a key exists" do instance.register("foo") { "bar" } expect(instance).to have_key("foo") expect(instance).not_to have_key("bar") end it "should be enumerable" do instance.register("foo") { "foovalue" } instance.register("bar") { "barvalue" } keys = [] values = [] instance.each do |key, value| keys << key values << value end expect(keys.sort).to eq(["bar", "foo"]) expect(values.sort).to eq(["barvalue", "foovalue"]) end it "should be able to convert to a hash" do instance.register("foo") { "foovalue" } instance.register("bar") { "barvalue" } result = instance.to_hash expect(result).to be_a(Hash) expect(result["foo"]).to eq("foovalue") expect(result["bar"]).to eq("barvalue") end describe "#length" do it "should return 0 when the registry is empty" do expect(instance.length).to eq(0) end it "should return the number of items in the registry" do instance.register("foo") { } instance.register("bar") { } expect(instance.length).to eq(2) end end describe "#size" do it "should be an alias to #length" do size = described_class.instance_method(:size) length = described_class.instance_method(:length) expect(size).to eq(length) end end describe "#empty" do it "should return true when the registry is empty" do expect(instance.empty?).to be(true) end it "should return false when there is at least one element" do instance.register("foo") { } expect(instance.empty?).to be(false) end end describe "merging" do it "should merge in another registry" do one = described_class.new two = described_class.new one.register("foo") { raise "BOOM!" } two.register("bar") { raise "BAM!" } three = one.merge(two) expect { three["foo"] }.to raise_error("BOOM!") expect { three["bar"] }.to raise_error("BAM!") end it "should NOT merge in the cache" do one = described_class.new two = described_class.new one.register("foo") { [] } one["foo"] << 1 two.register("bar") { [] } two["bar"] << 2 three = one.merge(two) expect(three["foo"]).to eq([]) expect(three["bar"]).to eq([]) end end describe "merge!" do it "should merge into self" do one = described_class.new two = described_class.new one.register("foo") { "foo" } two.register("bar") { "bar" } one.merge!(two) expect(one["foo"]).to eq("foo") expect(one["bar"]).to eq("bar") end end end vagrant-2.0.2/test/unit/vagrant/shared_helpers_test.rb000066400000000000000000000076361323370221500231430ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) require "vagrant/shared_helpers" require "vagrant/util/platform" describe Vagrant do include_context "unit" subject { described_class } describe "#global_lock" do it "yields to the block" do result = subject.global_lock { 42 } expect(result).to eq(42) end end describe "#in_installer?" do it "is not if env is not set" do with_temp_env("VAGRANT_INSTALLER_ENV" => nil) do expect(subject.in_installer?).to be(false) end end it "is if env is set" do with_temp_env("VAGRANT_INSTALLER_ENV" => "/foo") do expect(subject.in_installer?).to be(true) end end end describe "#installer_embedded_dir" do it "returns nil if not in an installer" do allow(Vagrant).to receive(:in_installer?).and_return(false) expect(subject.installer_embedded_dir).to be_nil end it "returns the set directory" do allow(Vagrant).to receive(:in_installer?).and_return(true) with_temp_env("VAGRANT_INSTALLER_EMBEDDED_DIR" => "/foo") do expect(subject.installer_embedded_dir).to eq("/foo") end end end describe "#plugins_enabled?" do it "returns true if the env is not set" do with_temp_env("VAGRANT_NO_PLUGINS" => nil) do expect(subject.plugins_enabled?).to be(true) end end it "returns false if the env is set" do with_temp_env("VAGRANT_NO_PLUGINS" => "1") do expect(subject.plugins_enabled?).to be(false) end end end describe "#server_url" do it "defaults to the default value" do with_temp_env("VAGRANT_SERVER_URL" => nil) do expect(subject.server_url).to eq( Vagrant::DEFAULT_SERVER_URL) end end it "defaults if the string is empty" do with_temp_env("VAGRANT_SERVER_URL" => "") do expect(subject.server_url).to eq( Vagrant::DEFAULT_SERVER_URL) end end it "is the VAGRANT_SERVER_URL value" do with_temp_env("VAGRANT_SERVER_URL" => "foo") do expect(subject.server_url).to eq("foo") end end it "is the VAGRANT_SERVER_URL value if the server url is configured" do with_temp_env("VAGRANT_SERVER_URL" => "foo") do expect(subject.server_url('bar')).to eq("foo") end end it "is the configured server url if VAGRANT_SERVER_URL is not set" do with_temp_env("VAGRANT_SERVER_URL" => nil) do expect(subject.server_url("bar")).to eq("bar") end end end describe "#user_data_path" do around do |example| env = { "USERPROFILE" => nil, "VAGRANT_HOME" => nil, } with_temp_env(env) { example.run } end it "defaults to ~/.vagrant.d" do expect(subject.user_data_path).to eql(Pathname.new("~/.vagrant.d").expand_path) end it "is VAGRANT_HOME if set" do with_temp_env("VAGRANT_HOME" => "/foo") do expected = Pathname.new("/foo").expand_path expect(subject.user_data_path).to eql(expected) end end it "is USERPROFILE/.vagrant.d if set" do with_temp_env("USERPROFILE" => "/bar") do expected = Pathname.new("/bar/.vagrant.d").expand_path expect(subject.user_data_path).to eql(expected) end end it "prefers VAGRANT_HOME over USERPOFILE if both are set" do env = { "USERPROFILE" => "/bar", "VAGRANT_HOME" => "/foo", } with_temp_env(env) do expected = Pathname.new("/foo").expand_path expect(subject.user_data_path).to eql(expected) end end end describe "#prerelease?" do it "should return true when Vagrant version is development" do stub_const("Vagrant::VERSION", "1.0.0.dev") expect(subject.prerelease?).to be(true) end it "should return false when Vagrant version is release" do stub_const("Vagrant::VERSION", "1.0.0") expect(subject.prerelease?).to be(false) end end end vagrant-2.0.2/test/unit/vagrant/ui_test.rb000066400000000000000000000261311323370221500205570ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) describe Vagrant::UI::Basic do context "in general" do it "outputs within the a new thread" do current = Thread.current.object_id expect(subject).to receive(:safe_puts).with(any_args) { |*args| expect(Thread.current.object_id).to_not eq(current) true } subject.output("foo") end it "outputs using `puts` by default" do expect(subject).to receive(:safe_puts).with(any_args) { |message, **opts| expect(opts[:printer]).to eq(:puts) true } subject.output("foo") end it "outputs using `print` if new_line is false" do expect(subject).to receive(:safe_puts).with(any_args) { |message, **opts| expect(opts[:printer]).to eq(:print) true } subject.output("foo", new_line: false) end it "outputs using `print` if new_line is false" do expect(subject).to receive(:safe_puts).with(any_args) { |message, **opts| expect(opts[:printer]).to eq(:print) true } subject.output("foo", new_line: false) end it "outputs to the assigned stdout" do stdout = StringIO.new subject.stdout = stdout expect(subject).to receive(:safe_puts).with(any_args) { |message, **opts| expect(opts[:io]).to be(stdout) true } subject.output("foo") end it "outputs to stdout by default" do expect(subject.stdout).to be($stdout) end it "outputs to the assigned stderr for errors" do stderr = StringIO.new subject.stderr = stderr expect(subject).to receive(:safe_puts).with(any_args) { |message, **opts| expect(opts[:io]).to be(stderr) true } subject.error("foo") end it "outputs to stderr for errors by default" do expect(subject.stderr).to be($stderr) end end context "#color?" do it "returns false" do expect(subject.color?).to be(false) end end context "#detail" do it "outputs details" do expect(subject).to receive(:safe_puts).with(any_args) { |message, **opts| expect(message).to eq("foo") true } subject.detail("foo") end it "doesn't output details if disabled" do expect(subject).to receive(:safe_puts).never subject.opts[:hide_detail] = true subject.detail("foo") end end context "with sensitive data" do let(:password){ "my-birthday" } let(:output){ "You're password is: #{password}" } before{ Vagrant::Util::CredentialScrubber.sensitive(password) } it "should remove sensitive information from the output" do expect(subject).to receive(:safe_puts).with(any_args) do |message, **opts| expect(message).not_to include(password) end subject.detail(output) end end end describe Vagrant::UI::Colored do include_context "unit" describe "#color?" do it "returns true" do expect(subject.color?).to be(true) end end describe "#detail" do it "colors output nothing by default" do expect(subject).to receive(:safe_puts).with("\033[0mfoo\033[0m", anything) subject.detail("foo") end it "does not bold by default with a color" do expect(subject).to receive(:safe_puts).with(any_args) { |message, *args| expect(message).to start_with("\033[0;31m") expect(message).to end_with("\033[0m") } subject.detail("foo", color: :red) end end describe "#error" do it "colors red" do expect(subject).to receive(:safe_puts).with(any_args) { |message, *args| expect(message).to start_with("\033[0;31m") expect(message).to end_with("\033[0m") } subject.error("foo") end end describe "#output" do it "colors output nothing by default, no bold" do expect(subject).to receive(:safe_puts).with("\033[0mfoo\033[0m", anything) subject.output("foo") end it "doesn't use a color if default color" do expect(subject).to receive(:safe_puts).with("\033[0mfoo\033[0m", anything) subject.output("foo", color: :default) end it "bolds output without color if specified" do expect(subject).to receive(:safe_puts).with("\033[1mfoo\033[0m", anything) subject.output("foo", bold: true) end it "colors output to color specified in global opts" do subject.opts[:color] = :red expect(subject).to receive(:safe_puts).with(any_args) { |message, *args| expect(message).to start_with("\033[0;31m") expect(message).to end_with("\033[0m") } subject.output("foo") end it "colors output to specified color over global opts" do subject.opts[:color] = :red expect(subject).to receive(:safe_puts).with(any_args) { |message, *args| expect(message).to start_with("\033[0;32m") expect(message).to end_with("\033[0m") } subject.output("foo", color: :green) end it "bolds the output if specified" do subject.opts[:color] = :red expect(subject).to receive(:safe_puts).with(any_args) { |message, *args| expect(message).to start_with("\033[1;31m") expect(message).to end_with("\033[0m") } subject.output("foo", bold: true) end end describe "#success" do it "colors green" do expect(subject).to receive(:safe_puts).with(any_args) { |message, *args| expect(message).to start_with("\033[0;32m") expect(message).to end_with("\033[0m") } subject.success("foo") end end describe "#warn" do it "colors yellow" do expect(subject).to receive(:safe_puts).with(any_args) { |message, *args| expect(message).to start_with("\033[0;33m") expect(message).to end_with("\033[0m") } subject.warn("foo") end end end describe Vagrant::UI::MachineReadable do describe "#ask" do it "raises an exception" do expect { subject.ask("foo") }. to raise_error(Vagrant::Errors::UIExpectsTTY) end end [:detail, :warn, :error, :info, :output, :success].each do |method| describe "##{method}" do it "outputs UI type to the machine-readable output" do expect(subject).to receive(:safe_puts).with(any_args) { |message| parts = message.split(",") expect(parts.length).to eq(5) expect(parts[1]).to eq("") expect(parts[2]).to eq("ui") expect(parts[3]).to eq(method.to_s) expect(parts[4]).to eq("foo") true } subject.send(method, "foo") end end end describe "#machine" do it "is formatted properly" do expect(subject).to receive(:safe_puts).with(any_args) { |message| parts = message.split(",") expect(parts.length).to eq(5) expect(parts[1]).to eq("") expect(parts[2]).to eq("type") expect(parts[3]).to eq("data") expect(parts[4]).to eq("another") true } subject.machine(:type, "data", "another") end it "includes a target if given" do expect(subject).to receive(:safe_puts).with(any_args) { |message| parts = message.split(",") expect(parts.length).to eq(4) expect(parts[1]).to eq("boom") expect(parts[2]).to eq("type") expect(parts[3]).to eq("data") true } subject.machine(:type, "data", target: "boom") end it "replaces commas" do expect(subject).to receive(:safe_puts).with(any_args) { |message| parts = message.split(",") expect(parts.length).to eq(4) expect(parts[3]).to eq("foo%!(VAGRANT_COMMA)bar") true } subject.machine(:type, "foo,bar") end it "replaces newlines" do expect(subject).to receive(:safe_puts).with(any_args) { |message| parts = message.split(",") expect(parts.length).to eq(4) expect(parts[3]).to eq("foo\\nbar\\r") true } subject.machine(:type, "foo\nbar\r") end # This is for a bug where JSON parses are frozen and an # exception was being raised. it "works properly with frozen string arguments" do expect(subject).to receive(:safe_puts).with(any_args) { |message| parts = message.split(",") expect(parts.length).to eq(4) expect(parts[3]).to eq("foo\\nbar\\r") true } subject.machine(:type, "foo\nbar\r".freeze) end end end describe Vagrant::UI::Prefixed do let(:prefix) { "foo" } let(:ui) { Vagrant::UI::Basic.new } subject { described_class.new(ui, prefix) } describe "#initialize_copy" do it "duplicates the underlying ui too" do another = subject.dup expect(another.opts).to_not equal(subject.opts) end end describe "#ask" do it "does not request bolding" do expect(ui).to receive(:ask).with(" #{prefix}: foo", bold: false, target: prefix) subject.ask("foo") end end describe "#detail" do it "prefixes with spaces and the message" do expect(ui).to receive(:safe_puts).with(" #{prefix}: foo", anything) subject.detail("foo") end it "prefixes every line" do expect(ui).to receive(:detail).with( " #{prefix}: foo\n #{prefix}: bar", bold: false, target: prefix) subject.detail("foo\nbar") end it "doesn't prefix if requested" do expect(ui).to receive(:detail).with("foo", prefix: false, bold: false, target: prefix) subject.detail("foo", prefix: false) end end describe "#machine" do it "sets the target option" do expect(ui).to receive(:machine).with(:foo, target: prefix) subject.machine(:foo) end it "preserves existing options" do expect(ui).to receive(:machine).with(:foo, :bar, foo: :bar, target: prefix) subject.machine(:foo, :bar, foo: :bar) end end describe "#opts" do it "is the parent's opts" do allow(ui).to receive(:opts).and_return(Object.new) expect(subject.opts).to be(ui.opts) end end describe "#output" do it "prefixes with an arrow and the message" do expect(ui).to receive(:output).with("==> #{prefix}: foo", anything) subject.output("foo") end it "prefixes with spaces if requested" do expect(ui).to receive(:output).with(" #{prefix}: foo", anything) subject.output("foo", prefix_spaces: true) end it "prefixes every line" do expect(ui).to receive(:output).with("==> #{prefix}: foo\n==> #{prefix}: bar", anything) subject.output("foo\nbar") end it "doesn't prefix if requestsed" do expect(ui).to receive(:output).with("foo", prefix: false, bold: true, target: prefix) subject.output("foo", prefix: false) end it "requests bolding" do expect(ui).to receive(:output).with("==> #{prefix}: foo", bold: true, target: prefix) subject.output("foo") end it "does not request bolding if class-level disabled" do ui.opts[:bold] = false expect(ui).to receive(:output).with("==> #{prefix}: foo", target: prefix) subject.output("foo") end it "prefixes with another prefix if requested" do expect(ui).to receive(:output).with("==> bar: foo", anything) subject.output("foo", target: "bar") end end end vagrant-2.0.2/test/unit/vagrant/util/000077500000000000000000000000001323370221500175305ustar00rootroot00000000000000vagrant-2.0.2/test/unit/vagrant/util/ansi_escape_code_remover_test.rb000066400000000000000000000005601323370221500261200ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/ansi_escape_code_remover" describe Vagrant::Util::ANSIEscapeCodeRemover do let(:klass) do Class.new do extend Vagrant::Util::ANSIEscapeCodeRemover end end it "should remove ANSI escape codes" do expect(klass.remove_ansi_escape_codes("\e[Hyo")).to eq("yo") end end vagrant-2.0.2/test/unit/vagrant/util/checkpoint_client_test.rb000066400000000000000000000110041323370221500245750ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/checkpoint_client" describe Vagrant::Util::CheckpointClient do include_context "unit" let(:iso_env) { isolated_environment } let(:env) { iso_env.create_vagrant_env } let(:result) { {} } let(:prefixed_ui) { double("prefixed_ui") } subject{ Vagrant::Util::CheckpointClient.instance } after{ subject.reset! } before do allow(subject).to receive(:result).and_return(result) allow(Vagrant::UI::Prefixed).to receive(:new).and_return(prefixed_ui) end it "should not be enabled by default" do expect(subject.enabled).to be(false) end describe "#setup" do before{ subject.setup(env) } it "should enable after setup" do expect(subject.enabled).to be(true) end it "should generate required paths" do expect(subject.files).not_to be_empty end end describe "#check" do context "without #setup" do it "should not start the check" do expect(Thread).not_to receive(:new) subject.check end end context "with setup" do before{ subject.setup(env) } it "should start the check" do expect(Thread).to receive(:new) subject.check end it "should call checkpoint" do expect(Thread).to receive(:new).and_yield expect(Checkpoint).to receive(:check) subject.check end end end describe "#display" do it "should only dislay once" do expect(subject).to receive(:version_check).once expect(subject).to receive(:alerts_check).once 2.times{ subject.display } end it "should not display cached information" do expect(subject).to receive(:result).and_return("cached" => true).at_least(:once) expect(subject).not_to receive(:version_check) expect(subject).not_to receive(:alerts_check) subject.display end end describe "#alerts_check" do let(:critical){ [{"level" => "critical", "message" => "critical message", "url" => "http://example.com", "date" => Time.now.to_i}] } let(:warn){ [{"level" => "warn", "message" => "warn message", "url" => "http://example.com", "date" => Time.now.to_i}] } let(:info){ [{"level" => "info", "message" => "info message", "url" => "http://example.com", "date" => Time.now.to_i}] } before{ subject.setup(env) } context "with no alerts" do it "should not display alerts" do expect(prefixed_ui).not_to receive(:info) subject.alerts_check end end context "with critical alerts" do let(:result) { {"alerts" => critical} } it "should display critical alert" do expect(prefixed_ui).to receive(:error) subject.alerts_check end end context "with warn alerts" do let(:result) { {"alerts" => warn} } it "should display warn alerts" do expect(prefixed_ui).to receive(:warn) subject.alerts_check end end context "with info alerts" do let(:result) { {"alerts" => info} } it "should display info alerts" do expect(prefixed_ui).to receive(:info) subject.alerts_check end end context "with mixed alerts" do let(:result) { {"alerts" => info + warn + critical} } it "should display all alert types" do expect(prefixed_ui).to receive(:info) expect(prefixed_ui).to receive(:warn) expect(prefixed_ui).to receive(:error) subject.alerts_check end end end describe "#version_check" do before{ subject.setup(env) } let(:new_version){ Gem::Version.new(Vagrant::VERSION).bump.to_s } let(:old_version){ Gem::Version.new("1.0.0") } context "latest version is same as current version" do let(:result) { {"current_version" => Vagrant::VERSION } } it "should not display upgrade information" do expect(prefixed_ui).not_to receive(:info) subject.version_check end end context "latest version is older than current version" do let(:result) { {"current_version" => old_version} } it "should not display upgrade information" do expect(prefixed_ui).not_to receive(:info) subject.version_check end end context "latest version is newer than current version" do let(:result) { {"current_version" => new_version} } it "should not display upgrade information" do expect(prefixed_ui).not_to receive(:info).at_least(:once) subject.version_check end end end end vagrant-2.0.2/test/unit/vagrant/util/command_deprecation_test.rb000066400000000000000000000055011323370221500251100ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/command_deprecation" describe Vagrant::Util do include_context "unit" let(:app){ lambda{|env|} } let(:argv){[]} let(:env){ {ui: Vagrant::UI::Silent.new} } let(:command_class) do Class.new(Vagrant.plugin("2", :command)) do def self.synopsis "base synopsis" end def self.name "VagrantPlugins::CommandTest::Command" end def execute @env[:ui].info("COMMAND CONTENT") 0 end end end let(:command){ command_class.new(app, env) } describe Vagrant::Util::CommandDeprecation do before{ command_class.include(Vagrant::Util::CommandDeprecation) } it "should add deprecation warning to synopsis" do expect(command_class.synopsis).to include('[DEPRECATED]') command.class.synopsis end it "should add deprecation warning to #execute" do expect(env[:ui]).to receive(:warn).with(/DEPRECATION WARNING/) command.execute end it "should execute original command" do expect(env[:ui]).to receive(:info).with("COMMAND CONTENT") command.execute end it "should return with a 0 value" do expect(command.execute).to eq(0) end context "with custom name defined" do before do command_class.class_eval do def deprecation_command_name "custom-name" end end end it "should use custom name within warning message" do expect(env[:ui]).to receive(:warn).with(/custom-name/) command.execute end end context "with deprecated subcommand" do let(:command_class) do Class.new(Vagrant.plugin("2", :command)) do def self.name "VagrantPlugins::CommandTest::Command::Action" end def execute @env[:ui].info("COMMAND CONTENT") 0 end end end it "should not modify empty synopsis" do expect(command_class.synopsis.to_s).to be_empty end it "should extract command name and subname" do expect(command.deprecation_command_name).to eq("test action") end end end describe Vagrant::Util::CommandDeprecation::Complete do before{ command_class.include(Vagrant::Util::CommandDeprecation::Complete) } it "should add deprecation warning to synopsis" do expect(command_class.synopsis).to include('[DEPRECATED]') command.class.synopsis end it "should raise a deprecation error when executed" do expect{ command.execute }.to raise_error(Vagrant::Errors::CommandDeprecated) end it "should not run original command" do expect(env[:ui]).not_to receive(:info).with("COMMAND CONTENT") expect{ command.execute }.to raise_error(Vagrant::Errors::CommandDeprecated) end end end vagrant-2.0.2/test/unit/vagrant/util/credential_scrubber_test.rb000066400000000000000000000047331323370221500251240ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/credential_scrubber" describe Vagrant::Util::CredentialScrubber do subject{ Vagrant::Util::CredentialScrubber } after{ subject.reset! } describe ".url_scrubber" do let(:user){ "vagrant-user" } let(:password){ "vagrant-pass" } let(:url){ "http://#{user}:#{password}@example.com" } it "should remove user credentials from URL" do result = subject.url_scrubber(url) expect(result).not_to include(user) expect(result).not_to include(password) end end describe ".sensitive" do it "should return a nil value" do expect(subject.sensitive("value")).to be_nil end it "should add value to list of strings" do subject.sensitive("value") expect(subject.sensitive_strings).to include("value") end it "should remove duplicates" do subject.sensitive("value") subject.sensitive("value") expect(subject.sensitive_strings.count("value")).to eq(1) end end describe ".unsensitive" do it "should return a nil value" do expect(subject.unsensitive("value")).to be_nil end it "should remove value from list" do subject.sensitive("value") expect(subject.sensitive_strings).to include("value") subject.unsensitive("value") expect(subject.sensitive_strings).not_to include("value") end end describe ".sensitive_strings" do it "should always return the same array" do expect(subject.sensitive_strings).to be(subject.sensitive_strings) end end describe ".desensitize" do let(:to_scrub){ [] } let(:string){ "a line of text with my-birthday and my-cats-birthday embedded" } before{ to_scrub.each{|s| subject.sensitive(s) }} context "with no sensitive strings registered" do it "should not modify the string" do expect(subject.desensitize(string)).to eq(string) end end context "with single value registered" do let(:to_scrub){ ["my-birthday"] } it "should remove the registered value" do expect(subject.desensitize(string)).not_to include(to_scrub.first) end end context "with multiple values registered" do let(:to_scrub){ ["my-birthday", "my-cats-birthday"] } it "should remove all registered values" do result = subject.desensitize(string) to_scrub.each do |registered_value| expect(result).not_to include(registered_value) end end end end end vagrant-2.0.2/test/unit/vagrant/util/deep_merge_test.rb000066400000000000000000000010401323370221500232030ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require 'vagrant/util/deep_merge' describe Vagrant::Util::DeepMerge do it "should deep merge hashes" do original = { "foo" => { "bar" => "baz", }, "bar" => "blah", } other = { "foo" => { "bar" => "new", }, } result = described_class.deep_merge(original, other) expect(result).to_not equal(original) expect(result).to eq({ "foo" => { "bar" => "new", }, "bar" => "blah", }) end end vagrant-2.0.2/test/unit/vagrant/util/downloader_test.rb000066400000000000000000000200201323370221500232440ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/downloader" describe Vagrant::Util::Downloader do let(:source) { "foo" } let(:destination) { "bar" } let(:exit_code) { 0 } let(:subprocess_result) do double("subprocess_result").tap do |result| allow(result).to receive(:exit_code).and_return(exit_code) allow(result).to receive(:stderr).and_return("") end end subject { described_class.new(source, destination) } before :each do allow(Vagrant::Util::Subprocess).to receive(:execute).and_return(subprocess_result) end describe "#download!" do let(:curl_options) { ["-q", "--fail", "--location", "--max-redirs", "10", "--user-agent", described_class::USER_AGENT, "--output", destination, source, {}] } context "with a good exit status" do let(:exit_code) { 0 } it "downloads the file and returns true" do expect(Vagrant::Util::Subprocess).to receive(:execute). with("curl", *curl_options). and_return(subprocess_result) expect(subject.download!).to be end end context "with a bad exit status" do let(:exit_code) { 1 } it "raises an exception" do expect(Vagrant::Util::Subprocess).to receive(:execute). with("curl", *curl_options). and_return(subprocess_result) expect { subject.download! }. to raise_error(Vagrant::Errors::DownloaderError) end end context "with a username and password" do it "downloads the file with the proper flags" do original_source = source source = "http://foo:bar@baz.com/box.box" subject = described_class.new(source, destination) i = curl_options.index(original_source) curl_options[i] = "http://baz.com/box.box" i = curl_options.index("--output") curl_options.insert(i, "foo:bar") curl_options.insert(i, "-u") expect(Vagrant::Util::Subprocess).to receive(:execute). with("curl", *curl_options). and_return(subprocess_result) expect(subject.download!).to be(true) end end context "with an urlescaped username and password" do it "downloads the file with unescaped credentials" do original_source = source source = "http://fo%5Eo:b%40r@baz.com/box.box" subject = described_class.new(source, destination) i = curl_options.index(original_source) curl_options[i] = "http://baz.com/box.box" i = curl_options.index("--output") curl_options.insert(i, "fo^o:b@r") curl_options.insert(i, "-u") expect(Vagrant::Util::Subprocess).to receive(:execute). with("curl", *curl_options). and_return(subprocess_result) expect(subject.download!).to be(true) end end context "with checksum" do let(:checksum_expected_value){ 'MD5_CHECKSUM_VALUE' } let(:checksum_invalid_value){ 'INVALID_VALUE' } let(:digest){ double("digest") } before do allow(digest).to receive(:file).and_return(digest) end [Digest::MD5, Digest::SHA1].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 it "should not raise an exception" do expect(subject.download!).to be(true) end 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 it "should raise an exception" do expect{ subject.download! }.to raise_error(Vagrant::Errors::DownloaderChecksumError) end end end end 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) } 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 it "should not raise an exception" do expect(subject.download!).to be(true) end end context "that only sha1 matches expected value" do subject { described_class.new(source, destination, md5: checksum_invalid_value, sha1: checksum_expected_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 end context "that only md5 matches expected value" do subject { described_class.new(source, destination, md5: checksum_expected_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 end context "that none match expected value" do 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 end end end end describe "#head" do let(:curl_options) { ["-q", "--fail", "--location", "--max-redirs", "10", "--user-agent", described_class::USER_AGENT, source, {}] } it "returns the output" do allow(subprocess_result).to receive(:stdout).and_return("foo") options = curl_options.dup options.unshift("-I") expect(Vagrant::Util::Subprocess).to receive(:execute). with("curl", *options).and_return(subprocess_result) expect(subject.head).to eq("foo") end end describe "#options" do describe "CURL_CA_BUNDLE" do let(:ca_bundle){ "CUSTOM_CA_BUNDLE" } context "when running within the installer" do before do allow(Vagrant).to receive(:in_installer?).and_return(true) allow(ENV).to receive(:[]).with("CURL_CA_BUNDLE").and_return(ca_bundle) end it "should set custom CURL_CA_BUNDLE in subprocess ENV" do _, subprocess_opts = subject.send(:options) expect(subprocess_opts[:env]).not_to be_nil expect(subprocess_opts[:env]["CURL_CA_BUNDLE"]).to eql(ca_bundle) end end context "when not running within the installer" do before{ allow(Vagrant).to receive(:installer?).and_return(false) } it "should not set custom CURL_CA_BUNDLE in subprocess ENV" do _, subprocess_opts = subject.send(:options) expect(subprocess_opts[:env]).to be_nil end end end end end vagrant-2.0.2/test/unit/vagrant/util/env_test.rb000066400000000000000000000022121323370221500217010ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require 'vagrant/util/env' describe Vagrant::Util::Env do context "with valid environment variables" do before do ENV["VAGRANT_TEST"] = "1" end after do ENV.delete("VAGRANT_TEST") end it "should execute block with original environment variables" do Vagrant::Util::Env.with_original_env do expect(ENV["VAGRANT_TEST"]).to be_nil end end it "should replace environment variables after executing block" do Vagrant::Util::Env.with_original_env do expect(ENV["VAGRANT_TEST"]).to be_nil end expect(ENV["VAGRANT_TEST"]).to eq("1") end end context "with invalid environment variables" do it "should not attempt to restore invalid environment variable" do invalid_vars = ENV.to_hash.merge("VAGRANT_OLD_ENV_" => "INVALID") mock = expect(ENV).to receive(:each) invalid_vars.each do |k,v| mock.and_yield(k, v) end expect do Vagrant::Util::Env.with_original_env do expect(ENV["VAGRANT_TEST"]).to be_nil end end.not_to raise_error end end end vagrant-2.0.2/test/unit/vagrant/util/file_checksum_test.rb000066400000000000000000000012431323370221500237150ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require 'digest/md5' require 'digest/sha1' require 'vagrant/util/file_checksum' describe FileChecksum do include_context "unit" let(:environment) { isolated_environment } it "should return a valid checksum for a file" do file = environment.workdir.join("file") file.open("w+") { |f| f.write("HELLO!") } # Check multiple digests instance = described_class.new(file, Digest::MD5) expect(instance.checksum).to eq("9ac96c64417b5976a58839eceaa77956") instance = described_class.new(file, Digest::SHA1) expect(instance.checksum).to eq("264b207c7913e461c43d0f63d2512f4017af4755") end end vagrant-2.0.2/test/unit/vagrant/util/hash_with_indifferent_access_test.rb000066400000000000000000000017651323370221500270010ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/hash_with_indifferent_access" describe Vagrant::Util::HashWithIndifferentAccess do let(:instance) { described_class.new } it "is a Hash" do expect(instance).to be_kind_of(Hash) end it "allows indifferent access when setting with a string" do instance["foo"] = "bar" expect(instance[:foo]).to eq("bar") end it "allows indifferent access when setting with a symbol" do instance[:foo] = "bar" expect(instance["foo"]).to eq("bar") end it "allows indifferent key lookup" do instance["foo"] = "bar" expect(instance.key?(:foo)).to be expect(instance.key?(:foo)).to be expect(instance.include?(:foo)).to be expect(instance.member?(:foo)).to be end it "allows for defaults to be passed in via an initializer block" do instance = described_class.new do |h,k| h[k] = "foo" end expect(instance[:foo]).to eq("foo") expect(instance["bar"]).to eq("foo") end end vagrant-2.0.2/test/unit/vagrant/util/is_port_open_test.rb000066400000000000000000000022641323370221500236200ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "socket" require "vagrant/util/is_port_open" describe Vagrant::Util::IsPortOpen do let(:klass) do Class.new do extend Vagrant::Util::IsPortOpen end end let(:open_port) { 52811 } let(:closed_port) { 52811 } it "should report open ports" do # Start a thread which listens on a port thr = Thread.new do server = TCPServer.new(open_port) Thread.current[:running] = true # Wait until we're told to die Thread.current[:die] = false while !Thread.current[:die] Thread.pass end # Die! server.close end # Wait until the server is running while !thr[:running] Thread.pass end # Verify that we report the port is open expect(klass.is_port_open?("localhost", open_port)).to be # Kill the thread thr[:die] = true thr.join end it "should report closed ports" do # This CAN fail, since port 52811 might actually be in use, but I'm # not sure what to do except choose some random port and hope for the # best, really. expect(klass.is_port_open?("localhost", closed_port)).not_to be end end vagrant-2.0.2/test/unit/vagrant/util/keypair_test.rb000066400000000000000000000017041323370221500225620ustar00rootroot00000000000000require "openssl" require File.expand_path("../../../base", __FILE__) require "vagrant/util/keypair" describe Vagrant::Util::Keypair do describe ".create" do it "generates a usable keypair with no password" do # I don't know how to validate the final return value yet... pubkey, privkey, _ = described_class.create pubkey = OpenSSL::PKey::RSA.new(pubkey) privkey = OpenSSL::PKey::RSA.new(privkey) encrypted = pubkey.public_encrypt("foo") decrypted = privkey.private_decrypt(encrypted) expect(decrypted).to eq("foo") end it "generates a keypair that requires a password" do pubkey, privkey, _ = described_class.create("password") pubkey = OpenSSL::PKey::RSA.new(pubkey) privkey = OpenSSL::PKey::RSA.new(privkey, "password") encrypted = pubkey.public_encrypt("foo") decrypted = privkey.private_decrypt(encrypted) expect(decrypted).to eq("foo") end end end vagrant-2.0.2/test/unit/vagrant/util/line_endings_helper_test.rb000066400000000000000000000005641323370221500251160ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/line_ending_helpers" describe Vagrant::Util::LineEndingHelpers do let(:klass) do Class.new do extend Vagrant::Util::LineEndingHelpers end end it "should convert DOS to unix-style line endings" do expect(klass.dos_to_unix("foo\r\nbar\r\n")).to eq("foo\nbar\n") end end vagrant-2.0.2/test/unit/vagrant/util/network_ip_test.rb000066400000000000000000000014611323370221500232770ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/network_ip" describe Vagrant::Util::NetworkIP do let(:klass) do Class.new do extend Vagrant::Util::NetworkIP end end describe "network address" do it "calculates it properly" do expect(klass.network_address("192.168.2.234", "255.255.255.0")).to eq("192.168.2.0") end it "calculates it properly with integer submask" do expect(klass.network_address("192.168.2.234", "24")).to eq("192.168.2.0") end it "calculates it properly for IPv6" do expect(klass.network_address("fde4:8dba:82e1::c4", "64")).to eq("fde4:8dba:82e1::") end it "calculates it properly for IPv6" do expect(klass.network_address("fde4:8dba:82e1::c4", 64)).to eq("fde4:8dba:82e1::") end end end vagrant-2.0.2/test/unit/vagrant/util/platform_test.rb000066400000000000000000000144641323370221500227510ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/platform" describe Vagrant::Util::Platform do include_context "unit" subject { described_class } describe "#cygwin_path" do let(:path) { "C:\\msys2\\home\\vagrant" } let(:updated_path) { "/home/vagrant" } 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(updated_path) end end it "takes a windows path and returns a formatted path" do allow(Vagrant::Util::Which).to receive(:which).and_return("C:/msys2/cygpath") allow(Vagrant::Util::Subprocess).to receive(:execute).and_return(subprocess_result) expect(Vagrant::Util::Subprocess).to receive(:execute).with("C:\\msys2\\cygpath", "-u", "-a", "C:\\msys2\\home\\vagrant") expect(subject.cygwin_path(path)).to eq("/home/vagrant") end end describe "#msys_path" do let(:updated_path) { "/home/vagrant" } 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(updated_path) end end let(:old_path) { "/old/path/bin:/usr/local/bin:/usr/bin" } it "takes a windows path and returns a formatted path" do path = ENV["PATH"] allow(Vagrant::Util::Which).to receive(:which).and_return("C:/msys2/cygpath") allow(Vagrant::Util::Subprocess).to receive(:execute).and_return(subprocess_result) allow(ENV).to receive(:[]).with("PATH").and_return(path) allow(ENV).to receive(:[]).with("VAGRANT_OLD_ENV_PATH").and_return(old_path) expect(Vagrant::Util::Subprocess).to receive(:execute).with("C:\\msys2\\cygpath", "-u", "-a", path) expect(subject.msys_path(path)).to eq("/home/vagrant") expect(ENV["PATH"]).to eq(path) end end describe "#cygwin?" do before do allow(subject).to receive(:platform).and_return("test") described_class.reset! end after do described_class.reset! end around do |example| with_temp_env(VAGRANT_DETECTED_OS: "nope", PATH: "") do example.run end end it "returns true if VAGRANT_DETECTED_OS includes cygwin" do with_temp_env(VAGRANT_DETECTED_OS: "cygwin") do expect(subject).to be_cygwin end end it "returns true if OSTYPE includes cygwin" do with_temp_env(OSTYPE: "cygwin") do expect(subject).to be_cygwin end end it "returns true if platform has cygwin" do allow(subject).to receive(:platform).and_return("cygwin") expect(subject).to be_cygwin end it "returns false if the PATH contains cygwin" do with_temp_env(PATH: "C:/cygwin") do expect(subject).to_not be_cygwin end end it "returns false if nothing is available" do expect(subject).to_not be_cygwin end end describe "#msys?" do before do allow(subject).to receive(:platform).and_return("test") described_class.reset! end after do described_class.reset! end around do |example| with_temp_env(VAGRANT_DETECTED_OS: "nope", PATH: "") do example.run end end it "returns true if VAGRANT_DETECTED_OS includes msys" do with_temp_env(VAGRANT_DETECTED_OS: "msys") do expect(subject).to be_msys end end it "returns true if OSTYPE includes msys" do with_temp_env(OSTYPE: "msys") do expect(subject).to be_msys end end it "returns true if platform has msys" do allow(subject).to receive(:platform).and_return("msys") expect(subject).to be_msys end it "returns false if the PATH contains msys" do with_temp_env(PATH: "C:/msys") do expect(subject).to_not be_msys end end it "returns false if nothing is available" do expect(subject).to_not be_msys end end describe "#fs_real_path" do it "fixes drive letters on Windows", :windows do expect(described_class.fs_real_path("c:/foo").to_s).to eql("C:/foo") end end describe "#windows_unc_path" do it "correctly converts a path" do expect(described_class.windows_unc_path("c:/foo").to_s).to eql("\\\\?\\c:\\foo") end context "when given a UNC path" do let(:unc_path){ "\\\\srvname\\path" } it "should not modify the path" do expect(described_class.windows_unc_path(unc_path).to_s).to eql(unc_path) end end end describe ".systemd?" do before{ allow(subject).to receive(:windows?).and_return(false) } after{ subject.reset! } context "on windows" do before{ expect(subject).to receive(:windows?).and_return(true) } it "should return false" do expect(subject.systemd?).to be_falsey end end it "should return true if systemd is in use" do expect(Vagrant::Util::Subprocess).to receive(:execute).and_return(double(:result, stdout: "systemd")) expect(subject.systemd?).to be_truthy end it "should return false if systemd is not in use" do expect(Vagrant::Util::Subprocess).to receive(:execute).and_return(double(:result, stdout: "other")) expect(subject.systemd?).to be_falsey end end describe ".wsl_validate_matching_vagrant_versions!" do let(:exe_version){ Vagrant::VERSION.to_s } before do allow(Vagrant::Util::Which).to receive(:which).and_return(true) allow(Vagrant::Util::Subprocess).to receive(:execute).with("vagrant.exe", "version"). and_return(double(exit_code: 0, stdout: "Installed Version: #{exe_version}")) end it "should not raise an error" do Vagrant::Util::Platform.wsl_validate_matching_vagrant_versions! end context "when windows vagrant.exe is not installed" do before{ expect(Vagrant::Util::Which).to receive(:which).with("vagrant.exe").and_return(nil) } it "should not raise an error" do Vagrant::Util::Platform.wsl_validate_matching_vagrant_versions! end end context "when versions do not match" do let(:exe_version){ "1.9.9" } it "should raise an error" do expect { Vagrant::Util::Platform.wsl_validate_matching_vagrant_versions! }.to raise_error(Vagrant::Errors::WSLVagrantVersionMismatch) end end end end vagrant-2.0.2/test/unit/vagrant/util/presence_test.rb000066400000000000000000000023301323370221500227160ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/presence" describe Vagrant::Util::Presence do subject { described_class } describe "#presence" do it "returns false for nil" do expect(subject.presence(nil)).to be(false) end it "returns false for false" do expect(subject.presence(false)).to be(false) end it "returns false for an empty string" do expect(subject.presence("")).to be(false) end it "returns false for a string with null bytes" do expect(subject.presence("\u0000")).to be(false) end it "returns false for an empty array" do expect(subject.presence([])).to be(false) end it "returns false for an array with nil values" do expect(subject.presence([nil, nil])).to be(false) end it "returns false for an empty hash" do expect(subject.presence({})).to be(false) end it "returns true for true" do expect(subject.presence(true)).to be(true) end it "returns the object for an object" do obj = Object.new expect(subject.presence(obj)).to be(obj) end it "returns the class for a class" do expect(subject.presence(String)).to be(String) end end end vagrant-2.0.2/test/unit/vagrant/util/retryable_test.rb000066400000000000000000000046141323370221500231120ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/retryable" describe Vagrant::Util::Retryable do let(:klass) do Class.new do extend Vagrant::Util::Retryable end end it "doesn't retry by default" do tries = 0 block = lambda do tries += 1 raise RuntimeError, "Try" end # It should re-raise the error expect { klass.retryable(&block) }. to raise_error(RuntimeError) # It should've tried once expect(tries).to eq(1) end it "retries the set number of times" do tries = 0 block = lambda do tries += 1 raise RuntimeError, "Try" end # It should re-raise the error expect { klass.retryable(tries: 5, &block) }. to raise_error(RuntimeError) # It should've tried all specified times expect(tries).to eq(5) end it "only retries on the given exception" do tries = 0 block = lambda do tries += 1 raise StandardError, "Try" end # It should re-raise the error expect { klass.retryable(tries: 5, on: RuntimeError, &block) }. to raise_error(StandardError) # It should've never tried since it was a different kind of error expect(tries).to eq(1) end it "can retry on multiple types of errors" do tries = 0 foo_error = Class.new(StandardError) bar_error = Class.new(StandardError) block = lambda do tries += 1 raise foo_error, "Try" if tries == 1 raise bar_error, "Try" if tries == 2 raise RuntimeError, "YAY" end # It should re-raise the error expect { klass.retryable(tries: 5, on: [foo_error, bar_error], &block) }. to raise_error(RuntimeError) # It should've never tried since it was a different kind of error expect(tries).to eq(3) end it "doesn't sleep between tries by default" do block = lambda do raise RuntimeError, "Try" end # Sleep should never be called expect(klass).not_to receive(:sleep) # Run it. expect { klass.retryable(tries: 5, &block) }. to raise_error(RuntimeError) end it "sleeps specified amount between retries" do block = lambda do raise RuntimeError, "Try" end # Sleep should be called between each retry expect(klass).to receive(:sleep).with(10).exactly(4).times # Run it. expect { klass.retryable(tries: 5, sleep: 10, &block) }. to raise_error(RuntimeError) end end vagrant-2.0.2/test/unit/vagrant/util/safe_chdir_test.rb000066400000000000000000000017301323370221500232040ustar00rootroot00000000000000require 'tmpdir' require File.expand_path("../../../base", __FILE__) require 'vagrant/util/safe_chdir' describe Vagrant::Util::SafeChdir do let(:temp_dir) { Dir.mktmpdir("vagrant-test-util-safe-chdir") } let(:temp_dir2) { Dir.mktmpdir("vagrant-test-util-safe-chdir-2") } after do FileUtils.rm_rf(temp_dir) FileUtils.rm_rf(temp_dir2) end it "should change directories" do expected = nil result = nil Dir.chdir(temp_dir) do expected = Dir.pwd end described_class.safe_chdir(temp_dir) do result = Dir.pwd end expect(result).to eq(expected) end it "should allow recursive chdir" do expected = nil result = nil Dir.chdir(temp_dir) do expected = Dir.pwd end expect do described_class.safe_chdir(temp_dir2) do described_class.safe_chdir(temp_dir) do result = Dir.pwd end end end.to_not raise_error expect(result).to eq(expected) end end vagrant-2.0.2/test/unit/vagrant/util/scoped_hash_override_test.rb000066400000000000000000000020271323370221500252740ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/scoped_hash_override" describe Vagrant::Util::ScopedHashOverride do let(:klass) do Class.new do extend Vagrant::Util::ScopedHashOverride end end it "should not mess with non-overrides" do original = { key: "value", another_value: "foo" } expect(klass.scoped_hash_override(original, "foo")).to eq(original) end it "should override if the scope matches" do original = { key: "value", scope__key: "replaced" } expected = { key: "replaced", scope__key: "replaced" } expect(klass.scoped_hash_override(original, "scope")).to eq(expected) end it "should ignore non-matching scopes" do original = { key: "value", scope__key: "replaced", another__key: "value" } expected = { key: "replaced", scope__key: "replaced", another__key: "value" } expect(klass.scoped_hash_override(original, "scope")).to eq(expected) end end vagrant-2.0.2/test/unit/vagrant/util/shell_quote_test.rb000066400000000000000000000004431323370221500234410ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/shell_quote" describe Vagrant::Util::ShellQuote do subject { described_class } it "quotes properly" do expected = "foo '\\''bar'\\''" expect(subject.escape("foo 'bar'", "'")).to eql(expected) end end vagrant-2.0.2/test/unit/vagrant/util/ssh_test.rb000066400000000000000000000220751323370221500217170ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/platform" require "vagrant/util/ssh" describe Vagrant::Util::SSH do include_context "unit" describe "checking key permissions" do let(:key_path) { temporary_file } it "should do nothing on Windows" do allow(Vagrant::Util::Platform).to receive(:windows?).and_return(true) key_path.chmod(0700) # Get the mode now and verify that it is untouched afterwards mode = key_path.stat.mode described_class.check_key_permissions(key_path) expect(key_path.stat.mode).to eq(mode) end it "should fix the permissions", :skip_windows do key_path.chmod(0644) described_class.check_key_permissions(key_path) expect(key_path.stat.mode).to eq(0100600) end end describe "#exec" do let(:ssh_info) {{ host: "localhost", port: 2222, username: "vagrant", private_key_path: [temporary_file], compression: true, dsa_authentication: true }} let(:ssh_path) { "/usr/bin/ssh" } it "searches original PATH for exectuable" do expect(Vagrant::Util::Which).to receive(:which).with("ssh", original_path: true).and_return("valid-return") allow(Vagrant::Util::SafeExec).to receive(:exec).and_return(nil) described_class.exec(ssh_info) end it "searches current PATH if original PATH did not result in valid executable" do expect(Vagrant::Util::Which).to receive(:which).with("ssh", original_path: true).and_return(nil) expect(Vagrant::Util::Which).to receive(:which).with("ssh").and_return("valid-return") allow(Vagrant::Util::SafeExec).to receive(:exec).and_return(nil) described_class.exec(ssh_info) end it "raises an exception if there is no ssh" do allow(Vagrant::Util::Which).to receive(:which).and_return(nil) expect { described_class.exec(ssh_info) }. to raise_error Vagrant::Errors::SSHUnavailable end it "raises an exception if there is no ssh and platform is windows" do allow(Vagrant::Util::Which).to receive(:which).and_return(nil) allow(Vagrant::Util::Platform).to receive(:windows?).and_return(true) expect { described_class.exec(ssh_info) }. to raise_error Vagrant::Errors::SSHUnavailableWindows end it "raises an exception if the platform is windows and uses PuTTY Link" do allow(Vagrant::Util::Platform).to receive(:windows?).and_return(true) allow(Vagrant::Util::Subprocess).to receive(:execute). and_return(double("output", stdout: 'PuTTY Link')) expect { described_class.exec(ssh_info) }. to raise_error Vagrant::Errors::SSHIsPuttyLink end it "invokes SSH with options if subprocess is not allowed" do allow(Vagrant::Util::SafeExec).to receive(:exec).and_return(nil) expect(described_class.exec(ssh_info)).to eq(nil) expect(Vagrant::Util::SafeExec).to have_received(:exec) .with(ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL","-o", "Compression=yes", "-o", "DSAAuthentication=yes", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "IdentityFile=\"#{ssh_info[:private_key_path][0]}\"") end context "when disabling compression or dsa_authentication flags" do let(:ssh_info) {{ host: "localhost", port: 2222, username: "vagrant", private_key_path: [temporary_file], compression: false, dsa_authentication: false }} it "does not include compression or dsa_authentication flags if disabled" do allow(Vagrant::Util::SafeExec).to receive(:exec).and_return(nil) expect(described_class.exec(ssh_info)).to eq(nil) expect(Vagrant::Util::SafeExec).to have_received(:exec) .with(ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "IdentityFile=\"#{ssh_info[:private_key_path][0]}\"") end end context "when verify_host_key is true" do let(:ssh_info) {{ host: "localhost", port: 2222, username: "vagrant", private_key_path: [temporary_file], verify_host_key: true }} it "does not disable StrictHostKeyChecking or set UserKnownHostsFile" do allow(Vagrant::Util::SafeExec).to receive(:exec).and_return(nil) expect(described_class.exec(ssh_info)).to eq(nil) expect(Vagrant::Util::SafeExec).to have_received(:exec) .with(ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "IdentityFile=\"#{ssh_info[:private_key_path][0]}\"") end end context "when not on solaris not using plain mode or with keys_only enabled" do let(:ssh_info) {{ host: "localhost", port: 2222, username: "vagrant", private_key_path: [temporary_file], keys_only: true }} it "adds IdentitiesOnly as an option for ssh" do allow(Vagrant::Util::SafeExec).to receive(:exec).and_return(nil) allow(Vagrant::Util::Platform).to receive(:solaris?).and_return(false) expect(described_class.exec(ssh_info, {plain_mode: true})).to eq(nil) expect(Vagrant::Util::SafeExec).to have_received(:exec) .with(ssh_path, "localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null") end end context "when forward_x11 is enabled" do let(:ssh_info) {{ host: "localhost", port: 2222, username: "vagrant", private_key_path: [temporary_file], forward_x11: true }} it "enables ForwardX11 options" do allow(Vagrant::Util::SafeExec).to receive(:exec).and_return(nil) expect(described_class.exec(ssh_info)).to eq(nil) expect(Vagrant::Util::SafeExec).to have_received(:exec) .with(ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "IdentityFile=\"#{ssh_info[:private_key_path][0]}\"","-o", "ForwardX11=yes", "-o", "ForwardX11Trusted=yes") end end context "when forward_agent is enabled" do let(:ssh_info) {{ host: "localhost", port: 2222, username: "vagrant", private_key_path: [temporary_file], forward_agent: true }} it "enables agent forwarding options" do allow(Vagrant::Util::SafeExec).to receive(:exec).and_return(nil) expect(described_class.exec(ssh_info)).to eq(nil) expect(Vagrant::Util::SafeExec).to have_received(:exec) .with(ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "IdentityFile=\"#{ssh_info[:private_key_path][0]}\"","-o", "ForwardAgent=yes") end end context "when extra_args is provided as an array" do let(:ssh_info) {{ host: "localhost", port: 2222, username: "vagrant", private_key_path: [temporary_file], extra_args: ["-L", "8008:localhost:80"] }} it "enables agent forwarding options" do allow(Vagrant::Util::SafeExec).to receive(:exec).and_return(nil) expect(described_class.exec(ssh_info)).to eq(nil) expect(Vagrant::Util::SafeExec).to have_received(:exec) .with(ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "IdentityFile=\"#{ssh_info[:private_key_path][0]}\"", "-L", "8008:localhost:80") end end context "when extra_args is provided as a string" do let(:ssh_info) {{ host: "localhost", port: 2222, username: "vagrant", private_key_path: [temporary_file], extra_args: "-6" }} it "enables agent forwarding options" do allow(Vagrant::Util::SafeExec).to receive(:exec).and_return(nil) expect(described_class.exec(ssh_info)).to eq(nil) expect(Vagrant::Util::SafeExec).to have_received(:exec) .with(ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "IdentityFile=\"#{ssh_info[:private_key_path][0]}\"", "-6") end end context "with subprocess enabled" do let(:ssh_info) {{ host: "localhost", port: 2222, username: "vagrant", private_key_path: [temporary_file], }} it "executes SSH in a subprocess with options and returns an exit code Fixnum" do # mock out ChildProcess process = double() allow(ChildProcess).to receive(:build).and_return(process) allow(process).to receive(:io).and_return(true) allow(process.io).to receive(:inherit!).and_return(true) allow(process).to receive(:start).and_return(true) allow(process).to receive(:wait).and_return(true) allow(process).to receive(:exit_code).and_return(0) expect(described_class.exec(ssh_info, {subprocess: true})).to eq(0) end end end end vagrant-2.0.2/test/unit/vagrant/util/string_block_editor_test.rb000066400000000000000000000043061323370221500251450ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/string_block_editor" describe Vagrant::Util::StringBlockEditor do describe "#keys" do it "should return all the keys" do data = < [:stdin]) } it 'yields the STDIN stream for the process if we set :notify => :stdin' do echo = described_class.new('echo', 'hello world', :notify => [:stdin]) echo.execute do |type, data| expect(type).to eq(:stdin) expect(data).to be_a(::IO) end end it 'can close STDIN' do result = cat.execute do |type, stdin| # We should be able to close STDIN without raising an exception stdin.close end # we should exit successfully. expect(result.exit_code).to eq(0) end it 'can write to STDIN correctly' do data = "hello world\n" result = cat.execute do |type, stdin| stdin.write(data) stdin.close end # we should exit successfully. expect(result.exit_code).to eq(0) # we should see our data as the output from `cat` expect(result.stdout).to eq(data) end end describe "#running?" do context "when subprocess has not been started" do it "should return false" do sp = described_class.new("ls") expect(sp.running?).to be(false) end end context "when subprocess has completed" do it "should return false" do sp = described_class.new("ls") sp.execute expect(sp.running?).to be(false) end end context "when subprocess is running" do it "should return true" do sp = described_class.new("sleep", "5") thread = Thread.new{ sp.execute } sleep(0.3) expect(sp.running?).to be(true) sp.stop thread.join end end end describe "#stop" do context "when subprocess has not been started" do it "should return false" do sp = described_class.new("ls") expect(sp.stop).to be(false) end end context "when subprocess has already completed" do it "should return false" do sp = described_class.new("ls") sp.execute expect(sp.stop).to be(false) end end context "when subprocess is running" do let(:sp){ described_class.new("sleep", "1") } it "should return true" do thread = Thread.new{ sp.execute } sleep(0.1) expect(sp.stop).to be(true) thread.join end it "should stop the process" do thread = Thread.new{ sp.execute } sleep(0.1) sp.stop expect(sp.running?).to be(false) thread.join end end end end vagrant-2.0.2/test/unit/vagrant/util/which_test.rb000066400000000000000000000027301323370221500222200ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require 'vagrant/util/platform' require 'vagrant/util/which' describe Vagrant::Util::Which do def tester (file_extension, test_extension, mode, &block) # create file in temp directory filename = '__vagrant_unit_test__' dir = Dir.tmpdir file = Pathname(dir) + (filename + file_extension) file.open("w") { |f| f.write("#") } file.chmod(mode) # set the path to the directory where the file is located allow(ENV).to receive(:[]).with("PATH").and_return(dir.to_s) block.call filename + test_extension file.unlink end it "should return a path for an executable file" do tester '.bat', '.bat', 0755 do |name| expect(described_class.which(name)).not_to be_nil end end if Vagrant::Util::Platform.windows? it "should return a path for a Windows executable file" do tester '.bat', '', 0755 do |name| expect(described_class.which(name)).not_to be_nil end end end it "should return nil for a non-executable file" do tester '.txt', '.txt', 0644 do |name| expect(described_class.which(name)).to be_nil end end context "original_path option" do before{ allow(ENV).to receive(:[]).with("PATH").and_return("") } it "should use the original path when instructed" do expect(ENV).to receive(:fetch).with("VAGRANT_OLD_ENV_PATH", any_args).and_return("") described_class.which("file", original_path: true) end end end vagrant-2.0.2/test/unit/vagrant/vagrantfile_test.rb000066400000000000000000000244201323370221500224430ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) require "pathname" require "tmpdir" require "vagrant/vagrantfile" describe Vagrant::Vagrantfile do include_context "unit" let(:keys) { [] } let(:loader) { Vagrant::Config::Loader.new( Vagrant::Config::VERSIONS, Vagrant::Config::VERSIONS_ORDER) } subject { described_class.new(loader, keys) } before do keys << :test end def configure(&block) loader.set(:test, [["2", block]]) end # A helper to register a provider for use in tests. def register_provider(name, config_class=nil, options=nil) provider_cls = Class.new(VagrantTests::DummyProvider) do if options && options[:unusable] def self.usable?(raise_error=false) raise Vagrant::Errors::VagrantError if raise_error false end end end register_plugin("2") do |p| p.provider(name, options) { provider_cls } if config_class p.config(name, :provider) { config_class } end end provider_cls end describe "#config" do it "exposes the global configuration" do configure do |config| config.vm.box = "what" end expect(subject.config.vm.box).to eq("what") end end describe "#machine" do let(:boxes) { Vagrant::BoxCollection.new(iso_env.boxes_dir) } let(:data_path) { Pathname.new(Dir.mktmpdir("vagrant-machine-data-path")) } let(:env) { iso_env.create_vagrant_env } let(:iso_env) { isolated_environment } let(:vagrantfile) { described_class.new(loader, keys) } subject { vagrantfile.machine(:default, :foo, boxes, data_path, env) } before do @foo_config_cls = Class.new(Vagrant.plugin("2", "config")) do attr_accessor :value end @provider_cls = register_provider("foo", @foo_config_cls) configure do |config| config.vm.box = "foo" config.vm.provider "foo" do |p| p.value = "rawr" end end iso_env.box3("foo", "1.0", :foo, vagrantfile: <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 123 end VF end after do FileUtils.rm_rf(data_path.to_s) end describe '#data_dir' do subject { super().data_dir } it { should eq(data_path) } end describe '#env' do subject { super().env } it { should equal(env) } end describe '#name' do subject { super().name } it { should eq(:default) } end describe '#provider' do subject { super().provider } it { should be_kind_of(@provider_cls) } end describe '#provider_name' do subject { super().provider_name } it { should eq(:foo) } end describe '#vagrantfile' do subject { super().vagrantfile } it { should equal(vagrantfile) } end it "has the proper box" do expect(subject.box.name).to eq("foo") end it "has the valid configuration" do expect(subject.config.vm.box).to eq("foo") end it "loads the provider-specific configuration" do expect(subject.provider_config).to be_kind_of(@foo_config_cls) expect(subject.provider_config.value).to eq("rawr") end end describe "#machine_config" do let(:iso_env) { isolated_environment } let(:boxes) { Vagrant::BoxCollection.new(iso_env.boxes_dir) } it "should return a basic configured machine" do provider_cls = register_provider("foo") configure do |config| config.vm.box = "foo" end results = subject.machine_config(:default, :foo, boxes) box = results[:box] config = results[:config] expect(config.vm.box).to eq("foo") expect(box).to be_nil expect(results[:provider_cls]).to equal(provider_cls) end it "configures without a provider or boxes" do register_provider("foo") configure do |config| config.vm.box = "foo" end results = subject.machine_config(:default, nil, nil) box = results[:box] config = results[:config] expect(config.vm.box).to eq("foo") expect(box).to be_nil expect(results[:provider_cls]).to be_nil end it "configures with sub-machine config" do register_provider("foo") configure do |config| config.ssh.port = "1" config.vm.box = "base" config.vm.define "foo" do |f| f.ssh.port = 100 end end results = subject.machine_config(:foo, :foo, boxes) config = results[:config] expect(config.vm.box).to eq("base") expect(config.ssh.port).to eq(100) end it "configures with box configuration if it exists" do register_provider("foo") configure do |config| config.vm.box = "base" end iso_env.box3("base", "1.0", :foo, vagrantfile: <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 123 end VF results = subject.machine_config(:default, :foo, boxes) box = results[:box] config = results[:config] expect(config.vm.box).to eq("base") expect(config.ssh.port).to eq(123) expect(box).to_not be_nil expect(box.name).to eq("base") end it "configures with the proper box version" do register_provider("foo") configure do |config| config.vm.box = "base" config.vm.box_version = "~> 1.2" end iso_env.box3("base", "1.0", :foo, vagrantfile: <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 123 end VF iso_env.box3("base", "1.3", :foo, vagrantfile: <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 245 end VF results = subject.machine_config(:default, :foo, boxes) box = results[:box] config = results[:config] expect(config.vm.box).to eq("base") expect(config.ssh.port).to eq(245) expect(box).to_not be_nil expect(box.name).to eq("base") expect(box.version).to eq("1.3") end it "configures with box config of other supported formats" do register_provider("foo", nil, box_format: "bar") configure do |config| config.vm.box = "base" end iso_env.box3("base", "1.0", :bar, vagrantfile: <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 123 end VF results = subject.machine_config(:default, :foo, boxes) config = results[:config] expect(config.vm.box).to eq("base") expect(config.ssh.port).to eq(123) end it "loads provider overrides if set" do register_provider("foo") register_provider("bar") configure do |config| config.ssh.port = 1 config.vm.box = "base" config.vm.provider "foo" do |_, c| c.ssh.port = 100 end end # Test with the override results = subject.machine_config(:default, :foo, boxes) config = results[:config] expect(config.vm.box).to eq("base") expect(config.ssh.port).to eq(100) # Test without the override results = subject.machine_config(:default, :bar, boxes) config = results[:config] expect(config.vm.box).to eq("base") expect(config.ssh.port).to eq(1) end it "loads the proper box if in a provider override" do register_provider("foo") configure do |config| config.vm.box = "base" config.vm.provider "foo" do |_, c| c.vm.box = "foobox" end end iso_env.box3("base", "1.0", :foo, vagrantfile: <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 123 end VF iso_env.box3("foobox", "1.0", :foo, vagrantfile: <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 234 end VF results = subject.machine_config(:default, :foo, boxes) config = results[:config] box = results[:box] expect(config.vm.box).to eq("foobox") expect(config.ssh.port).to eq(234) expect(box).to_not be_nil expect(box.name).to eq("foobox") end it "raises an error if the machine is not found" do expect { subject.machine_config(:foo, :foo, boxes) }. to raise_error(Vagrant::Errors::MachineNotFound) end it "raises an error if the provider is not found" do expect { subject.machine_config(:default, :foo, boxes) }. to raise_error(Vagrant::Errors::ProviderNotFound) end it "raises an error if the provider is not usable" do register_provider("foo", nil, unusable: true) expect { subject.machine_config(:default, :foo, boxes) }. to raise_error(Vagrant::Errors::ProviderNotUsable) end end describe "#machine_names" do it "returns the default name when single-VM" do configure { |config| } expect(subject.machine_names).to eq([:default]) end it "returns all of the names in a multi-VM" do configure do |config| config.vm.define "foo" config.vm.define "bar" end expect(subject.machine_names).to eq( [:foo, :bar]) end end describe "#machine_names_and_options" do it "returns the default name" do configure { |config| } expect(subject.machine_names_and_options).to eq({ default: { config_version: "2" }, }) end it "returns all the machines" do configure do |config| config.vm.define "foo" config.vm.define "bar", autostart: false config.vm.define "baz", autostart: true end expect(subject.machine_names_and_options).to eq({ foo: { config_version: "2" }, bar: { config_version: "2", autostart: false }, baz: { config_version: "2", autostart: true }, }) end end describe "#primary_machine_name" do it "returns the default name when single-VM" do configure { |config| } expect(subject.primary_machine_name).to eq(:default) end it "returns the designated machine in multi-VM" do configure do |config| config.vm.define "foo" config.vm.define "bar", primary: true config.vm.define "baz" end expect(subject.primary_machine_name).to eq(:bar) end it "returns nil if no designation in multi-VM" do configure do |config| config.vm.define "foo" config.vm.define "baz" end expect(subject.primary_machine_name).to be_nil end end end vagrant-2.0.2/test/unit/vagrant_test.rb000066400000000000000000000105351323370221500201430ustar00rootroot00000000000000require File.expand_path("../base", __FILE__) describe Vagrant do include_context "unit" it "has the path to the source root" do expect(described_class.source_root).to eq(Pathname.new(File.expand_path("../../../", __FILE__))) end describe "plugin superclass" do describe "v1" do it "returns the proper class for version 1" do expect(described_class.plugin("1")).to eq(Vagrant::Plugin::V1::Plugin) end it "returns the proper components for version 1" do expect(described_class.plugin("1", :command)).to eq(Vagrant::Plugin::V1::Command) expect(described_class.plugin("1", :communicator)).to eq(Vagrant::Plugin::V1::Communicator) expect(described_class.plugin("1", :config)).to eq(Vagrant::Plugin::V1::Config) expect(described_class.plugin("1", :guest)).to eq(Vagrant::Plugin::V1::Guest) expect(described_class.plugin("1", :host)).to eq(Vagrant::Plugin::V1::Host) expect(described_class.plugin("1", :provider)).to eq(Vagrant::Plugin::V1::Provider) expect(described_class.plugin("1", :provisioner)).to eq(Vagrant::Plugin::V1::Provisioner) end end describe "v2" do it "returns the proper class for version 2" do expect(described_class.plugin("2")).to eq(Vagrant::Plugin::V2::Plugin) end it "returns the proper components for version 2" do expect(described_class.plugin("2", :command)).to eq(Vagrant::Plugin::V2::Command) expect(described_class.plugin("2", :communicator)).to eq(Vagrant::Plugin::V2::Communicator) expect(described_class.plugin("2", :config)).to eq(Vagrant::Plugin::V2::Config) expect(described_class.plugin("2", :guest)).to eq(Vagrant::Plugin::V2::Guest) expect(described_class.plugin("2", :host)).to eq(Vagrant::Plugin::V2::Host) expect(described_class.plugin("2", :provider)).to eq(Vagrant::Plugin::V2::Provider) expect(described_class.plugin("2", :provisioner)).to eq(Vagrant::Plugin::V2::Provisioner) expect(described_class.plugin("2", :synced_folder)).to eq(Vagrant::Plugin::V2::SyncedFolder) end end it "raises an exception if an unsupported version is given" do expect { described_class.plugin("88") }. to raise_error(ArgumentError) end end describe "has_plugin?" do after(:each) do manager.reset! end let(:manager) { described_class.plugin("2").manager } it "should find the installed plugin by the registered name" do Class.new(described_class.plugin(Vagrant::Config::CURRENT_VERSION)) do name "i_am_installed" end expect(described_class.has_plugin?("i_am_installed")).to be(true) end it "should return false if the plugin is not installed" do expect(described_class.has_plugin?("i_dont_exist")).to be(false) end it "finds plugins by gem name" do specs = [Gem::Specification.new] specs[0].name = "foo" allow(Vagrant::Plugin::Manager.instance).to receive(:installed_specs).and_return(specs) expect(described_class.has_plugin?("foo")).to be(true) expect(described_class.has_plugin?("bar")).to be(false) end it "finds plugins by gem name and version" do specs = [Gem::Specification.new] specs[0].name = "foo" specs[0].version = "1.2.3" allow(Vagrant::Plugin::Manager.instance).to receive(:installed_specs).and_return(specs) expect(described_class.has_plugin?("foo", "~> 1.2.0")).to be(true) expect(described_class.has_plugin?("foo", "~> 1.0.0")).to be(false) expect(described_class.has_plugin?("bar", "~> 1.2.0")).to be(false) end end describe "require_version" do it "should succeed if valid range" do expect { described_class.require_version(Vagrant::VERSION) }. to_not raise_error end it "should not succeed if bad range" do expect { described_class.require_version("> #{Vagrant::VERSION}") }. to raise_error(Vagrant::Errors::VagrantVersionBad) end end describe "original_env" do before do ENV["VAGRANT_OLD_ENV_foo"] = "test" ENV["VAGRANT_OLD_ENV_bar"] = "test" end after do ENV["VAGRANT_OLD_ENV_foo"] = "test" ENV["VAGRANT_OLD_ENV_bar"] = "test" end it "should return the original environment" do expect(Vagrant.original_env).to eq( "foo" => "test", "bar" => "test", ) end end end vagrant-2.0.2/test/vagrant-spec/000077500000000000000000000000001323370221500165245ustar00rootroot00000000000000vagrant-2.0.2/test/vagrant-spec/Vagrantfile.spec000066400000000000000000000101261323370221500216420ustar00rootroot00000000000000# Guest boxes to use for vagrant-spec GUEST_BOXES = { 'hashicorp-vagrant/ubuntu-16.04' => '1.0.1', 'hashicorp-vagrant/centos-7.4' => '1.0.1', 'hashicorp-vagrant/windows-10' => '1.0.0', 'spox/osx-10.12' => '0.0.1' } # Host boxes to run vagrant-spec HOST_BOXES = { 'hashicorp-vagrant/ubuntu-16.04' => '1.0.1', 'hashicorp-vagrant/centos-7.4' => '1.0.1', 'hashicorp-vagrant/windows-10' => '1.0.0', 'spox/osx-10.12' => '0.0.1' } # Determine what providers to test enabled_providers = ENV.fetch("VAGRANT_SPEC_PROVIDERS", "virtualbox").split(",") # Set what boxes should be used enabled_guests = ENV["VAGRANT_GUEST_BOXES"] ? ENV["VAGRANT_GUEST_BOXES"].split(",") : GUEST_BOXES.keys enabled_hosts = ENV["VAGRANT_HOST_BOXES"] ? ENV["VAGRANT_HOST_BOXES"].split(",") : HOST_BOXES.keys guest_boxes = Hash[GUEST_BOXES.find_all{|name, version| enabled_guests.include?(name)}.compact] host_boxes = Hash[HOST_BOXES.find_all{|name, version| enabled_hosts.include?(name)}.compact] # Grab vagrantcloud token, if available vagrantcloud_token = ENV["VAGRANT_CLOUD_TOKEN"] # Download copies of the guest boxes for testing if missing enabled_providers.each do |provider_name| guest_boxes.each do |guest_box, box_version| box_owner, box_name = guest_box.split('/') box_path = File.join(File.dirname(__FILE__), "./boxes/#{guest_box.sub('/', '_')}.#{provider_name}.#{box_version}.box") if !File.exist?(box_path) $stderr.puts "Downloading guest box #{guest_box}" cmd = "curl -Lf -o #{box_path} https://app.vagrantup.com/#{box_owner}/boxes/#{box_name}/versions/#{box_version}/providers/#{provider_name}.box" if vagrantcloud_token cmd += "?access_token=#{vagrantcloud_token}" end result = system(cmd) if !result $stderr.puts $stderr.puts "ERROR: Failed to download guest box #{guest_box} for #{provider_name}!" exit 1 end end end end Vagrant.configure(2) do |global_config| host_boxes.each do |box_name, box_version| platform = box_name.split('/').last.sub(/[^a-z]+$/, '') enabled_providers.each do |provider_name| global_config.vm.define("#{box_name.split('/').last}-#{provider_name}") do |config| config.vm.box = box_name config.vm.box_version = box_version config.vm.synced_folder '.', '/vagrant', disable: true config.vm.synced_folder '../../', '/vagrant' [:vmware_workstation, :vmware_fusion].each do |vmware_provider| config.vm.provider vmware_provider do |vmware| vmware.vmx["memsize"] = ENV.fetch("VAGRANT_HOST_MEMORY", "2048") vmware.vmx['vhv.enable'] = 'TRUE' vmware.vmx['vhv.allow'] = 'TRUE' end end if platform == "windows" config.vm.provision :shell, path: "./scripts/#{platform}-setup.#{provider_name}.ps1", run: "once" else config.vm.provision :shell, path: "./scripts/#{platform}-setup.#{provider_name}.sh", run: "once" end guest_boxes.each_with_index do |box_info, idx| guest_box, box_version = box_info spec_cmd_args = ENV["VAGRANT_SPEC_ARGS"] if idx != 0 spec_cmd_args = "#{spec_cmd_args} --without-component cli/*".strip end if platform == "windows" config.vm.provision( :shell, path: "./scripts/#{platform}-run.#{provider_name}.ps1", keep_color: true, env: { "VAGRANT_SPEC_ARGS" => "--no-builtin #{spec_cmd_args}".strip, "VAGRANT_SPEC_BOX" => "c:/vagrant/#{guest_box.sub('/', '_')}.#{provider_name}.#{box_version}.box" } ) else config.vm.provision( :shell, path: "./scripts/#{platform}-run.#{provider_name}.sh", keep_color: true, env: { "VAGRANT_SPEC_ARGS" => "--no-builtin #{spec_cmd_args}".strip, "VAGRANT_SPEC_BOX" => "/vagrant/test/vagrant-spec/boxes/#{guest_box.sub('/', '_')}.#{provider_name}.#{box_version}.box" } ) end end end end end end vagrant-2.0.2/test/vagrant-spec/boxes/000077500000000000000000000000001323370221500176445ustar00rootroot00000000000000vagrant-2.0.2/test/vagrant-spec/boxes/.keep000066400000000000000000000000001323370221500205570ustar00rootroot00000000000000vagrant-2.0.2/test/vagrant-spec/configs/000077500000000000000000000000001323370221500201545ustar00rootroot00000000000000vagrant-2.0.2/test/vagrant-spec/configs/vagrant-spec.config.virtualbox.rb000066400000000000000000000005361323370221500265410ustar00rootroot00000000000000require_relative "../../acceptance/base" Vagrant::Spec::Acceptance.configure do |c| c.component_paths << File.expand_path("../test/acceptance", __FILE__) c.skeleton_paths << File.expand_path("../test/acceptance/skeletons", __FILE__) c.provider "virtualbox", box: ENV["VAGRANT_SPEC_BOX"], contexts: ["provider-context/virtualbox"] end vagrant-2.0.2/test/vagrant-spec/readme.md000066400000000000000000000031721323370221500203060ustar00rootroot00000000000000# Running vagrant-spec ## Requirements - vagrant installed (from source, or from packages) - vagrant vmware plugin - ![vagrant](https://github.com/hashicorp/vagrant) repo - ![vagrant-spec](https://github.com/hashicorp/vagrant-spec) repo ## How to run First, we need to build vagrant-spec: ``` cd vagrant-spec gem build *.gemspec cp vagrant-spec-0.0.1.gem /path/to/vagrant/vagrant-spec.gem ``` Next, make a decision as to which host and guest boxes will be used to run the tests. From the root dir of the `vagrant` project, run the following command: ```shell VAGRANT_CLOUD_TOKEN=REAL_TOKEN_HERE VAGRANT_HOST_BOXES=hashicorp-vagrant/centos-7.4 VAGRANT_GUEST_BOXES=hashicorp-vagrant/windows-10 VAGRANT_CWD=test/vagrant-spec/ VAGRANT_VAGRANTFILE=Vagrantfile.spec vagrant up ``` If you are running windows, you must give your host box more memory than the default. That can be done through the environment variable `VAGRANT_HOST_MEMORY` ```shell VAGRANT_HOST_MEMORY=10000 VAGRANT_CLOUD_TOKEN=REAL_TOKEN_HERE VAGRANT_HOST_BOXES=hashicorp-vagrant/centos-7.4 VAGRANT_GUEST_BOXES=hashicorp-vagrant/windows-10 VAGRANT_CWD=test/vagrant-spec/ VAGRANT_VAGRANTFILE=Vagrantfile.spec vagrant up ``` ## Relevant environment variables: - VAGRANT_CLOUD_TOKEN + Token to use if fetching a private box (like windows) - VAGRANT_HOST_BOXES - Vagrant box to use to host and run tests - VAGRANT_GUEST_BOXES - Vagrant box to use to run tests on - VAGRANT_CWD - Directory location of vagrant-spec Vagrantfile - VAGRANT_VAGRANTFILE - Vagrantfile to use for running vagrant-spec - VAGRANT_HOST_MEMORY - Set how much memory your host will use (defaults to 2048) vagrant-2.0.2/test/vagrant-spec/scripts/000077500000000000000000000000001323370221500202135ustar00rootroot00000000000000vagrant-2.0.2/test/vagrant-spec/scripts/centos-run.virtualbox.sh000066400000000000000000000003111323370221500250350ustar00rootroot00000000000000#!/bin/bash set -x export VAGRANT_SPEC_BOX="${VAGRANT_SPEC_BOX}" vagrant vagrant-spec ${VAGRANT_SPEC_ARGS} /vagrant/test/vagrant-spec/configs/vagrant-spec.config.virtualbox.rb result=$? exit $result vagrant-2.0.2/test/vagrant-spec/scripts/centos-setup.virtualbox.sh000066400000000000000000000006001323370221500253720ustar00rootroot00000000000000#!/bin/bash set -xe curl -Lo /etc/yum.repos.d/virtualbox.repo http://download.virtualbox.org/virtualbox/rpm/rhel/virtualbox.repo yum groupinstall -y "Development Tools" yum install -y kernel-devel-$(uname -r) yum install -y VirtualBox-${VAGRANT_CENTOS_VIRTUALBOX_VERSION:-5.1} pushd /vagrant rpm -ivh ./pkg/dist/vagrant_*_x86_64.rpm vagrant plugin install ./vagrant-spec.gem popd vagrant-2.0.2/test/vagrant-spec/scripts/ubuntu-run.virtualbox.sh000066400000000000000000000003111323370221500250640ustar00rootroot00000000000000#!/bin/bash set -x export VAGRANT_SPEC_BOX="${VAGRANT_SPEC_BOX}" vagrant vagrant-spec ${VAGRANT_SPEC_ARGS} /vagrant/test/vagrant-spec/configs/vagrant-spec.config.virtualbox.rb result=$? exit $result vagrant-2.0.2/test/vagrant-spec/scripts/ubuntu-setup.virtualbox.sh000066400000000000000000000003351323370221500254260ustar00rootroot00000000000000#!/bin/bash set -xe apt-get update -q apt-get install -qy linux-headers-$(uname -r) apt-get install -qy virtualbox pushd /vagrant dpkg -i ./pkg/dist/vagrant_*_x86_64.deb vagrant plugin install ./vagrant-spec.gem popd vagrant-2.0.2/test/vagrant-spec/scripts/windows-run.virtualbox.ps1000066400000000000000000000002501323370221500253270ustar00rootroot00000000000000cd /vagrant vagrant plugin install ./vagrant-spec.gem vagrant vagrant-spec $Env:VAGRANT_SPEC_ARGS /vagrant/test/vagrant-spec/configs/vagrant-spec.config.virtualbox.rb vagrant-2.0.2/test/vagrant-spec/scripts/windows-setup.virtualbox.ps1000066400000000000000000000046561323370221500257010ustar00rootroot00000000000000Write-Output "Downloading virtualbox guest additions" $vboxadd_url = "http://download.virtualbox.org/virtualbox/5.2.2/VBoxGuestAdditions_5.2.2.iso" $vboxadd_output = "C:/Windows/Temp/vboxguestadditions.iso" (New-Object System.Net.WebClient).DownloadFile($vboxadd_url, $vboxadd_output) Write-Output "Mounting virtualbox guest additions" Mount-DiskImage -ImagePath $vboxadd_output Write-Output "Installing Virtualbox Guest Additions" Write-Output "Checking for Certificates in vBox ISO" if(test-path E:\ -Filter *.cer) { Get-ChildItem E:\cert -Filter *.cer | ForEach-Object { certutil -addstore -f "TrustedPublisher" $_.FullName } } Start-Process -FilePath "E:\VBoxWindowsAdditions.exe" -ArgumentList "/S" -Wait Write-Output "Downloading virtualbox" $vbox_url = "http://download.virtualbox.org/virtualbox/5.2.2/VirtualBox-5.2.2-119230-Win.exe" $vbox_output = "C:/Windows/Temp/virtualbox.exe" (New-Object System.Net.WebClient).DownloadFile($vbox_url, $vbox_output) Write-Output "Installing virtualbox" # Extract the contents of the installer Start-Process -FilePath $vbox_output ` -ArgumentList ('--extract','--silent','--path','C:\Windows\Temp') ` -Wait ` -NoNewWindow # Find the installer # Determine if this is a 64-bit or 32-bit CPU $architecture="x86" if ((Get-WmiObject -Class Win32_OperatingSystem).OSArchitecture -eq "64-bit") { $architecture = "amd64" } cd "C:\Windows\Temp" $matches = Get-ChildItem | Where-Object { $_.Name -match "VirtualBox-.*_$($architecture).msi" } if ($matches.Count -ne 1) { Write-Host "Multiple matches for VirtualBox MSI found: $($matches.Count)" exit 1 } $installerPath = Resolve-Path $matches[0] # Run the installer Start-Process -FilePath "$($env:systemroot)\System32\msiexec.exe" ` -ArgumentList "/i `"$installerPath`" /qn /norestart /l*v `"$($pwd)\vbox_install.log`"" ` -Verb RunAs ` -Wait ` -WorkingDirectory "$pwd" cd "C:\vagrant\pkg\dist" $vagrant_matches = Get-ChildItem | Where-Object { $_.Name -match "vagrant.*_x86_64.msi" } if ($vagrant_matches.Count -ne 1) { Write-Host "Could not find vagrant installer" exit 1 } $vagrant_installerPath = Resolve-Path $vagrant_matches[0] Write-Output $vagrant_installerPath Write-Output "Installing vagrant" Start-Process -FilePath "$($env:systemroot)\System32\msiexec.exe" ` -ArgumentList "/i `"$vagrant_installerPath`" /qn /norestart /l*v `"$($pwd)\vagrant_install.log`"" ` -Verb RunAs ` -Wait ` -WorkingDirectory "$pwd" vagrant-2.0.2/vagrant-spec.config.example.rb000066400000000000000000000005351323370221500207730ustar00rootroot00000000000000require_relative "test/acceptance/base" Vagrant::Spec::Acceptance.configure do |c| c.component_paths << File.expand_path("../test/acceptance", __FILE__) c.skeleton_paths << File.expand_path("../test/acceptance/skeletons", __FILE__) c.provider "virtualbox", box: "", contexts: ["provider-context/virtualbox"] end vagrant-2.0.2/vagrant.gemspec000066400000000000000000000072521323370221500161700ustar00rootroot00000000000000$:.unshift File.expand_path("../lib", __FILE__) require "vagrant/version" Gem::Specification.new do |s| s.name = "vagrant" s.version = Vagrant::VERSION s.platform = Gem::Platform::RUBY s.authors = ["Mitchell Hashimoto", "John Bender"] s.email = ["mitchell.hashimoto@gmail.com", "john.m.bender@gmail.com"] s.homepage = "https://www.vagrantup.com" s.license = 'MIT' 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.6" s.required_rubygems_version = ">= 1.3.6" s.rubyforge_project = "vagrant" s.add_dependency "childprocess", "~> 0.6.0" s.add_dependency "erubis", "~> 2.7.0" s.add_dependency "i18n", ">= 0.6.0", "<= 0.8.0" s.add_dependency "listen", "~> 3.1.5" s.add_dependency "hashicorp-checkpoint", "~> 0.1.5" s.add_dependency "log4r", "~> 1.1.9", "< 1.1.11" s.add_dependency "net-ssh", "~> 4.2.0" s.add_dependency "net-sftp", "~> 2.1" s.add_dependency "net-scp", "~> 1.2.0" s.add_dependency "rb-kqueue", "~> 0.2.0" s.add_dependency "rest-client", ">= 1.6.0", "< 3.0" s.add_dependency "wdm", "~> 0.1.0" s.add_dependency "winrm", "~> 2.1" s.add_dependency "winrm-fs", "~> 1.0" s.add_dependency "winrm-elevated", "~> 1.1" # 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). # Explicit constraint is defined to provide required dependency to listen without imposing # tighter restrictions on valid ruby versions s.add_dependency "ruby_dep", "<= 1.3.1" # Constraint rake to properly handle deprecated method usage # from within rspec s.add_development_dependency "rake", "~> 12.0.0" s.add_development_dependency "rspec", "~> 3.5.0" s.add_development_dependency "rspec-its", "~> 1.2.0" s.add_development_dependency "webmock", "~> 2.3.1" s.add_development_dependency "fake_ftp", "~> 0.1.1" # The following block of code determines the files that should be included # in the gem. It does this by reading all the files in the directory where # this gemspec is, and parsing out the ignored files from the gitignore. # Note that the entire gitignore(5) syntax is not supported, specifically # the "!" syntax, but it should mostly work correctly. root_path = File.dirname(__FILE__) all_files = Dir.chdir(root_path) { Dir.glob("**/{*,.*}") } all_files.reject! { |file| [".", ".."].include?(File.basename(file)) } all_files.reject! { |file| file.start_with?("website/") } gitignore_path = File.join(root_path, ".gitignore") gitignore = File.readlines(gitignore_path) gitignore.map! { |line| line.chomp.strip } gitignore.reject! { |line| line.empty? || line =~ /^(#|!)/ } unignored_files = all_files.reject do |file| # Ignore any directories, the gemspec only cares about files next true if File.directory?(file) # Ignore any paths that match anything in the gitignore. We do # two tests here: # # - First, test to see if the entire path matches the gitignore. # - Second, match if the basename does, this makes it so that things # like '.DS_Store' will match sub-directories too (same behavior # as git). # gitignore.any? do |ignore| File.fnmatch(ignore, file, File::FNM_PATHNAME) || File.fnmatch(ignore, File.basename(file), File::FNM_PATHNAME) end end s.files = unignored_files s.executables = unignored_files.map { |f| f[/^bin\/(.*)/, 1] }.compact s.require_path = 'lib' end vagrant-2.0.2/version.txt000066400000000000000000000000061323370221500153750ustar00rootroot000000000000002.0.2