Vagrant Multi-Machine functionality seems pretty cool, however one thing that bothers me (or is isn't immediately apparent) is that there doesn't seem to be a good way to share configuration options between a "parent" Vagrantfile and "children" Vagrantfiles. Is there a way to effectively and maintainably share configuration options between the parent and it's children? An example might make this more clear.
Let's assume I've got a platform which is comprised of 3 apps/services: API, Web, and Worker.
Let's presume a directory structure of the following:
/some_platform
/api
# app code...
Vagrantfile
/web
# app code...
Vagrantfile
/worker
# app code...
Vagrantfile
Vagrantfile
Let's say /some_platform/api/Vagrantfile
looks like:
Vagrant.configure("2") do |config|
config.vm.box = "debian/jessie64"
end
Presumably the web and worker Vagrantfiles look similar.
Now, using the wonders of Multi-Machine I rely on Vagrant coordinate these VMs, and /some_platform/Vagrantfile
looks like:
Vagrant.configure("2") do |config|
config.vm.define "web" do |api|
api.vm.box = "debian/jessie64"
end
config.vm.define "web" do |web|
web.vm.box = "debian/jessie64"
end
config.vm.define "web" do |worker|
worker.vm.box = "debian/jessie64"
end
end
I realize this example is contrived, but it's easy to see how once you get more and more complex config declarations, it's annoying and hazardous to have that config duplicated in two places.
You might be wondering "Why does each project have it's own Vagrantfile?" Doing so provides a single source of truth for how the server that app runs on should be setup. I realize there are provisioners you can use (and I will use them), but you still have to declare a few other things outside of that and I want to keep that DRY so that I can either bring up a cluster of apps via Multi-Machine, or I can work on a single app and change it's VM/server setup.
What I'd really love is a way to merge other Vagrantfiles into a "parent" file.
Is that possible? Or am I crazy for trying? Any clever ideas on how to achieve this? I've mucked about with some yaml files and POROs to skate around this issue, but none of the hacks feel very satisfying.
Good news!
You can in fact apply DRY principles in a Vagrantfile.
First: Create a file /some_platform/DRY_vagrant/Vagrantfile.sensible
to hold some sensible defaults :
Vagrant.configure("2") do |config|
# With the setting below, any vagrantfile VM without a 'config.vm.box' will
# automatically inherit "debian/jessie64"
config.vm.box = "debian/jessie64"
end
Second: Create a file /some_platform/DRY_vagrant/Vagrantfile.worker
for the 'worker' virtual machine :
Vagrant.configure("2") do |config|
config.vm.define "worker" do |worker|
# This 'worker' VM will not inherit "debian/jessie64".
# Instead, this VM will explicitly use "debian/stretch64"
worker.vm.box = "debian/stretch64"
end
end
Finally: Create a file /some_platform/Vagrantfile
to tie it all together :
# Load Sensible Defaults
sensible_defaults_vagrantfile = '/some_platform/DRY_vagrant/Vagrantfile.sensible'
load sensible_defaults_vagrantfile if File.exists?(sensible_defaults_vagrantfile)
# Define the 'api' VM within the main Vagrantfile
Vagrant.configure("2") do |config|
config.vm.define "api" do |api|
# This 'api' VM will automatically inherit the "debian/jessie64" which we
# configured in Vagrantfile.sensible
# Make customizations to the 'api' VM
api.vm.hostname = "vm-debian-jessie64-api"
end
end
# Load the 'worker' VM
worker_vm_vagrantfile = '/some_platform/DRY_vagrant/Vagrantfile.worker'
load worker_vm_vagrantfile if File.exists?(worker_vm_vagrantfile)
This approach can be used for almost any other vagrantfile config options. It is not limited to just the "config.vm.box" setting.
Hope this helped!
There are 2 things you can look at (probably more, but those two comes to my mind)
look at How to template Vagrantfile using Ruby? its an example how you can read the content of another file, Vagrantfile is just a ruby script so you can use all the power of ruby.
vagrant has a concept of loading and merging, see from doc so if you wanted to do something anytime you run a vagrant command, you could create a Vagrantfile under your ~/.vagrant.d/
folder and it will always run
one drawback (or at least to pay attention) : the Vagrantfile is a ruby script that is evaluated each (and every time) a vagrant command is executed (up, status, halt ....)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With