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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +172 -0
- data/README.md +53 -12
- data/Rakefile +9 -7
- data/TODO.md +14 -0
- data/Vagrantfile +37 -15
- data/lib/vagrant/ansible_auto/cap/guest/posix/check_open_port.rb +22 -3
- data/lib/vagrant/ansible_auto/cap/guest/posix/executable_installed.rb +10 -2
- data/lib/vagrant/ansible_auto/cap/guest/posix/gateway_addresses.rb +8 -23
- data/lib/vagrant/ansible_auto/cap/guest/posix/private_key.rb +16 -1
- data/lib/vagrant/ansible_auto/cap/guest/posix/public_key.rb +18 -3
- data/lib/vagrant/ansible_auto/cap/guest/posix/ssh_server_address.rb +22 -12
- data/lib/vagrant/ansible_auto/cap/guest/posix.rb +16 -0
- data/lib/vagrant/ansible_auto/command/inventory.rb +37 -11
- data/lib/vagrant/ansible_auto/command/root.rb +34 -31
- data/lib/vagrant/ansible_auto/config.rb +74 -33
- data/lib/vagrant/ansible_auto/errors.rb +30 -1
- data/lib/vagrant/ansible_auto/host.rb +123 -34
- data/lib/vagrant/ansible_auto/inventory.rb +196 -34
- data/lib/vagrant/ansible_auto/plugin.rb +23 -8
- data/lib/vagrant/ansible_auto/provisioner.rb +121 -79
- data/lib/vagrant/ansible_auto/util/config.rb +61 -0
- data/lib/vagrant/ansible_auto/util/hash_with_indifferent_access.rb +58 -0
- data/lib/vagrant/ansible_auto/util/keys.rb +49 -0
- data/lib/vagrant/ansible_auto/util/shell_quote.rb +24 -0
- data/lib/vagrant/ansible_auto/version.rb +2 -1
- data/lib/vagrant/ansible_auto.rb +15 -0
- data/locales/en.yml +34 -0
- data/spec/spec_helper.rb +5 -85
- data/spec/support/context.rb +111 -0
- data/spec/support/matchers.rb +45 -0
- data/spec/unit/vagrant/ansible_auto/config_spec.rb +72 -0
- data/spec/unit/vagrant/ansible_auto/host_spec.rb +131 -0
- data/spec/unit/vagrant/ansible_auto/inventory_spec.rb +349 -0
- data/spec/unit/vagrant/ansible_auto/provisioner_spec.rb +248 -0
- data/spec/unit/vagrant/ansible_auto/util/config_spec.rb +63 -0
- data/spec/unit/vagrant/ansible_auto/util/keys_spec.rb +66 -0
- data/vagrant-ansible_auto.gemspec +6 -4
- data/vagrant-spec.config.rb +3 -0
- data/yard/extensions.rb +45 -0
- metadata +36 -11
- data/Vagrantfile2 +0 -4
- data/Vagrantfile3 +0 -8
- data/Vagrantfile4 +0 -31
- data/lib/vagrant/ansible_auto/cap/guest/posix/bash_installed.rb +0 -30
- data/lib/vagrant/ansible_auto/util.rb +0 -24
- data/spec/vagrant/ansible_auto/host_spec.rb +0 -43
- data/spec/vagrant/ansible_auto/inventory_spec.rb +0 -79
@@ -1,83 +1,63 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'vagrant'
|
3
4
|
require Vagrant.source_root + 'plugins/provisioners/ansible/provisioner/guest'
|
4
5
|
require 'vagrant/util/keypair'
|
5
6
|
|
6
|
-
require 'vagrant/ansible_auto/util'
|
7
|
+
require 'vagrant/ansible_auto/util/keys'
|
7
8
|
|
9
|
+
require 'i18n'
|
8
10
|
require 'log4r'
|
9
|
-
require 'shellwords'
|
10
11
|
|
11
12
|
module VagrantPlugins
|
12
13
|
module AnsibleAuto
|
14
|
+
# Class backing the +ansible_auto+ provisioner
|
13
15
|
class Provisioner < VagrantPlugins::Ansible::Provisioner::Guest
|
14
16
|
include Vagrant::Util::Retryable
|
15
|
-
include
|
17
|
+
include Util::Keys
|
16
18
|
|
17
19
|
def initialize(machine, config)
|
18
20
|
super
|
19
21
|
@logger = Log4r::Logger.new('vagrant::provisioners::ansible_auto')
|
20
22
|
end
|
21
23
|
|
24
|
+
# Configure the provisioner by storing the root Ansible configuration
|
25
|
+
# object for use as the set of base options for a particular machine
|
26
|
+
# @api private
|
27
|
+
# @return [void]
|
22
28
|
def configure(root_config)
|
23
29
|
super
|
24
30
|
@__ansible_config = root_config.ansible
|
25
31
|
end
|
26
32
|
|
33
|
+
# Provision a {Vagrant::Machine} with the +ansible_auto+ provisioner
|
34
|
+
# @api private
|
35
|
+
# @return [void]
|
27
36
|
def provision
|
28
37
|
@config = @__ansible_config.merge(config)
|
29
38
|
|
30
|
-
|
31
|
-
|
32
|
-
# TODO: figure out how to access ansible_auto configuration done
|
33
|
-
# on the `other' machine.
|
34
|
-
# @config = other.config.ansible.merge(merged_config)
|
35
|
-
if machine.guest.capability?(:ssh_server_address)
|
39
|
+
return super unless machine.guest.capability?(:ssh_server_address)
|
36
40
|
|
37
|
-
|
38
|
-
|
41
|
+
with_active_machines do |other, name, provider|
|
42
|
+
if other.nil?
|
43
|
+
@logger.warn I18n.t('vagrant.ansible_auto.provisioner.machine_not_configured', machine_name: name)
|
44
|
+
else
|
39
45
|
if name == machine.name
|
40
|
-
other_hostvars = {
|
41
|
-
elsif other.nil?
|
42
|
-
machine.ui.warn "Machine #{name} is not configured for this environment; omitting it from the inventory"
|
46
|
+
other_hostvars = { ansible_connection: 'local' }
|
43
47
|
else
|
44
|
-
#
|
45
|
-
#
|
46
|
-
|
47
|
-
private_key_paths = []
|
48
|
-
retryable(on: Vagrant::Errors::SSHNotReady, tries: @config.host_connect_tries, sleep: @config.host_connect_sleep) do
|
49
|
-
raise Vagrant::Errors::SSHNotReady unless other.communicate.ready? and !other.ssh_info.nil?
|
50
|
-
|
51
|
-
private_key_paths = other.ssh_info.fetch(:private_key_path, [])
|
52
|
-
raise Vagrant::Errors::SSHNotReady if private_key_paths.empty?
|
53
|
-
|
54
|
-
if other.config.ssh.insert_key
|
55
|
-
raise Vagrant::Errors::SSHNotReady unless private_key_paths.any? { |k| !insecure_key?(k) }
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
ssh_host, ssh_port = machine.guest.capability(:ssh_server_address, other)
|
60
|
-
other_hostvars = { ssh_host: ssh_host, ssh_port: ssh_port }
|
48
|
+
# Merge the other machine's configuration, with this machine's
|
49
|
+
# configuration taking precedence.
|
50
|
+
@config = other.config.ansible.merge(@config)
|
61
51
|
|
62
|
-
|
63
|
-
machine.ui.warn "No private keys available for machine #{name}; provisioner will likely fail"
|
64
|
-
end
|
65
|
-
|
66
|
-
source_priv_key_path = fetch_private_key(other)
|
52
|
+
wait_for_ssh_ready!(other, @config.host_connect_tries, @config.host_connect_sleep)
|
67
53
|
|
68
|
-
|
69
|
-
|
70
|
-
else
|
71
|
-
machine.ui.info "Adding #{name} to Ansible inventory"
|
54
|
+
ssh_host, ssh_port = machine.guest.capability(:ssh_server_address, other)
|
55
|
+
other_hostvars = { ansible_ssh_host: ssh_host, ansible_ssh_port: ssh_port }
|
72
56
|
|
73
|
-
|
74
|
-
other.guest.capability(:insert_public_key, openssh)
|
75
|
-
else
|
76
|
-
remote_priv_key_path = File.join(@config.tmp_path, 'ssh', name.to_s, provider.to_s, File.basename(source_priv_key_path))
|
77
|
-
create_and_chown_and_chmod_remote_file(source_priv_key_path, remote_priv_key_path)
|
78
|
-
end
|
57
|
+
machine.ui.info I18n.t('vagrant.ansible_auto.provisioner.inventory_addition', machine_name: name)
|
79
58
|
|
80
|
-
|
59
|
+
unless (other_ssh_private_key_file = configure_ssh_access!(other, name, provider)).nil?
|
60
|
+
other_hostvars[:ansible_ssh_private_key_file] = other_ssh_private_key_file
|
81
61
|
end
|
82
62
|
end
|
83
63
|
|
@@ -85,75 +65,128 @@ module VagrantPlugins
|
|
85
65
|
end
|
86
66
|
end
|
87
67
|
|
68
|
+
@config.inventory.validate!
|
69
|
+
|
88
70
|
super
|
89
71
|
end
|
90
72
|
|
91
73
|
private
|
92
74
|
|
93
|
-
def
|
94
|
-
|
95
|
-
|
96
|
-
|
75
|
+
def control_machine_ssh_key_dir
|
76
|
+
Pathname.new(@config.tmp_path).join('ssh')
|
77
|
+
end
|
78
|
+
|
79
|
+
def control_machine_private_key_path
|
80
|
+
@control_machine_private_key_path ||= control_machine_ssh_key_dir.join('vagrant_ansible_id_rsa')
|
81
|
+
end
|
82
|
+
|
83
|
+
def control_machine_public_key_path
|
84
|
+
@control_machine_public_key_path ||= control_machine_private_key_path.sub_ext('.pub')
|
85
|
+
end
|
97
86
|
|
98
|
-
|
99
|
-
|
87
|
+
def control_machine_public_key
|
88
|
+
@control_machine_public_key ||= configure_keypair!
|
89
|
+
end
|
90
|
+
|
91
|
+
def configure_ssh_access!(other, name, provider)
|
92
|
+
# Insert the control machine's public key on the target machines
|
93
|
+
if config.insert_control_machine_public_key
|
94
|
+
insert_control_machine_public_key!(other, name)
|
95
|
+
elsif config.upload_inventory_host_private_keys
|
96
|
+
upload_inventory_host_private_key!(other, name, provider)
|
97
|
+
else
|
98
|
+
machine.ui.warn I18n.t('vagrant.ansible_auto.provisioner.cannot_configure_keys', machine_name: name)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def insert_control_machine_public_key!(other, name)
|
103
|
+
# Don't bother inserting the key if it is already authorized
|
104
|
+
if other.guest.capability?(:authorized_key?) && other.guest.capability(:authorized_key?, control_machine_public_key)
|
105
|
+
@logger.info I18n.t('vagrant.ansible_auto.provisioner.public_key_authorized', machine_name: name)
|
106
|
+
elsif other.guest.capability?(:insert_public_key)
|
107
|
+
@logger.info I18n.t('vagrant.ansible_auto.provisioner.inserting_public_key', machine_name: name)
|
108
|
+
other.guest.capability(:insert_public_key, control_machine_public_key)
|
109
|
+
else
|
110
|
+
@logger.warn I18n.t('vagrant.ansible_auto.provisioner.cannot_insert_public_key', machine_name: name)
|
111
|
+
return
|
112
|
+
end
|
113
|
+
|
114
|
+
control_machine_private_key_path
|
115
|
+
end
|
116
|
+
|
117
|
+
def upload_inventory_host_private_key!(other, name, provider)
|
118
|
+
source_priv_key_path = fetch_private_key(other)
|
119
|
+
|
120
|
+
if source_priv_key_path.nil?
|
121
|
+
@logger.warn I18n.t('vagrant.ansible_auto.provisioner.private_key_missing', machine_name: name)
|
122
|
+
return
|
123
|
+
end
|
124
|
+
|
125
|
+
@logger.info I18n.t('vagrant.ansible_auto.provisioner.uploading_private_key', machine_name: name)
|
126
|
+
|
127
|
+
other_priv_key_path = control_machine_ssh_key_dir.join(name.to_s, provider.to_s, File.basename(source_priv_key_path))
|
128
|
+
create_and_chown_and_chmod_remote_file(source_priv_key_path, other_priv_key_path)
|
129
|
+
other_priv_key_path
|
130
|
+
end
|
131
|
+
|
132
|
+
def configure_keypair!
|
133
|
+
# Check whether user already has a private key
|
134
|
+
if !machine.communicate.test("test -r #{control_machine_private_key_path} && test -r #{control_machine_public_key_path}")
|
100
135
|
_pub, priv, openssh = Vagrant::Util::Keypair.create
|
101
|
-
write_and_chown_and_chmod_remote_file(priv,
|
102
|
-
write_and_chown_and_chmod_remote_file(openssh,
|
136
|
+
write_and_chown_and_chmod_remote_file(priv, control_machine_private_key_path)
|
137
|
+
write_and_chown_and_chmod_remote_file(openssh, control_machine_public_key_path)
|
103
138
|
elsif machine.guest.capability?(:fetch_public_key)
|
104
|
-
openssh = machine.guest.capability(:fetch_public_key,
|
139
|
+
openssh = machine.guest.capability(:fetch_public_key, control_machine_private_key_path)
|
105
140
|
end
|
106
141
|
|
107
|
-
|
142
|
+
openssh
|
108
143
|
end
|
109
144
|
|
110
|
-
def handle_remote_file(to,
|
145
|
+
def handle_remote_file(to, opts = {})
|
146
|
+
owner = opts.fetch(:owner) { machine.ssh_info[:username] }
|
147
|
+
mode = opts.fetch(:mode) { 0o600 }
|
111
148
|
machine.communicate.tap do |comm|
|
149
|
+
# NOTE create_and_chown_remote_folder is defined in the parent class
|
112
150
|
create_and_chown_remote_folder(File.dirname(to))
|
113
151
|
|
114
152
|
yield comm, to
|
115
153
|
|
116
|
-
comm.sudo("chown -h #{
|
154
|
+
comm.sudo("chown -h #{owner} #{to}")
|
117
155
|
comm.sudo("chmod #{format('0%o', mode)} #{to}")
|
118
156
|
end
|
119
157
|
end
|
120
158
|
|
121
|
-
def create_and_chown_and_chmod_remote_file(from, to,
|
122
|
-
handle_remote_file(to,
|
159
|
+
def create_and_chown_and_chmod_remote_file(from, to, opts = {})
|
160
|
+
handle_remote_file(to, opts) do |comm, target|
|
123
161
|
comm.upload(from, target)
|
124
162
|
end
|
125
163
|
end
|
126
164
|
|
127
|
-
def write_and_chown_and_chmod_remote_file(contents, to,
|
128
|
-
handle_remote_file(to,
|
165
|
+
def write_and_chown_and_chmod_remote_file(contents, to, opts = {})
|
166
|
+
handle_remote_file(to, opts) do |comm, target|
|
129
167
|
contents = contents.strip << "\n"
|
130
168
|
|
131
|
-
to_parent = File.dirname(to)
|
132
|
-
|
133
169
|
remote_path = "/tmp/vagrant-temp-asset-#{Time.now.to_i}"
|
134
|
-
Tempfile.open(
|
170
|
+
Tempfile.open('vagrant-temp-asset') do |f|
|
135
171
|
f.binmode
|
136
172
|
f.write(contents)
|
137
173
|
f.fsync
|
138
174
|
f.close
|
139
|
-
machine.ui.
|
175
|
+
machine.ui.detail I18n.t('vagrant.ansible_auto.provisioner.uploading_file', local_path: f.path, remote_path: remote_path)
|
140
176
|
comm.upload(f.path, remote_path)
|
141
177
|
end
|
142
178
|
|
143
|
-
#
|
144
|
-
comm.execute
|
145
|
-
mkdir #{to_parent}
|
146
|
-
mv #{remote_path} #{to}
|
147
|
-
EOH
|
179
|
+
# Parent directory of target will already exist
|
180
|
+
comm.execute("mv #{remote_path} #{target}")
|
148
181
|
end
|
149
182
|
end
|
150
183
|
|
151
184
|
def generate_inventory_machines
|
152
|
-
if config.strict_host_key_checking
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
185
|
+
machine_format = if config.strict_host_key_checking
|
186
|
+
'%s'
|
187
|
+
else
|
188
|
+
"%s ansible_ssh_extra_args='-o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o StrictHostKeyChecking=no'"
|
189
|
+
end
|
157
190
|
|
158
191
|
config.inventory.with_ini_lines_hosts.map do |host_spec|
|
159
192
|
machine_format % host_spec
|
@@ -171,11 +204,20 @@ module VagrantPlugins
|
|
171
204
|
begin
|
172
205
|
yield machine.env.machine(name, provider), name, provider
|
173
206
|
rescue Vagrant::Errors::MachineNotFound, CommunicatorWinRM::Errors::WinRMNotReady => e
|
174
|
-
|
175
|
-
|
207
|
+
@logger.warn I18n.t('vagrant.ansible_auto.provisioner.cannot_find_machine', machine_name: name, error_message: e.message)
|
208
|
+
yield nil, name, provider
|
176
209
|
end
|
177
210
|
end
|
178
211
|
end
|
212
|
+
|
213
|
+
# The machines we are trying to manage might not yet be ready to
|
214
|
+
# connect -- retry a configurable number of times/durations until
|
215
|
+
# we can connect; otherwise raise an exception.
|
216
|
+
def wait_for_ssh_ready!(other, tries, sleep)
|
217
|
+
retryable(on: Vagrant::Errors::SSHNotReady, tries: tries, sleep: sleep) do
|
218
|
+
raise Vagrant::Errors::SSHNotReady unless other.communicate.ready? && !other.ssh_info.nil?
|
219
|
+
end
|
220
|
+
end
|
179
221
|
end
|
180
222
|
end
|
181
223
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'vagrant/util/deep_merge'
|
4
|
+
|
5
|
+
module VagrantPlugins
|
6
|
+
module AnsibleAuto
|
7
|
+
# Helper methods for common operations
|
8
|
+
module Util
|
9
|
+
# Helper module for operations related to configuration settings
|
10
|
+
module Config
|
11
|
+
# @return [Boolean] +false+ when an object is +nil+, undefined, or the
|
12
|
+
# special +UNSET_VALUE+ object, +true+ otherwise
|
13
|
+
def unset?(o)
|
14
|
+
o.nil? || !defined?(o) || (self.class.const_defined?(:UNSET_VALUE) && o == self.class.const_get(:UNSET_VALUE))
|
15
|
+
end
|
16
|
+
|
17
|
+
def bool?(o)
|
18
|
+
(o == true) || (o == false)
|
19
|
+
end
|
20
|
+
|
21
|
+
def num?(o)
|
22
|
+
float?(o) || int?(o)
|
23
|
+
end
|
24
|
+
|
25
|
+
def int?(o)
|
26
|
+
coerced_int = int!(o)
|
27
|
+
return false if coerced_int.nil?
|
28
|
+
float!(coerced_int) == float!(o)
|
29
|
+
end
|
30
|
+
|
31
|
+
def int!(o)
|
32
|
+
Integer(o)
|
33
|
+
rescue ArgumentError, TypeError
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def float?(o)
|
38
|
+
!float!(o).nil?
|
39
|
+
end
|
40
|
+
|
41
|
+
def float!(o)
|
42
|
+
Float(o)
|
43
|
+
rescue ArgumentError, TypeError
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
# Deep merge two objects, ensuring first that neither object is
|
48
|
+
# {#unset?}
|
49
|
+
def conditional_merge(a, b, &block)
|
50
|
+
if unset? b
|
51
|
+
a
|
52
|
+
elsif unset? a
|
53
|
+
b
|
54
|
+
else
|
55
|
+
Vagrant::Util::DeepMerge.deep_merge(a, b, &block)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'vagrant/util/hash_with_indifferent_access'
|
4
|
+
|
5
|
+
module VagrantPlugins
|
6
|
+
module AnsibleAuto
|
7
|
+
# Helper methods for common operations
|
8
|
+
module Util
|
9
|
+
# Helper methods for operations related to SSH keys
|
10
|
+
class HashWithIndifferentAccess < Vagrant::Util::HashWithIndifferentAccess
|
11
|
+
# Combine two hashes into a new hash
|
12
|
+
# @overload merge(other)
|
13
|
+
# @param (see #merge!)
|
14
|
+
# @return (see #merge!)
|
15
|
+
# @overload merge(other, &block)
|
16
|
+
# @param (see #merge!)
|
17
|
+
# @return (see #merge!)
|
18
|
+
# @yieldparam (see #merge!)
|
19
|
+
# @yieldparam (see #merge!)
|
20
|
+
# @yieldparam (see #merge!)
|
21
|
+
def merge(other, &block)
|
22
|
+
dup.merge!(other, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
# In-place merge of two {HashWithIndifferentAccess} object. Provide
|
26
|
+
# the optional +&block+ argument if you want to control what happens a
|
27
|
+
# key exists in both hashes; by default, the value from +other+
|
28
|
+
# overwrites the current value for that key.
|
29
|
+
# @overload merge!(other)
|
30
|
+
# @param [Hash] the hash to merge into the current hash
|
31
|
+
# @return [HashWithIndifferentAccess] the merged hash
|
32
|
+
# @overload merge!(other, &block)
|
33
|
+
# @param [Hash] the hash to merge into the current hash
|
34
|
+
# @return [HashWithIndifferentAccess] the merged hash
|
35
|
+
# @yieldparam [String] key
|
36
|
+
# @yieldparam [HashWithIndifferentAccess] current
|
37
|
+
# @yieldparam [Hash] other
|
38
|
+
def merge!(other)
|
39
|
+
if block_given?
|
40
|
+
other.each do |key, value|
|
41
|
+
convert_key(key).tap do |ckey|
|
42
|
+
self[ckey] = if key? ckey
|
43
|
+
yield(ckey, self[ckey], value)
|
44
|
+
else
|
45
|
+
value
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
else
|
50
|
+
super(other)
|
51
|
+
end
|
52
|
+
|
53
|
+
self
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module AnsibleAuto
|
5
|
+
# Helper methods for common operations
|
6
|
+
module Util
|
7
|
+
# Helper methods for operations related to SSH keys
|
8
|
+
module Keys
|
9
|
+
# @return [Pathname] the path to the Vagrant insecure private key
|
10
|
+
def insecure_key_path
|
11
|
+
Vagrant.source_root.join('keys/vagrant')
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [String] the contents of the Vagrant insecure private key
|
15
|
+
def insecure_key_contents
|
16
|
+
@__insecure_key_contents ||= insecure_key_path.read.chomp
|
17
|
+
end
|
18
|
+
|
19
|
+
# @param [String] path path to a file
|
20
|
+
# @return [Boolean] whether the file is the Vagrant insecure private key
|
21
|
+
# @note Adapted from
|
22
|
+
# {VagrantPlugins::CommunicatorSSH::Communicator#insecure_key?}
|
23
|
+
def insecure_key?(path)
|
24
|
+
return false if path.nil? || !File.file?(path)
|
25
|
+
File.read(path).chomp == insecure_key_contents
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param [Vagrant::Machine] machine a guest machine
|
29
|
+
# @return [Array<String>] the first of the machine's keys that aren't the
|
30
|
+
# Vagrant private key as determined by {#insecure_key?}
|
31
|
+
def fetch_private_key(machine)
|
32
|
+
fetch_private_keys(machine).first
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param (see #fetch_private_key)
|
36
|
+
# @return [Array<String>] a list of the paths to all of the machine's
|
37
|
+
# keys that aren't the Vagrant private key as determined by
|
38
|
+
# {#insecure_key?}
|
39
|
+
def fetch_private_keys(machine)
|
40
|
+
if machine.config.ssh.insert_key && !machine.ssh_info.nil?
|
41
|
+
machine.ssh_info.fetch(:private_key_path, []).find_all { |k| !insecure_key?(k) }
|
42
|
+
else
|
43
|
+
[machine.env.default_private_key_path]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module AnsibleAuto
|
3
|
+
# Helper methods for common operations
|
4
|
+
module Util
|
5
|
+
# Helper methods for operations related to SSH keys
|
6
|
+
module ShellQuote
|
7
|
+
# In-place equivalent of {#shellescape}
|
8
|
+
# @param text [#to_s] the text to escape
|
9
|
+
# @return [String] the text, with all occurrences of +'+ replaced by
|
10
|
+
# +'"'"'+
|
11
|
+
def shellescape!(text)
|
12
|
+
text.to_s.gsub!(%('), %('"'"'))
|
13
|
+
end
|
14
|
+
|
15
|
+
# Escape a string for use as a shell parameter
|
16
|
+
# @param (see #shellescape!)
|
17
|
+
# @return (see #shellescape!)
|
18
|
+
def shellescape(text)
|
19
|
+
text.to_s.gsub(%('), %('"'"'))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/vagrant/ansible_auto.rb
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
|
2
5
|
require 'vagrant'
|
3
6
|
|
4
7
|
require 'vagrant/ansible_auto/plugin'
|
5
8
|
require 'vagrant/ansible_auto/version'
|
9
|
+
|
10
|
+
module VagrantPlugins
|
11
|
+
# Ansible command and provisioner plugin with automatic inventory generation
|
12
|
+
module AnsibleAuto
|
13
|
+
class << self
|
14
|
+
# @return [Pathname] path to the gem source root directory
|
15
|
+
def source_root
|
16
|
+
Pathname.new('../../..').expand_path(__FILE__)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/locales/en.yml
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
en:
|
2
|
+
vagrant:
|
3
|
+
ansible_auto:
|
4
|
+
provisioner:
|
5
|
+
machine_not_configured: 'Machine %{machine_name} is not configured for this environment; omitting it from the inventory'
|
6
|
+
private_key_missing: 'Private key for %{machine_name} not available for upload; provisioner will likely fail'
|
7
|
+
inventory_addition: 'Adding %{machine_name} to Ansible inventory'
|
8
|
+
public_key_authorized: 'Control machine public key already authorized on %{machine_name}'
|
9
|
+
inserting_public_key: 'Inserting control machine public key on %{machine_name}'
|
10
|
+
cannot_insert_public_key: 'Cannot insert control machine public key on %{machine_name}'
|
11
|
+
uploading_private_key: 'Uploading private key for %{machine_name} onto control machine'
|
12
|
+
cannot_configure_keys: 'unable to insert public key or upload existing private key to control machine for %{machine_name}'
|
13
|
+
uploading_file: 'Uploading %{local_path} to %{remote_path}'
|
14
|
+
cannot_find_machine: 'Unable to find machine %{machine_name}: %{error_message}'
|
15
|
+
command:
|
16
|
+
root:
|
17
|
+
synopsis: 'build an Ansible inventory'
|
18
|
+
option: {}
|
19
|
+
diag: {}
|
20
|
+
skel:
|
21
|
+
synopsis: 'output a dynamic Ansible inventory'
|
22
|
+
usage: 'Usage: vagrant ansible inventory [options]'
|
23
|
+
option:
|
24
|
+
list: 'List all hosts as JSON'
|
25
|
+
diag:
|
26
|
+
not_running: 'machine %{machine} is not running; falling back to default hostvar values'
|
27
|
+
errors:
|
28
|
+
inventory:
|
29
|
+
missing_group: 'expected group %{group} is missing from the inventory'
|
30
|
+
group_missing_child: 'group %{group} defines nonextant child group %{child}'
|
31
|
+
invalid_host_type: "cannot add object of type %{type} as inventory host"
|
32
|
+
invalid_group_name: "%{group} is not a valid group name"
|
33
|
+
command:
|
34
|
+
unrecognized_command: 'unrecognized_command: %{command}'
|
data/spec/spec_helper.rb
CHANGED
@@ -1,93 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
unless RUBY_ENGINE == 'rbx' # coverage support is broken on rbx
|
3
|
-
require 'simplecov'
|
4
|
-
require 'coveralls'
|
5
|
-
|
6
|
-
formatters = [SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter]
|
7
|
-
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(formatters)
|
8
|
-
SimpleCov.start do
|
9
|
-
minimum_coverage 100
|
10
|
-
add_group 'Sources', 'lib'
|
11
|
-
add_group 'Tests', 'spec'
|
12
|
-
end
|
13
|
-
end
|
14
2
|
|
15
3
|
require 'pathname'
|
16
4
|
|
17
|
-
|
18
|
-
|
19
|
-
require 'vagrant/ansible_auto'
|
20
|
-
|
21
|
-
# We're using an older version of rspec-expectations that doesn't have the
|
22
|
-
# `all' matcher.
|
23
|
-
RSpec::Matchers.define :all do |expected|
|
24
|
-
define_method :index_failed_objects do |actual|
|
25
|
-
end
|
26
|
-
|
27
|
-
def index_failed_objects(actual)
|
28
|
-
return enum_for(__method__, actual) unless block_given?
|
29
|
-
|
30
|
-
@failed_items = []
|
31
|
-
|
32
|
-
actual.each_with_index do |o, i|
|
33
|
-
@failed_items[i] = yield o
|
34
|
-
end
|
35
|
-
|
36
|
-
@failed_items.compact.empty?
|
37
|
-
end
|
38
|
-
|
39
|
-
match_for_should do |actual|
|
40
|
-
index_failed_objects(actual) do |item|
|
41
|
-
expected.failure_message_for_should unless expected.matches?(item)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
match_for_should_not do |actual|
|
46
|
-
index_failed_objects(actual) do |item|
|
47
|
-
expected.failure_message_for_should_not if expected.matches?(item)
|
48
|
-
end
|
49
|
-
end
|
5
|
+
Pathname.new(__FILE__).tap do |helper|
|
6
|
+
$LOAD_PATH.unshift((helper.parent.parent + 'lib').to_s)
|
50
7
|
|
51
|
-
|
52
|
-
if actual.respond_to?(:each_with_index)
|
53
|
-
@failed_items.each_with_index.reject { |f, _i| f.nil? }.map do |f, i|
|
54
|
-
"object at index #{i} failed to match: #{f}"
|
55
|
-
end.join("\n")
|
56
|
-
else
|
57
|
-
"provided object is not iterable"
|
58
|
-
end
|
59
|
-
end
|
8
|
+
require 'vagrant/ansible_auto'
|
60
9
|
|
61
|
-
|
62
|
-
failure_message_for_should_not { |actual| failure_message(actual) }
|
10
|
+
helper.parent.join('support').find { |f| require f if f.extname == '.rb' }
|
63
11
|
end
|
64
12
|
|
65
|
-
|
66
|
-
let(:machine) { double('machine') }
|
67
|
-
let(:state) { double('state') }
|
68
|
-
let(:ssh_info) { Hash.new }
|
69
|
-
|
70
|
-
before do
|
71
|
-
machine.stub(ssh_info: ssh_info, state: state)
|
72
|
-
machine.stub(name: 'mymachine')
|
73
|
-
state.stub(id: :running)
|
74
|
-
allow(ssh_info).to receive(:[])
|
75
|
-
allow(ssh_info).to receive(:fetch).with(:private_key_path, anything).and_return([])
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
shared_context 'host' do
|
80
|
-
require 'vagrant/ansible_auto/host'
|
81
|
-
|
82
|
-
include_context 'machine'
|
83
|
-
|
84
|
-
let(:host) { VagrantPlugins::AnsibleAuto::Host.new(machine) }
|
85
|
-
end
|
86
|
-
|
87
|
-
shared_context 'inventory' do
|
88
|
-
require 'vagrant/ansible_auto/host'
|
89
|
-
|
90
|
-
include_context 'machine'
|
91
|
-
|
92
|
-
let(:inventory) { VagrantPlugins::AnsibleAuto.new }
|
93
|
-
end
|
13
|
+
SimpleCov.start
|