Learn How To Implement A Custom Provisioner

custom provisioner

custom provisioner

Vagrant has support for a wide range of provisioning solutions. However, there are a large number of provisioners used privately or publicly that need to be supported. This gap is bridged by plugins which provide a mechanism for supporting new provisioners. The guest machine is configured after booting is done by the provisioner. By using a provisioner to handle software installation and configuration the process becomes predictable and repeatable.

Definition of a provisioner component happens through a provisioner method. Consider the definition shown below:

The provisioner name is the only parameter passed to the provisioner method. This name will be required in the Vagrantfile to call config.vm.provisioner so that the provisioner is enabled. The provisioner defined previously will be enabled by including the entry config.vm.provision “apt_get” in the Vagrantfile. The class that implements the provisioner is inherited from Vagrant.plugin(“2”, “provisioner”), which is the method responsible for handling API version 2. Once inheriting happens the only requirement is implementation of provision method. Vagrant automatically calls the provision method without any parameters after machine creation and there are no values returned.

When using apt-get provisioner, we would define our implementation as shown below:

The provisioner defined above has a list of packages that will be installed and when it is required to provision it will run the command apt-get install. The provisioner will return output to the user showing the install status of the packages.

The Vagrantfile required to run the provisioner defined previously, is shown below.

Although Vagrant always provides reasonable defaults to ensure out of the box functionality configuration, options can be used to modify defaults. Provisioners built using plugins are used to modify configuration options. Provisioner configurations are merely specialized plugin configuration components. It is important to ensure in the component configuration definition the name is consistent with the provisioner name and the value of the second parameter is :provision so that Vagrant understands it is a configuration component.

The name of the configuration component and provisioner is consistently apt_get, and the second parameter is correctly provided, therefore Vagrant will properly handle the provisioner. In this section, we will demonstrate how to install packages specified by the user. The configuration is implemented as shown below:

There are two approaches that can be used to configure the provisioner defined above. The first approach is shown below:

In the approach above, Vagrant will handle translation of supplied options into attributes that are set on the configuration instance.

The second approach to configuring the provisioner is shown below:

The advantage of the second approach over the first approach is the class exposes methods so they are callable while in the first approach you can only set attributes.

After defining the configuration and implementing it the next step is calling it. To expose a provisioner configuration you use the @config variable in the implementation. For a configuration that does not have a provisioner configuration defined the instance variable will be nil, when a configuration is defined the instance variable will be a configuration class instance.

The implementation that will install user specified packages is shown below:

The packages that are to be installed are read from the configuration through @config.packages

Among the different provisioners there are those that besides configuring options also need to perform machine configuration. Machine configuration happens through the configure method. The only argument passed to the configure method is the machine root configuration that the provisioner modifies as necessary. In the apt-get provisioner implemented earlier there is no need for a machine configuration. For instance, if we are using Chef Solo and we need to include synced folders in the machine to ensure Cookbooks are available we would use the implementation below:

The components discussed up to this point have been concerned with adding new functionality to Vagrant. Plugins can also be developed to change the existing functionality for example disabling vagrant destroy or any other command. To change Vagrant behavior, you use components referred to as action hooks. The internal building blocks Vagrant uses to implement advanced functionality are actions. The action hook components inject themselves in the action sequence to change behavior.

An example of an action hook definition is shown below:

Defining an action hook is done by using the action_hook method that takes the action hook name and action sequence as its parameters. The action hook method also needs a block to lazily load the implementation that will be used. An argument is passed in the block that instructs Vagrant the actions to hook into.

In this article, we introduced the use of plugins to provide support to provisioners. We discussed how to define a provisioner using the provisioner method and discussed how to implement the provisioner. We also discussed how to define and implement configurations.


Please enter your comment!
Please enter your name here