hashicorp / vagrant

Vagrant is a tool for building and distributing development environments.
https://www.vagrantup.com
Other
26.26k stars 4.43k forks source link

Inter-machine provisioner ordering and split machine blocks #13107

Closed zeocs closed 1 year ago

zeocs commented 1 year ago

I'm not sure if this a bug, a feature request or just a question. Anyways:

I have a multi-machine setup where I need "global" control over the order in which machine-scope provisioners are executed, i.e., I want to control not only the order of provisioners for each machine, but the global order regardless of which machine they belong to. This will probably be more clear by considering the following Vagrantfile, my goal there being that the provisioners run in order "1 2 3 4 5 6". So since the docs said that provisioners by default run in the order in which they are defined, at first I attempted the following:

Vagrant.configure("2") do |config|

  config.vm.box = "peru/ubuntu-20.04-server-amd64"

  config.vm.provider "libvirt" do |libvirt|
    libvirt.memory = 512
  end

  config.vm.define "alpha" do |alpha|
    alpha.vm.provision "shell", inline: 'echo "1"'
  end

  config.vm.define "beta" do |beta|
    beta.vm.provision "shell", inline: 'echo "2"'
  end

  config.vm.define "gamma" do |gamma|
    gamma.vm.provision "shell", inline: 'echo "3"'
    gamma.vm.provision "shell", inline: 'echo "4"'
  end

  config.vm.define "alpha" do |alpha|
    alpha.vm.provision "shell", inline: 'echo "5"'
  end

  config.vm.define "beta" do |beta|
    beta.vm.provision "shell", inline: 'echo "6"'
  end

end

As the Vagrantfile indicates, I need to run some provisioners on alpha and beta, then some on gamma, and after that again some on alpha and beta. I thus attempted to "split" the definition of alpha and beta by just opening a block for them again after gamma. I didn't find anything in the docs or any question about such "split" machine definitions, so this is sort of a side question: Is this valid? If not, is there a proper way of doing this? I imagine an alternative could be to somehow keep a reference to the alpha and beta machines in a variable in the global scope and then use that later, instead of the second blocks for alpha and beta. But not sure, I'm pretty new to Ruby. As far as I can tell, my approach seems to work. Anyways, the main concern of this issue (inter-machine provisioner ordering) is independent of using split machine definitions.

So after running vagrant up and vagrant destroy many times with this Vagrantfile and observing the output, it seems that provisioners for each machine indeed run in the order they are defined, even when my approach to splitting definitions is used. In other words, I observed that 5 is always run after 1 (alpha), and that 6 is always run after 2 (beta), and of course that 4 is always run after 3 (gamma). But besides this, the order seems random, likely due to a race condition because no inter-machine provisioner order is enforced. This might be a bug by design, although I wish there was a way around this, or at least that it was mentioned in the docs.

I also tried to export VAGRANT_EXPERIMENTAL="dependency_provisioners" and ...

So it would be really awesome to be able to order provisioners "globally". Also, maybe the docs should be clearer about the current limitations. Is there any workaround though? Thanks in advance and sorry for the long issue description.

In case this is a bug:

chrisroberts commented 1 year ago

Hi there,

The type of behavior you are expecting from Vagrant is not currently possible due to how the Vagrant configuration file is parsed and processed. When the Vagrantfile is loaded each guest's configuration is merged from all the blocks found in the Vagrantfile into a single configuration (which preserves the order of defined provisioners order for the specific guest). The global order of the provisioners is not recorded and is not known by Vagrant when the configuration is loaded. Provisioners are executed in order for a specific guest, but not in a global order when more than one guest has been configured. For providers which support parallel guest creation, multiple guests will be launched and provisioned simultaneously, however the order of the provisioning will be specific to each individual guest. This explains why you would see interleaving of the provisioners being executed during your vagrant up.

An alternative approach to this would be to run a vagrant up --no-provision to allow all the guest to be brought up to a running state. From this point, you can then run a separate script which would provision each guest in the order you would like to impose by using the vagrant provision command with the --provision-with flag. However, to be able to use the --provision-with flag effectively, you will need to name your provisioners so you can call them by name. For example:

Vagrant.configure("2") do |config|
  ...
  config.vm.define "alpha" do |alpha|
    alpha.vm.provision "shell", name: "script_1", inline: 'echo "1"'
  end
...

If you have any issues, please feel free to respond on this issue or create a new issue.

Cheers!