vagrant-ansible_auto 0.1.5 → 0.2.1

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