vagrant-masonry 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.yardopts +6 -0
- data/CHANGELOG +190 -0
- data/Gemfile +16 -0
- data/LICENSE +15 -0
- data/README.md +129 -0
- data/docs/GettingStarted.markdown +175 -0
- data/examples/Vagrantfile +1 -0
- data/examples/roles.yaml +29 -0
- data/examples/vms.yaml +12 -0
- data/lib/config_builder.rb +24 -0
- data/lib/config_builder/action/load_extensions.rb +14 -0
- data/lib/config_builder/class_registry.rb +72 -0
- data/lib/config_builder/extension_handler.rb +22 -0
- data/lib/config_builder/filter.rb +6 -0
- data/lib/config_builder/filter/boxes.rb +22 -0
- data/lib/config_builder/filter/roles.rb +149 -0
- data/lib/config_builder/filter_stack.rb +37 -0
- data/lib/config_builder/loader.rb +23 -0
- data/lib/config_builder/loader/yaml.rb +44 -0
- data/lib/config_builder/loader/yaml_erb.rb +24 -0
- data/lib/config_builder/model.rb +67 -0
- data/lib/config_builder/model/base.rb +101 -0
- data/lib/config_builder/model/network/forwarded_port.rb +37 -0
- data/lib/config_builder/model/network/private_network.rb +15 -0
- data/lib/config_builder/model/provider/azure.rb +66 -0
- data/lib/config_builder/model/provider/libvirt.rb +108 -0
- data/lib/config_builder/model/provider/virtualbox.rb +35 -0
- data/lib/config_builder/model/provider/vmware.rb +40 -0
- data/lib/config_builder/model/provider/vmware_fusion.rb +8 -0
- data/lib/config_builder/model/provider/vmware_workstation.rb +8 -0
- data/lib/config_builder/model/provider/vsphere.rb +30 -0
- data/lib/config_builder/model/provisioner/file.rb +24 -0
- data/lib/config_builder/model/provisioner/puppet.rb +37 -0
- data/lib/config_builder/model/provisioner/puppet_server.rb +27 -0
- data/lib/config_builder/model/provisioner/shell.rb +27 -0
- data/lib/config_builder/model/root.rb +69 -0
- data/lib/config_builder/model/ssh.rb +110 -0
- data/lib/config_builder/model/synced_folder.rb +43 -0
- data/lib/config_builder/model/vm.rb +235 -0
- data/lib/config_builder/model/winrm.rb +56 -0
- data/lib/config_builder/model_delegator.rb +30 -0
- data/lib/config_builder/plugin.rb +15 -0
- data/lib/config_builder/runner.rb +33 -0
- data/lib/config_builder/version.rb +3 -0
- data/lib/vagrant-masonry.rb +1 -0
- data/spec/config_builder/filter/boxes_spec.rb +87 -0
- data/spec/config_builder/filter/roles_spec.rb +287 -0
- data/spec/config_builder/loader/yaml_spec.rb +76 -0
- data/spec/config_builder/model/provider/vmware_fusion_spec.rb +29 -0
- data/spec/spec_helper.rb +4 -0
- data/templates/locales/en.yml +11 -0
- data/vagrant-masonry.gemspec +24 -0
- metadata +128 -0
@@ -0,0 +1 @@
|
|
1
|
+
Vagrant.configure('2', &ConfigBuilder.load(:yaml, :yamldir, '/path/to/config'))
|
data/examples/roles.yaml
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
---
|
2
|
+
roles:
|
3
|
+
puppetmaster:
|
4
|
+
provider:
|
5
|
+
type: virtualbox
|
6
|
+
customize: [[modifyvm, !ruby/sym id, '--memory', 1024]]
|
7
|
+
provisioners:
|
8
|
+
-
|
9
|
+
type: shell
|
10
|
+
inline: 'echo "nameserver 8.8.8.8" > /etc/resolv.conf'
|
11
|
+
-
|
12
|
+
type: hosts
|
13
|
+
-
|
14
|
+
type: pe_bootstrap
|
15
|
+
role: !ruby/sym master
|
16
|
+
relocate_manifests: false
|
17
|
+
version: '2.6.0'
|
18
|
+
puppetagent:
|
19
|
+
provisioners:
|
20
|
+
-
|
21
|
+
type: shell
|
22
|
+
inline: 'echo "nameserver 8.8.8.8" > /etc/resolv.conf'
|
23
|
+
-
|
24
|
+
type: hosts
|
25
|
+
-
|
26
|
+
type: pe_bootstrap
|
27
|
+
role: !ruby/sym agent
|
28
|
+
version: '2.6.0'
|
29
|
+
|
data/examples/vms.yaml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'vagrant'
|
2
|
+
|
3
|
+
module ConfigBuilder
|
4
|
+
require 'config_builder/loader'
|
5
|
+
require 'config_builder/model'
|
6
|
+
require 'config_builder/runner'
|
7
|
+
require 'config_builder/plugin'
|
8
|
+
require 'config_builder/version'
|
9
|
+
|
10
|
+
def self.load(identifier, method, value)
|
11
|
+
runner = ConfigBuilder::Runner.new
|
12
|
+
runner.run(identifier, method, value)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.source_root
|
16
|
+
@source_root ||= File.expand_path('..', __FILE__)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.template_root
|
20
|
+
@template_root ||= File.expand_path('../templates', source_root)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
I18n.load_path << File.join(ConfigBuilder.template_root, 'locales/en.yml')
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'vagrant/errors'
|
2
|
+
|
3
|
+
module ConfigBuilder
|
4
|
+
|
5
|
+
# This defines a registry for classes, and behaves as a factory for registered classes.
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
class ClassRegistry
|
9
|
+
|
10
|
+
class UnknownEntry < Vagrant::Errors::VagrantError
|
11
|
+
error_key('unknown_entry', 'config_builder.class_registry')
|
12
|
+
end
|
13
|
+
|
14
|
+
class DuplicateEntry < Vagrant::Errors::VagrantError
|
15
|
+
error_key('duplicate_entry', 'config_builder.class_registry')
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(name)
|
19
|
+
@name = name
|
20
|
+
@klasses = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
# Register a class with a given identifier
|
24
|
+
#
|
25
|
+
# @param identifier [Symbol]
|
26
|
+
# @param klass [Class]
|
27
|
+
#
|
28
|
+
# @return [void]
|
29
|
+
def register(identifier, klass)
|
30
|
+
if @klasses[identifier]
|
31
|
+
raise DuplicateEntry, :registry => @name,
|
32
|
+
:identifier => identifier.inspect
|
33
|
+
else
|
34
|
+
@klasses[identifier] = klass
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Retrieve a class associated with the specified identifier
|
39
|
+
#
|
40
|
+
# @param identifier [Symbol]
|
41
|
+
#
|
42
|
+
# @return [Class]
|
43
|
+
def retrieve(identifier)
|
44
|
+
if (klass = @klasses[identifier])
|
45
|
+
klass
|
46
|
+
else
|
47
|
+
raise UnknownEntry, :registry => @name,
|
48
|
+
:identifier => identifier.inspect,
|
49
|
+
:identifiers => @klasses.keys
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Generate a class instance with the given hash
|
54
|
+
#
|
55
|
+
# @param hash [Hash] The identifier and options for the new object
|
56
|
+
# @option hash type [String] The identifier of the class to instantiate
|
57
|
+
#
|
58
|
+
# @return [Object] The generated object
|
59
|
+
def generate(hash)
|
60
|
+
config = hash.dup
|
61
|
+
identifier = config.delete('type')
|
62
|
+
|
63
|
+
klass = retrieve(identifier)
|
64
|
+
|
65
|
+
klass.new_from_hash(hash)
|
66
|
+
end
|
67
|
+
|
68
|
+
def inspect
|
69
|
+
"<#{self.class}: (#{@name}) keys: #{@klasses.keys.inspect}>"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ConfigBuilder
|
2
|
+
# Welcome to hell.
|
3
|
+
# @api private
|
4
|
+
class ExtensionHandler
|
5
|
+
def initialize
|
6
|
+
@logger = Log4r::Logger.new('vagrant::config_builder::extension_handler')
|
7
|
+
end
|
8
|
+
|
9
|
+
def load_from_plugins
|
10
|
+
Vagrant.plugin('2').manager.registered.each do |plugin|
|
11
|
+
load_plugin(plugin)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def load_plugin(plugin)
|
16
|
+
if plugin.respond_to? :config_builder_hook
|
17
|
+
@logger.info "Loading config_builder extension #{plugin.inspect}"
|
18
|
+
plugin.config_builder_hook
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class ConfigBuilder::Filter::Boxes
|
2
|
+
|
3
|
+
attr_reader :boxes
|
4
|
+
|
5
|
+
def set_config(root_config)
|
6
|
+
@root_config = root_config
|
7
|
+
@boxes = (@root_config.delete('boxes') || {})
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
return @root_config if @root_config['vms'].nil?
|
12
|
+
|
13
|
+
@root_config['vms'].each { |vm_hash| filter_vm(vm_hash) }
|
14
|
+
@root_config
|
15
|
+
end
|
16
|
+
|
17
|
+
def filter_vm(vm_hash)
|
18
|
+
if (box_name = vm_hash['box'])
|
19
|
+
vm_hash['box_url'] ||= @boxes[box_name]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# The 'roles' filter adds a mechanism for defining generic VM roles and
|
2
|
+
# applying them to VMs.
|
3
|
+
#
|
4
|
+
# Defining roles
|
5
|
+
# --------------
|
6
|
+
#
|
7
|
+
# This filter adds support for a top level `roles` key. It contains a hash of
|
8
|
+
# role names that define a hash containing the role behavior.
|
9
|
+
#
|
10
|
+
# @note: The 'vms' field is of type Array, while the 'roles' field is of type
|
11
|
+
# Hash. This is because order of declaration matters for the actual VMs, while
|
12
|
+
# the order of declaration of roles does not matter.
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# >> run()
|
16
|
+
# => {
|
17
|
+
# 'roles' => {
|
18
|
+
# 'webserver' => {
|
19
|
+
# 'synced_folders' => [
|
20
|
+
# {'host_path' => './www', 'guest_path' => '/var/www'},
|
21
|
+
# {'host_path' => './webserver-binaries', 'guest_path' => '/opt/webserver-binaries'},
|
22
|
+
# ]
|
23
|
+
# },
|
24
|
+
# 'database' => {
|
25
|
+
# 'provisioners' => [
|
26
|
+
# {'type' => 'puppet', 'manifest' => 'dbserver.pp'},
|
27
|
+
# {'type' => 'shell', 'path' => 'scripts/initialize-db.sh'},
|
28
|
+
# ],
|
29
|
+
# }
|
30
|
+
# },
|
31
|
+
# 'vms' => [
|
32
|
+
# {'name' => 'web', 'roles' => 'webserver'},
|
33
|
+
# {'name' => 'db', 'roles' => 'database'},
|
34
|
+
# {'name' => 'standalone', 'roles' => ['webserver', 'database']},
|
35
|
+
# ],
|
36
|
+
# }
|
37
|
+
#
|
38
|
+
class ConfigBuilder::Filter::Roles
|
39
|
+
|
40
|
+
# @!attribute [r] roles
|
41
|
+
# @return [Hash<String, Object>]
|
42
|
+
attr_reader :roles
|
43
|
+
|
44
|
+
def set_config(root_config)
|
45
|
+
@root_config = root_config
|
46
|
+
@roles = @root_config.delete('roles')
|
47
|
+
@vms = @root_config.delete('vms')
|
48
|
+
end
|
49
|
+
|
50
|
+
def run
|
51
|
+
return @root_config if @vms.nil?
|
52
|
+
|
53
|
+
@root_config['vms'] = @vms.map { |vm_hash| filter_vm(vm_hash) }
|
54
|
+
@root_config
|
55
|
+
end
|
56
|
+
|
57
|
+
# @param old_vm [Hash]
|
58
|
+
#
|
59
|
+
# @return [Hash] The filtered VM
|
60
|
+
def filter_vm(old_vm)
|
61
|
+
role_list = old_vm.delete('roles')
|
62
|
+
node_stack = roles_by_name(role_list)
|
63
|
+
|
64
|
+
node_stack << old_vm
|
65
|
+
|
66
|
+
new_vm = node_stack.inject({}) do |accumulator, role|
|
67
|
+
merge_nodes(accumulator, role)
|
68
|
+
end
|
69
|
+
|
70
|
+
new_vm
|
71
|
+
end
|
72
|
+
|
73
|
+
# Fetch the role associated with the given name
|
74
|
+
#
|
75
|
+
# @param name [String]
|
76
|
+
#
|
77
|
+
# @return [Hash<String, Object>]
|
78
|
+
def role(name)
|
79
|
+
if (retval = @roles[name])
|
80
|
+
retval
|
81
|
+
else
|
82
|
+
raise ArgumentError, "Requested role #{name.inspect} is not defined, available roles: #{@roles.keys}."
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# @overload roles_by_name(name)
|
87
|
+
# @param name [String] A single role name
|
88
|
+
# @return [Array<Hash>] An array containing the requested role
|
89
|
+
#
|
90
|
+
# @overload roles_by_name(names)
|
91
|
+
# @param names [Array<String>] A list of role names
|
92
|
+
# @return [Array<Hash>] An array containing all of the requested roles in the
|
93
|
+
# order requested.
|
94
|
+
#
|
95
|
+
# @overload roles_by_name(nothing)
|
96
|
+
# @param nothing [NilClass] nil
|
97
|
+
# @return [Array<>] An empty array
|
98
|
+
#
|
99
|
+
# @return [Array]
|
100
|
+
def roles_by_name(field)
|
101
|
+
|
102
|
+
case field
|
103
|
+
when Array then names = field
|
104
|
+
when String then names = [field]
|
105
|
+
when NilClass then names = []
|
106
|
+
end
|
107
|
+
|
108
|
+
names.map { |name| role(name) }
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
# Merge two node hash structures, with values in `right` overwriting values
|
114
|
+
# in `left`.
|
115
|
+
#
|
116
|
+
# @param left [Hash]
|
117
|
+
# @param right [Hash]
|
118
|
+
#
|
119
|
+
# @return [Hash]
|
120
|
+
def merge_nodes(left, right)
|
121
|
+
retval = right.clone
|
122
|
+
|
123
|
+
array_keys = %w[
|
124
|
+
providers
|
125
|
+
provisioners
|
126
|
+
synced_folders
|
127
|
+
forwarded_ports
|
128
|
+
private_networks
|
129
|
+
public_networks
|
130
|
+
guest
|
131
|
+
]
|
132
|
+
|
133
|
+
array_keys.each do |key|
|
134
|
+
if (left[key] and right[key])
|
135
|
+
retval[key] += left[key]
|
136
|
+
elsif left[key]
|
137
|
+
retval[key] = left[key].clone
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
single_keys = %w[provider box name communicator]
|
142
|
+
|
143
|
+
single_keys.each do |key|
|
144
|
+
retval[key] = left[key] if left[key]
|
145
|
+
end
|
146
|
+
|
147
|
+
retval
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'config_builder/filter'
|
2
|
+
|
3
|
+
module ConfigBuilder
|
4
|
+
class FilterStack
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@filter_stack = []
|
8
|
+
end
|
9
|
+
|
10
|
+
# @param input [Hash]
|
11
|
+
#
|
12
|
+
# @return [Hash]
|
13
|
+
def filter(input)
|
14
|
+
set_filters(input.delete(:filters))
|
15
|
+
|
16
|
+
output = @filter_stack.inject(input) do |current_input, filter|
|
17
|
+
filter.set_config(current_input)
|
18
|
+
filter.run
|
19
|
+
end
|
20
|
+
|
21
|
+
output
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# @note The implementation of this method is not final, use at your own peril.
|
27
|
+
# @api private
|
28
|
+
def set_filters(list)
|
29
|
+
if list.nil?
|
30
|
+
@filter_stack << ConfigBuilder::Filter::Roles.new
|
31
|
+
@filter_stack << ConfigBuilder::Filter::Boxes.new
|
32
|
+
else
|
33
|
+
@filter_stack = list
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'config_builder/class_registry'
|
2
|
+
|
3
|
+
module ConfigBuilder
|
4
|
+
module Loader
|
5
|
+
@registry = ConfigBuilder::ClassRegistry.new(:loader)
|
6
|
+
|
7
|
+
def self.register(identifier, klass)
|
8
|
+
@registry.register(identifier, klass)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.retrieve(identifier)
|
12
|
+
@registry.retrieve(identifier)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.generate(identifier, method_name, method_value)
|
16
|
+
obj = retrieve(identifier).new
|
17
|
+
obj.send(method_name, method_value)
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'config_builder/loader/yaml'
|
21
|
+
require 'config_builder/loader/yaml_erb'
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'deep_merge/core'
|
3
|
+
|
4
|
+
class ConfigBuilder::Loader::YAML
|
5
|
+
|
6
|
+
# Load configuration from YAML files in one or more directories
|
7
|
+
#
|
8
|
+
# @overload yamldir(path)
|
9
|
+
# @param path [String] A directory path containing YAML files
|
10
|
+
# @overload yamldir(paths)
|
11
|
+
# @param paths [Array<String>] A list of directory paths containing YAML files
|
12
|
+
#
|
13
|
+
# @return [Hash]
|
14
|
+
def yamldir(input)
|
15
|
+
dirs = Array(input)
|
16
|
+
|
17
|
+
files = dirs.map do |dir|
|
18
|
+
pattern = File.join(dir, '*.{yml,yaml}')
|
19
|
+
Dir.glob(pattern)
|
20
|
+
end.flatten
|
21
|
+
|
22
|
+
rv = {}
|
23
|
+
|
24
|
+
files.each do |file|
|
25
|
+
contents = yamlfile(file)
|
26
|
+
if contents.is_a? Hash
|
27
|
+
rv = DeepMerge::deep_merge!(contents, rv, {:preserve_unmergables => false})
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
rv
|
32
|
+
end
|
33
|
+
|
34
|
+
# Load configuration from a file
|
35
|
+
#
|
36
|
+
# @param file_path [String]
|
37
|
+
#
|
38
|
+
# @return [Hash]
|
39
|
+
def yamlfile(file_path)
|
40
|
+
::YAML.load_file(file_path)
|
41
|
+
end
|
42
|
+
|
43
|
+
ConfigBuilder::Loader.register(:yaml, self)
|
44
|
+
end
|