vagrant-masonry 0.13.0

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