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.
- 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
|