167 lines
4.9 KiB
Markdown
167 lines
4.9 KiB
Markdown
---
|
|
page_title: "Custom Configuration - Plugin Development"
|
|
sidebar_current: "plugins-configuration"
|
|
---
|
|
|
|
# Plugin Development: Configuration
|
|
|
|
This page documents how to add new configuration options to Vagrant,
|
|
settable with `config.YOURKEY` in Vagrantfiles. Prior to reading this, you should be familiar
|
|
with the [plugin development basics](/v2/plugins/development-basics.html).
|
|
|
|
<div class="alert alert-warn">
|
|
<p>
|
|
<strong>Warning: Advanced Topic!</strong> Developing plugins is an
|
|
advanced topic that only experienced Vagrant users who are reasonably
|
|
comfortable with Ruby should approach.
|
|
</p>
|
|
</div>
|
|
|
|
## Definition Component
|
|
|
|
Within the context of a plugin definition, new configuration keys can be defined
|
|
like so:
|
|
|
|
```ruby
|
|
config "foo" do
|
|
require_relative "config"
|
|
Config
|
|
end
|
|
```
|
|
|
|
Configuration keys are defined with the `config` method, which takes as an
|
|
argument the name of the configuration variable as the argument. This
|
|
means that the configuration object will be accessible via `config.foo`
|
|
in Vagrantfiles. Then, the block argument returns a class that implements
|
|
the `Vagrant.plugin(2, :config)` interface.
|
|
|
|
## Implementation
|
|
|
|
Implementations of configuration keys should subclass `Vagrant.plugin(2, :config)`,
|
|
which is a Vagrant method that will return the proper subclass for a version
|
|
2 configuration section. The implementation is very simple, and acts mostly
|
|
as a plain Ruby object. Here is an example:
|
|
|
|
```ruby
|
|
class Config < Vagrant.plugin(2, :config)
|
|
attr_accessor :widgets
|
|
|
|
def initialize
|
|
@widgets = UNSET_VALUE
|
|
end
|
|
|
|
def finalize!
|
|
@widgets = 0 if @widgets == UNSET_VALUE
|
|
end
|
|
end
|
|
```
|
|
|
|
When using this configuration class, it looks like the following:
|
|
|
|
```ruby
|
|
Vagrant.configure("2") do |config|
|
|
# ...
|
|
|
|
config.foo.widgets = 12
|
|
end
|
|
```
|
|
|
|
Easy. The only odd thing is the `UNSET_VALUE` bits above. This is actually
|
|
so that Vagrant can properly automatically merge multiple configurations.
|
|
Merging is covered in the next section, and `UNSET_VALUE` will be explained
|
|
there.
|
|
|
|
## Merging
|
|
|
|
Vagrant works by loading [multiple Vagrantfiles and merging them](/v2/vagrantfile/index.html#load-order).
|
|
This merge logic is built-in to configuration classes. When merging two
|
|
configuration objects, we'll call them "old" and "new", it'll by default
|
|
take all the instance variables defined on "new" that aren't `UNSET_VALUE`
|
|
and set them onto the merged result.
|
|
|
|
The reason `UNSET_VALUE` is used instead of Ruby's `nil` is because
|
|
it is possible that you want the default to be some value, and the user
|
|
actually wants to set the value to `nil`, and it is impossible for Vagrant
|
|
to automatically determine whether the user set the instance variable, or
|
|
if it was defaulted as nil.
|
|
|
|
This merge logic is what you want almost every time. Hence, in the example
|
|
above, `@widgets` is set to `UNSET_VALUE`. If we had two Vagrant configuration
|
|
objects in the same file, then Vagrant would properly merge the follows.
|
|
The example below shows this:
|
|
|
|
```ruby
|
|
Vagrant.configure("2") do |config|
|
|
config.widgets = 1
|
|
end
|
|
|
|
Vagrant.configure("2") do |config|
|
|
# ... other stuff
|
|
end
|
|
|
|
Vagrant.configure("2") do |config|
|
|
config.widgets = 2
|
|
end
|
|
```
|
|
|
|
If this were placed in a Vagrantfile, after merging, the value of widgets
|
|
would be "2".
|
|
|
|
The `finalize!` method is called only once ever on the final configuration
|
|
object in order to set defaults. If `finalize!` is called, that configuration
|
|
will never be merged again, it is final. This lets you detect any `UNSET_VALUE`
|
|
and set the proper default, as we do in the above example.
|
|
|
|
Of course, sometimes you want custom merge logic. Let's say we
|
|
wanted our widgets to be additive. We can override the `merge` method to
|
|
do this:
|
|
|
|
```ruby
|
|
class Config < Vagrant.config("2", :config)
|
|
attr_accessor :widgets
|
|
|
|
def initialize
|
|
@widgets = 0
|
|
end
|
|
|
|
def merge(other)
|
|
super.tap do |result|
|
|
result.widgets = @widgets + other.widgets
|
|
end
|
|
end
|
|
end
|
|
```
|
|
|
|
In this case, we didn't use `UNSET_VALUE` for widgets because we didn't
|
|
need that behavior. We default to 0 and always merge by summing the
|
|
two widgets. Now, if we ran the example above that had the 3 configuration
|
|
blocks, the final value of widgets would be "3".
|
|
|
|
## Validation
|
|
|
|
Configuration classes are also responsible for validating their own
|
|
values. Vagrant will call the `validate` method to do this. An example
|
|
validation method is shown below:
|
|
|
|
```ruby
|
|
class Config < Vagrant.plugin("2", :config)
|
|
# ...
|
|
|
|
def validate(machine)
|
|
if @widgets <= 5
|
|
return { "foo" => ["widgets must be greater than 5"] }
|
|
end
|
|
|
|
{}
|
|
end
|
|
end
|
|
```
|
|
|
|
The validation method is given a `machine` object, since validation is
|
|
done for each machine that Vagrant is managing. This allows you to
|
|
conditionally validate some keys based on the state of the machine and so on.
|
|
|
|
The return value is a Ruby Hash object, where the key is a section name,
|
|
and the value is a list of error messages. These will be displayed by
|
|
Vagrant. If there are no errors, an empty hash must be returned.
|