vagrant-ansible_auto 0.1.5 → 0.2.1

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +172 -0
  5. data/README.md +53 -12
  6. data/Rakefile +9 -7
  7. data/TODO.md +14 -0
  8. data/Vagrantfile +37 -15
  9. data/lib/vagrant/ansible_auto/cap/guest/posix/check_open_port.rb +22 -3
  10. data/lib/vagrant/ansible_auto/cap/guest/posix/executable_installed.rb +10 -2
  11. data/lib/vagrant/ansible_auto/cap/guest/posix/gateway_addresses.rb +8 -23
  12. data/lib/vagrant/ansible_auto/cap/guest/posix/private_key.rb +16 -1
  13. data/lib/vagrant/ansible_auto/cap/guest/posix/public_key.rb +18 -3
  14. data/lib/vagrant/ansible_auto/cap/guest/posix/ssh_server_address.rb +22 -12
  15. data/lib/vagrant/ansible_auto/cap/guest/posix.rb +16 -0
  16. data/lib/vagrant/ansible_auto/command/inventory.rb +37 -11
  17. data/lib/vagrant/ansible_auto/command/root.rb +34 -31
  18. data/lib/vagrant/ansible_auto/config.rb +74 -33
  19. data/lib/vagrant/ansible_auto/errors.rb +30 -1
  20. data/lib/vagrant/ansible_auto/host.rb +123 -34
  21. data/lib/vagrant/ansible_auto/inventory.rb +196 -34
  22. data/lib/vagrant/ansible_auto/plugin.rb +23 -8
  23. data/lib/vagrant/ansible_auto/provisioner.rb +121 -79
  24. data/lib/vagrant/ansible_auto/util/config.rb +61 -0
  25. data/lib/vagrant/ansible_auto/util/hash_with_indifferent_access.rb +58 -0
  26. data/lib/vagrant/ansible_auto/util/keys.rb +49 -0
  27. data/lib/vagrant/ansible_auto/util/shell_quote.rb +24 -0
  28. data/lib/vagrant/ansible_auto/version.rb +2 -1
  29. data/lib/vagrant/ansible_auto.rb +15 -0
  30. data/locales/en.yml +34 -0
  31. data/spec/spec_helper.rb +5 -85
  32. data/spec/support/context.rb +111 -0
  33. data/spec/support/matchers.rb +45 -0
  34. data/spec/unit/vagrant/ansible_auto/config_spec.rb +72 -0
  35. data/spec/unit/vagrant/ansible_auto/host_spec.rb +131 -0
  36. data/spec/unit/vagrant/ansible_auto/inventory_spec.rb +349 -0
  37. data/spec/unit/vagrant/ansible_auto/provisioner_spec.rb +248 -0
  38. data/spec/unit/vagrant/ansible_auto/util/config_spec.rb +63 -0
  39. data/spec/unit/vagrant/ansible_auto/util/keys_spec.rb +66 -0
  40. data/vagrant-ansible_auto.gemspec +6 -4
  41. data/vagrant-spec.config.rb +3 -0
  42. data/yard/extensions.rb +45 -0
  43. metadata +36 -11
  44. data/Vagrantfile2 +0 -4
  45. data/Vagrantfile3 +0 -8
  46. data/Vagrantfile4 +0 -31
  47. data/lib/vagrant/ansible_auto/cap/guest/posix/bash_installed.rb +0 -30
  48. data/lib/vagrant/ansible_auto/util.rb +0 -24
  49. data/spec/vagrant/ansible_auto/host_spec.rb +0 -43
  50. data/spec/vagrant/ansible_auto/inventory_spec.rb +0 -79
@@ -1,101 +1,178 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'set'
3
4
  require 'json'
5
+
6
+ require 'vagrant/ansible_auto/errors'
4
7
  require 'vagrant/ansible_auto/host'
8
+ require 'vagrant/ansible_auto/util/config'
9
+ require 'vagrant/ansible_auto/util/hash_with_indifferent_access'
5
10
 
6
11
  module VagrantPlugins
7
12
  module AnsibleAuto
13
+ # Class representing an Ansible inventory with hosts, groups, group
14
+ # children, and group variables
8
15
  class Inventory
16
+ include VagrantPlugins::AnsibleAuto::Util::Config
17
+
18
+ UNNAMED_GROUP = '_'.freeze
19
+
20
+ # @return [Hash{String=>Set<Host>}] group names mapped to their members
9
21
  def groups
10
- @groups = Hash.new { |hash, key| hash[key] = Set.new } if unset?(@groups)
22
+ if unset? @groups
23
+ @groups = Util::HashWithIndifferentAccess.new do |hash, key|
24
+ hash[key] = Set.new
25
+ end
26
+ end
27
+
11
28
  @groups
12
29
  end
13
30
 
31
+ # @return [Set<Host>] the hosts in the {Inventory}
14
32
  def hosts
15
33
  @hosts = Set.new if unset?(@hosts)
16
34
  @hosts
17
35
  end
18
36
 
37
+ # @return [Hash{String=>Hash}] group names mapped to their variables
19
38
  def vars
20
- @vars = Hash.new { |hash, key| hash[key] = {} } if unset?(@vars)
39
+ if unset? @vars
40
+ @vars = Util::HashWithIndifferentAccess.new do |hash, key|
41
+ hash[key] = Util::HashWithIndifferentAccess.new
42
+ end
43
+ end
44
+
21
45
  @vars
22
46
  end
23
47
 
48
+ # @return [Hash{String=>Set}] group names mapped to their children
24
49
  def children
25
- @children = Hash.new { |hash, key| hash[key] = Set.new } if unset?(@children)
50
+ if unset? @children
51
+ @children = Util::HashWithIndifferentAccess.new do |hash, key|
52
+ hash[key] = Set.new
53
+ end
54
+ end
55
+
26
56
  @children
27
57
  end
28
58
 
59
+ # Set the groups for the {Inventory}.
60
+ # @note overwrites the current {#groups}.
61
+ # @param [Hash{String=>Array,Hash}] new_groups the groups to assign to the
62
+ # {Inventory}
63
+ # @option new_groups [Array] group the hosts in +group+
64
+ # @option new_groups [Hash] group:vars the variables for +group+
65
+ # @option new_groups [Array] group:chilren the child groups for +group+
66
+ # @return [Hash{String=>Array,Hash}] the created groups
29
67
  def groups=(new_groups)
30
68
  @groups = nil
31
69
 
32
70
  new_groups.each do |group_heading, entries|
33
- group, type = group_heading.to_s.split(':')
71
+ group, type = parse_group_heading(group_heading)
72
+
34
73
  case type
35
74
  when 'vars'
36
- entries = {} if entries.nil?
37
- vars_for(group, entries)
75
+ entries = {} if entries.nil?
76
+ vars_for(group, entries)
38
77
  when 'children'
39
- entries = [] if entries.nil?
40
- children_of(group, *entries)
78
+ entries = [] if entries.nil?
79
+ children_of(group, *entries)
80
+ else
81
+ entries = [] if entries.nil?
82
+ if entries.is_a? Hash
83
+ add_complex_group(group, entries)
41
84
  else
42
- entries = [] if entries.nil?
43
85
  add_group(group, *entries)
86
+ end
44
87
  end
45
88
  end
46
89
 
47
90
  groups
48
91
  end
49
92
 
50
- def hosts=(_new_hosts)
93
+ # Set the hosts for the {Inventory}
94
+ # @note overwrites the current {#hosts}.
95
+ # @param [Hash{String=>Hash}] new_hosts the hosts in the inventory
96
+ # @option new_hosts [Hash{String=>Hash, nil}] host a host plus any hostvars
97
+ # @return [Hash{String=>Hash}] the created hosts
98
+ def hosts=(new_hosts)
51
99
  @hosts = nil
52
100
 
53
- news_hosts.each do |host, hostvars|
54
- hostvars = {} if hostvars.nil?
55
- add_host(host, hostvars)
101
+ new_hosts.each do |host, hostvars|
102
+ add_host(host, hostvars || {})
56
103
  end
57
104
 
58
105
  hosts
59
106
  end
60
107
 
108
+ # Set the variables for the groups in the {Inventory}
109
+ # @note overwrites the current {#vars}
110
+ # @param [Hash{String,Hash}] new_vars the variables to add to the
111
+ # {Inventory}
112
+ # @option new_vars [Hash{String,Hash}] group a group plus any group
113
+ # variables
114
+ # @return [Hash{String,Hash}] the created variables
61
115
  def vars=(new_vars)
62
116
  @vars = nil
63
117
 
64
- new_vars.each_pair do |group, group_vars|
118
+ new_vars.each do |group, group_vars|
65
119
  vars_for(group, group_vars)
66
120
  end
67
121
 
68
122
  vars
69
123
  end
70
124
 
125
+ # Set the children of the groups in the {Inventory}
126
+ # @note overwrites the currrent {#children}
127
+ # @param [Hash{String=>Array<String>}] new_children the group children to add to
128
+ # the {Inventory}
129
+ # @option new_children [Array<String>] group a group name and the list of
130
+ # its children
131
+ # @return [Hash{String=>Set<String>}] the created children
71
132
  def children=(new_children)
72
133
  @children = nil
73
134
 
74
- new_children.each_pair do |group, group_children|
135
+ new_children.each do |group, group_children|
75
136
  children_of(group, *group_children)
76
137
  end
77
138
 
78
139
  children
79
140
  end
80
141
 
142
+ # Add a group to the {Inventory}
143
+ # @param [#to_s] group the name of the group
144
+ # @param [Array] members the hosts to add to the group
145
+ # @return [Set] the members of the added group
81
146
  def add_group(group, *members)
147
+ raise InvalidGroupNameError, group: group if group.to_s == UNNAMED_GROUP
148
+
149
+ add_complex_group(group, members.pop) if members.last.is_a? Hash
150
+
82
151
  groups[group.to_s].tap do |group_members|
83
152
  group_members.merge(members)
84
153
  return group_members
85
154
  end
86
155
  end
87
156
 
88
- def add_host(host, hostvars = {})
157
+ # Add a host to the {Inventory}
158
+ # @param [Host,String,Symbol,Vagrant::Machine] host the host to add
159
+ # @param [Hash] hostvars hostvars to assign to the host
160
+ def add_host(host, hostvars = nil)
89
161
  hosts.add case host
90
162
  when Host
91
- host
163
+ host.tap { |h| h.hostvars = hostvars unless hostvars.nil? }
92
164
  when String, Symbol
93
- Host.new(host, hostvars)
165
+ Host.new(host, hostvars || {})
166
+ when Vagrant::Machine
167
+ HostMachine.new(host, hostvars || {})
94
168
  else
95
- HostMachine.new(host, hostvars)
169
+ raise Errors::InvalidHostTypeError, type: host.class.name
96
170
  end
97
171
  end
98
172
 
173
+ # Assign variables to a group
174
+ # @param [#to_s] group the name of the group
175
+ # @param [Hash] new_vars the variables to assign to the group
99
176
  def vars_for(group, new_vars = {})
100
177
  vars[group.to_s].tap do |group_vars|
101
178
  group_vars.merge!(new_vars)
@@ -103,6 +180,9 @@ module VagrantPlugins
103
180
  end
104
181
  end
105
182
 
183
+ # Assign child groups to a group
184
+ # @param [#to_s] group the name of the group
185
+ # @param [Array] new_children the child groups to assign to the group
106
186
  def children_of(group, *new_children)
107
187
  children[group.to_s].tap do |group_children|
108
188
  group_children.merge(new_children.map(&:to_s))
@@ -110,7 +190,12 @@ module VagrantPlugins
110
190
  end
111
191
  end
112
192
 
193
+ # Perform in-place merge of two {Inventory} instances
194
+ # @param [Inventory] other the inventory to merge into this one
195
+ # @return [self] the updated inventory
113
196
  def merge!(other)
197
+ hosts.merge(other.hosts)
198
+
114
199
  @groups = groups.merge(other.groups) do |_group, group_members, other_group_members|
115
200
  group_members.merge(other_group_members)
116
201
  end
@@ -126,34 +211,69 @@ module VagrantPlugins
126
211
  self
127
212
  end
128
213
 
214
+ # Merge two {Inventory} instances
215
+ # @param [Inventory] other the inventory to merge into this one
216
+ # @return [Inventory] the updated inventory
129
217
  def merge(other)
130
218
  clone.merge!(other)
131
219
  end
132
220
 
221
+ # @return [Hash{String=>Hash}] the merged hostvars for all hosts in the
222
+ # inventory
133
223
  def hostvars
134
- hosts.each_with_object({}) { |host, acc| acc.merge!(host.hostvars) }
224
+ Hash[hosts.map { |h| [h.name, h.hostvars] }]
135
225
  end
136
226
 
227
+ # A representation of an {Inventory} as a +Hash+
228
+ # @note the hosts in the inventory will be returned as +Hash+es under the
229
+ # key {UNNAMED_GROUP}
230
+ # @return [Hash{String=>Hash,Array}] a +Hash+ containing the hosts in the
231
+ # inventory (coerced to hashes) under the {UNNAMED_GROUP} key, as well
232
+ # as each group name mapped to a subhashes with hosts under the key
233
+ # +"hosts"+, variables under the key +"vars"+, and children under the
234
+ # key +"children"+
137
235
  def to_h
138
- {}.tap do |h|
139
- h.merge!(Hash[groups.map { |group, members| [group, members.to_a] }])
140
- h['_'] = hosts.map(&:to_h)
141
- h.merge!(Hash[vars.map { |group, group_vars| ["#{group}:vars", group_vars] }])
142
- h.merge!(Hash[children.map { |group, group_children| ["#{group}:children", group_children.to_a] }])
236
+ Hash.new { |h, k| h[k] = {} }.tap do |h|
237
+ h[UNNAMED_GROUP] = hosts.map(&:to_h)
238
+
239
+ groups.each do |group, group_hosts|
240
+ h[group]['hosts'] = group_hosts.to_a
241
+ end
242
+
243
+ vars.each do |group, group_vars|
244
+ h[group]['vars'] = group_vars
245
+ end
246
+
247
+ children.each do |group, group_children|
248
+ h[group]['children'] = group_children.to_a
249
+ end
143
250
  end
144
251
  end
145
252
 
146
- def to_json
253
+ # @return [String] the {Inventory} represented as a JSON object in the
254
+ # form of a "Dynamic Inventory"
255
+ # @see http://docs.ansible.com/ansible/intro_dynamic_inventory.html
256
+ def to_json(*args)
147
257
  to_h.tap do |h|
148
- h.delete('_')
149
- h['_meta'] = hostvars
150
- end.to_json
258
+ h.delete(UNNAMED_GROUP)
259
+ h['_meta'] = { 'hostvars' => hostvars }
260
+ end.to_json(*args)
151
261
  end
152
262
 
263
+ # Return the {Inventory} as an INI document
264
+ # @return [String] the inventory in a newline-separated string
153
265
  def to_ini
154
266
  with_ini_lines.to_a.join("\n")
155
267
  end
156
268
 
269
+ # Iterate over the lines of the {Inventory} represented as an INI
270
+ # document
271
+ # @overload
272
+ # @return [Enumerator] the lines of the INI document
273
+ # @overload
274
+ # @yieldparam [String] each line in the INI document
275
+ # @see with_ini_lines_hosts
276
+ # @see with_ini_lines_groups
157
277
  def with_ini_lines
158
278
  return enum_for(__method__) unless block_given?
159
279
 
@@ -162,27 +282,69 @@ module VagrantPlugins
162
282
  end
163
283
  end
164
284
 
285
+ # Iterate over the hosts in the {Inventory} represented as an INI
286
+ # document
287
+ # @overload
288
+ # @return [Enumerator] the lines of the INI document
289
+ # @overload
290
+ # @yieldparam [String] each line in the INI document
165
291
  def with_ini_lines_hosts
166
292
  return enum_for(__method__) unless block_given?
167
293
  hosts.each { |host| yield host.to_ini }
168
294
  end
169
295
 
296
+ # Iterate over the groups in the {Inventory} represented as an INI
297
+ # document
298
+ # @overload
299
+ # @return [Enumerator] the lines of the INI document
300
+ # @overload
301
+ # @yieldparam [String] each line in the INI document
170
302
  def with_ini_lines_groups
171
303
  return enum_for(__method__) unless block_given?
172
304
 
173
- to_h.tap { |h| h.delete('_') }.each do |group, entries|
305
+ to_h.tap { |h| h.delete(UNNAMED_GROUP) }.sort.each do |group, entries|
174
306
  yield "[#{group}]"
175
307
 
176
- (entries.is_a?(Hash) ? entries.map { |entry, value| "#{entry}=#{value}" } : entries).each do |entry|
177
- yield entry
308
+ entries.fetch('hosts', []).sort.each { |h| yield h }
309
+
310
+ if entries.key? 'children'
311
+ yield "[#{group}:children]"
312
+ entries['children'].sort.each { |c| yield c }
313
+ end
314
+
315
+ if entries.key? 'vars'
316
+ yield "[#{group}:vars]"
317
+ entries['vars'].sort.each { |k, v| yield "#{k} = #{v}" }
318
+ end
319
+ end
320
+ end
321
+
322
+ # A sanity check of the inventory's state
323
+ # @return [void]
324
+ # @raise [Errors::GroupMissingChildError] when a group has a child group
325
+ # that doesn't exist
326
+ def validate!
327
+ children.each do |group, group_children|
328
+ group_children.each do |child|
329
+ raise Errors::GroupMissingChildError, group: group, child: child unless groups.key? child
178
330
  end
179
331
  end
180
332
  end
181
333
 
182
334
  private
183
335
 
184
- def unset?(obj)
185
- defined?(obj).nil? or obj.nil?
336
+ def parse_group_heading(group_heading)
337
+ group_elts = group_heading.to_s.split(/(?<!\\):/)
338
+ type = group_elts.length > 1 && %w[vars children].include?(group_elts.last) ? group_elts.pop.chomp.strip : nil
339
+ group = group_elts.join(':').chomp.strip
340
+ [group, type]
341
+ end
342
+
343
+ def add_complex_group(group, group_spec = {})
344
+ group_spec = Util::HashWithIndifferentAccess.new(group_spec)
345
+ vars_for(group, group_spec['vars']) if group_spec.key? 'vars'
346
+ children_of(group, *(group_spec['children'])) if group_spec.key? 'children'
347
+ add_group(group, *(group_spec['hosts'])) if group_spec.key? 'hosts'
186
348
  end
187
349
  end
188
350
  end
@@ -1,8 +1,17 @@
1
1
  # frozen_string_literal: true
2
+
3
+ # Namespace for Vagrant plugins
2
4
  module VagrantPlugins
5
+ # Namespace for the +ansible_auto+ provisioner and +ansible+ command
3
6
  module AnsibleAuto
7
+ # Vagrant plugin class for the +ansible_auto+ provisioner and +ansible+
8
+ # command
4
9
  class Plugin < Vagrant.plugin(2)
5
- name 'ansible inventory'
10
+ name 'ansible auto'
11
+ description <<-DESC
12
+ Automatically generate Ansible inventories for use when running Ansible
13
+ on guest machines
14
+ DESC
6
15
 
7
16
  config 'ansible' do
8
17
  require_relative 'config'
@@ -35,7 +44,7 @@ module VagrantPlugins
35
44
  Provisioner
36
45
  end
37
46
 
38
- guest_capability 'linux', :check_open_port do
47
+ guest_capability 'linux', :port_open? do
39
48
  require_relative 'cap/guest/posix/check_open_port'
40
49
  Cap::Guest::POSIX::CheckOpenPort
41
50
  end
@@ -55,11 +64,6 @@ module VagrantPlugins
55
64
  Cap::Guest::POSIX::ExecutableInstalled
56
65
  end
57
66
 
58
- guest_capability 'linux', :bash_installed? do
59
- require_relative 'cap/guest/posix/bash_installed'
60
- Cap::Guest::POSIX::BashInstalled
61
- end
62
-
63
67
  guest_capability 'linux', :generate_private_key do
64
68
  require_relative 'cap/guest/posix/private_key'
65
69
  Cap::Guest::POSIX::PrivateKey
@@ -67,7 +71,18 @@ module VagrantPlugins
67
71
 
68
72
  guest_capability 'linux', :fetch_public_key do
69
73
  require_relative 'cap/guest/posix/public_key'
70
- Cap::Guest::POSIX::PrivateKey
74
+ Cap::Guest::POSIX::PublicKey
75
+ end
76
+
77
+ guest_capability 'linux', :authorized_key? do
78
+ require_relative 'cap/guest/posix/public_key'
79
+ Cap::Guest::POSIX::PublicKey
80
+ end
81
+
82
+ action_hook 'environment_plugins_loaded' do
83
+ require 'i18n'
84
+ I18n.load_path << VagrantPlugins::AnsibleAuto.source_root.join('locales/en.yml')
85
+ I18n.reload!
71
86
  end
72
87
  end
73
88
  end