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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.yardopts +6 -0
  4. data/CHANGELOG +190 -0
  5. data/Gemfile +16 -0
  6. data/LICENSE +15 -0
  7. data/README.md +129 -0
  8. data/docs/GettingStarted.markdown +175 -0
  9. data/examples/Vagrantfile +1 -0
  10. data/examples/roles.yaml +29 -0
  11. data/examples/vms.yaml +12 -0
  12. data/lib/config_builder.rb +24 -0
  13. data/lib/config_builder/action/load_extensions.rb +14 -0
  14. data/lib/config_builder/class_registry.rb +72 -0
  15. data/lib/config_builder/extension_handler.rb +22 -0
  16. data/lib/config_builder/filter.rb +6 -0
  17. data/lib/config_builder/filter/boxes.rb +22 -0
  18. data/lib/config_builder/filter/roles.rb +149 -0
  19. data/lib/config_builder/filter_stack.rb +37 -0
  20. data/lib/config_builder/loader.rb +23 -0
  21. data/lib/config_builder/loader/yaml.rb +44 -0
  22. data/lib/config_builder/loader/yaml_erb.rb +24 -0
  23. data/lib/config_builder/model.rb +67 -0
  24. data/lib/config_builder/model/base.rb +101 -0
  25. data/lib/config_builder/model/network/forwarded_port.rb +37 -0
  26. data/lib/config_builder/model/network/private_network.rb +15 -0
  27. data/lib/config_builder/model/provider/azure.rb +66 -0
  28. data/lib/config_builder/model/provider/libvirt.rb +108 -0
  29. data/lib/config_builder/model/provider/virtualbox.rb +35 -0
  30. data/lib/config_builder/model/provider/vmware.rb +40 -0
  31. data/lib/config_builder/model/provider/vmware_fusion.rb +8 -0
  32. data/lib/config_builder/model/provider/vmware_workstation.rb +8 -0
  33. data/lib/config_builder/model/provider/vsphere.rb +30 -0
  34. data/lib/config_builder/model/provisioner/file.rb +24 -0
  35. data/lib/config_builder/model/provisioner/puppet.rb +37 -0
  36. data/lib/config_builder/model/provisioner/puppet_server.rb +27 -0
  37. data/lib/config_builder/model/provisioner/shell.rb +27 -0
  38. data/lib/config_builder/model/root.rb +69 -0
  39. data/lib/config_builder/model/ssh.rb +110 -0
  40. data/lib/config_builder/model/synced_folder.rb +43 -0
  41. data/lib/config_builder/model/vm.rb +235 -0
  42. data/lib/config_builder/model/winrm.rb +56 -0
  43. data/lib/config_builder/model_delegator.rb +30 -0
  44. data/lib/config_builder/plugin.rb +15 -0
  45. data/lib/config_builder/runner.rb +33 -0
  46. data/lib/config_builder/version.rb +3 -0
  47. data/lib/vagrant-masonry.rb +1 -0
  48. data/spec/config_builder/filter/boxes_spec.rb +87 -0
  49. data/spec/config_builder/filter/roles_spec.rb +287 -0
  50. data/spec/config_builder/loader/yaml_spec.rb +76 -0
  51. data/spec/config_builder/model/provider/vmware_fusion_spec.rb +29 -0
  52. data/spec/spec_helper.rb +4 -0
  53. data/templates/locales/en.yml +11 -0
  54. data/vagrant-masonry.gemspec +24 -0
  55. metadata +128 -0
@@ -0,0 +1 @@
1
+ Vagrant.configure('2', &ConfigBuilder.load(:yaml, :yamldir, '/path/to/config'))
@@ -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
+
@@ -0,0 +1,12 @@
1
+ ---
2
+ vms:
3
+ -
4
+ name: master
5
+ private_networks: [ {auto_network: true} ]
6
+ box: centos-5-i386
7
+ roles: puppetmaster
8
+ -
9
+ name: agent
10
+ private_networks: [ {auto_network: true} ]
11
+ box: centos-5-i386
12
+ roles: puppetagent
@@ -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,14 @@
1
+ module ConfigBuilder
2
+ module Action
3
+ class LoadExtensions
4
+ def initialize(app, env)
5
+ @app, @env = app, env
6
+ end
7
+
8
+ def call(hash)
9
+ hash[:env].hook(:config_builder_extension)
10
+ @app.call(hash)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -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,6 @@
1
+ module ConfigBuilder
2
+ module Filter
3
+ require 'config_builder/filter/roles'
4
+ require 'config_builder/filter/boxes'
5
+ end
6
+ 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