vagrant-masonry 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|