hekevintran
hekevintran

Reputation: 23722

How do you write a Vagrantfile for multiple providers that each use a different box?

My goal is to create one Vagrantfile where I can choose to use either Docker or VirtualBox using the --provider flag (e.g. vagrant up --provider=virtualbox). I'm running into a problem because the Docker provider is saying that my box is not compatible. This is true but I don't want Docker to use this box; I have a Dockerfile which I point to for Docker to use. I want the box to be used only if VirtualBox is the provider. How can I make the Docker provider ignore the box declaration or make the box declaration run only when VirtualBox is the provider?

This is my Vagrantfile:

Vagrant.require_version ">= 1.7.0"

Vagrant.configure('2') do |config|
  PROJECT_ROOT = File.expand_path(File.join(File.dirname(File.expand_path(__FILE__)), '..'))

  config.vm.provider "docker" do |d|
    d.build_dir = "."
    d.has_ssh = true
  end

  config.vm.box = "ubuntu/trusty64" # <--------- this is the line the Docker provider chokes on
  config.vm.provider "virtualbox" do |v|
    v.gui = true
  end

  config.vm.network :forwarded_port, guest: 80, host: 9002
  config.vm.network :forwarded_port, guest: 9000, host: 9000
  config.vm.network :forwarded_port, guest: 9001, host: 9001

  config.vm.synced_folder PROJECT_ROOT, '/srv'

  config.vm.provision :ansible do |ansible|
    ansible.verbose = "v"
    ansible.playbook = 'provisioning/playbook.yml'
  end
end

Upvotes: 1

Views: 1549

Answers (2)

Tarun Lalwani
Tarun Lalwani

Reputation: 146510

You just need to move that box condition based on provider

Vagrant.require_version ">= 1.7.0"

Vagrant.configure('2') do |config|
  PROJECT_ROOT = File.expand_path(File.join(File.dirname(File.expand_path(__FILE__)), '..'))

  config.vm.provider "docker" do |d|
    d.build_dir = "."
    d.has_ssh = true
  end

  config.vm.provider "virtualbox" do |v, override|
    override.vm.box = "ubuntu/trusty64"
    v.gui = true
  end

  config.vm.network :forwarded_port, guest: 80, host: 9002
  config.vm.network :forwarded_port, guest: 9000, host: 9000
  config.vm.network :forwarded_port, guest: 9001, host: 9001

  config.vm.synced_folder PROJECT_ROOT, '/srv'

  config.vm.provision :ansible do |ansible|
    ansible.verbose = "v"
    ansible.playbook = 'provisioning/playbook.yml'
  end
end

Upvotes: 4

hekevintran
hekevintran

Reputation: 23722

The problem fundamentally comes down to the fact that Vagrant has a builtin assumption that boxes and providers are totally independent and can be swapped in any combination. This is not true. A box is compatible only with the providers it is designed to work with. The correct design would allow each provider to specify its own box instead of have the box be a global value. This design already exists in the Docker provider which allows you to choose a Docker image, but does not exist in any other provider. As far as I'm concerned this is a bug in Vagrant.

My solution to this is to parse the provider argument passed and wrap the provider declarations in if statements:

require 'optparse'

def get_provider
  ret = nil
  opt_parser = OptionParser.new do |opts|
    opts.on("--provider provider") do |provider|
      ret = provider
    end
  end
  opt_parser.parse!(ARGV)
  ret
end
provider = get_provider || "virtualbox"

PROJECT_ROOT = File.expand_path(File.join(File.dirname(File.expand_path(__FILE__)), '..'))

Vagrant.require_version ">= 1.7.0"

Vagrant.configure('2') do |config|
  if provider == "docker"
    config.vm.provider "docker" do |d|
      d.build_dir = "."
      d.has_ssh = true
    end
  end

  if provider == "virtualbox"
    config.vm.box = "ubuntu/trusty64"
    config.vm.provider "virtualbox" do |v|
      v.gui = true
    end
  end

  config.vm.network :forwarded_port, guest: 80, host: 9002
  config.vm.network :forwarded_port, guest: 9000, host: 9000
  config.vm.network :forwarded_port, guest: 9001, host: 9001

  config.vm.synced_folder PROJECT_ROOT, '/srv'

  config.vm.provision :ansible do |ansible|
    ansible.verbose = "v"
    ansible.playbook = 'provisioning/playbook.yml'
  end
end

Upvotes: 1

Related Questions