From baea923e9cf3327336ad896f5024541339e3b599 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 5 Nov 2015 13:58:15 -0800 Subject: [PATCH] commands/up: automatically install providers --- lib/vagrant/environment.rb | 2 + plugins/commands/up/command.rb | 85 +++++++++++++++++++----- plugins/providers/virtualbox/provider.rb | 9 +++ templates/locales/en.yml | 7 ++ 4 files changed, 87 insertions(+), 16 deletions(-) diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb index 034d71163..88779a2e9 100644 --- a/lib/vagrant/environment.rb +++ b/lib/vagrant/environment.rb @@ -309,6 +309,7 @@ module Vagrant 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) default = ENV["VAGRANT_DEFAULT_PROVIDER"] default = nil if default == "" @@ -376,6 +377,7 @@ module Vagrant # Find the matching implementation ordered.each do |_, key, impl, _| + return key if !opts[:check_usable] return key if impl.usable?(false) end diff --git a/plugins/commands/up/command.rb b/plugins/commands/up/command.rb index 826bea0ae..b93e859cf 100644 --- a/plugins/commands/up/command.rb +++ b/plugins/commands/up/command.rb @@ -1,4 +1,5 @@ require 'optparse' +require 'set' require "vagrant" @@ -16,6 +17,7 @@ module VagrantPlugins def execute options = {} options[:destroy_on_error] = true + options[:install_provider] = true options[:parallel] = true options[:provision_ignore_sentinel] = false @@ -41,6 +43,11 @@ module VagrantPlugins "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 @@ -53,24 +60,32 @@ module VagrantPlugins # Go over each VM and bring it up @logger.debug("'Up' each target VM...") - # Build up the batch job of what we'll do - machines = [] - @env.batch(options[:parallel]) do |batch| - 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? + # 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 names + # 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 + # 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) + 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", @@ -106,6 +121,44 @@ module VagrantPlugins # Success, exit status 0 0 end + + protected + + def install_providers(names) + # 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 + names.each do |name| + providers.add(@env.default_provider(machine: name.to_sym, check_usable: false)) + 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 diff --git a/plugins/providers/virtualbox/provider.rb b/plugins/providers/virtualbox/provider.rb index 969fb4718..6bde344d2 100644 --- a/plugins/providers/virtualbox/provider.rb +++ b/plugins/providers/virtualbox/provider.rb @@ -5,6 +5,15 @@ module VagrantPlugins 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 diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 2bafd0f55..3b7094310 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -154,6 +154,13 @@ en: 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.