Merge pull request #5765 from mitchellh/gildegoma/key-in-ansible-inventory
Store the first SSH private key in generated Ansible inventory
This commit is contained in:
commit
8f951291be
|
@ -33,6 +33,7 @@ IMPROVEMENTS:
|
|||
- guests/solaris,solaris11: support inserting generated key [GH-5182]
|
||||
[GH-5290]
|
||||
- providers/docker: images are pulled prior to starting [GH-5249]
|
||||
- provisioners/ansible: store the first ssh private key in the auto-generated inventory [GH-5765]
|
||||
- provisioners/chef: add capability for checking if Chef is installed on Windows [GH-5669]
|
||||
- provisioners/puppet: add support for Puppet 4 and configuration options [GH-5601]
|
||||
- provisioners/puppet: add support for `synced_folder_args` in apply [GH-5359]
|
||||
|
|
|
@ -20,8 +20,8 @@ module VagrantPlugins
|
|||
# Ansible provisioner options
|
||||
#
|
||||
|
||||
# Connect with Vagrant SSH identity
|
||||
options = %W[--private-key=#{@ssh_info[:private_key_path][0]} --user=#{@ssh_info[:username]}]
|
||||
# By default, connect with Vagrant SSH username
|
||||
options = %W[--user=#{@ssh_info[:username]}]
|
||||
|
||||
# Connect with native OpenSSH client
|
||||
# Other modes (e.g. paramiko) are not officially supported,
|
||||
|
@ -127,7 +127,7 @@ module VagrantPlugins
|
|||
m = @machine.env.machine(*am)
|
||||
m_ssh_info = m.ssh_info
|
||||
if !m_ssh_info.nil?
|
||||
inventory += "#{m.name} ansible_ssh_host=#{m_ssh_info[:host]} ansible_ssh_port=#{m_ssh_info[:port]}\n"
|
||||
inventory += "#{m.name} ansible_ssh_host=#{m_ssh_info[:host]} ansible_ssh_port=#{m_ssh_info[:port]} ansible_ssh_private_key_file=#{m_ssh_info[:private_key_path][0]}\n"
|
||||
inventory_machines[m.name] = m
|
||||
else
|
||||
@logger.error("Auto-generated inventory: Impossible to get SSH information for machine '#{m.name} (#{m.provider_name})'. This machine should be recreated.")
|
||||
|
@ -242,8 +242,10 @@ module VagrantPlugins
|
|||
ssh_options << "-o IdentitiesOnly=yes" unless Vagrant::Util::Platform.solaris?
|
||||
|
||||
# Multiple Private Keys
|
||||
@ssh_info[:private_key_path].drop(1).each do |key|
|
||||
ssh_options << "-o IdentityFile=#{key}"
|
||||
unless !config.inventory_path && @ssh_info[:private_key_path].size == 1
|
||||
@ssh_info[:private_key_path].each do |key|
|
||||
ssh_options << "-o IdentityFile=#{key}"
|
||||
end
|
||||
end
|
||||
|
||||
# SSH Forwarding
|
||||
|
|
|
@ -67,16 +67,15 @@ VF
|
|||
#
|
||||
|
||||
def self.it_should_set_arguments_and_environment_variables(
|
||||
expected_args_count = 7, expected_vars_count = 4, expected_host_key_checking = false, expected_transport_mode = "ssh")
|
||||
expected_args_count = 6, expected_vars_count = 4, expected_host_key_checking = false, expected_transport_mode = "ssh")
|
||||
|
||||
it "sets implicit arguments in a specific order" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||
|
||||
expect(args[0]).to eq("ansible-playbook")
|
||||
expect(args[1]).to eq("--private-key=#{machine.ssh_info[:private_key_path][0]}")
|
||||
expect(args[2]).to eq("--user=#{machine.ssh_info[:username]}")
|
||||
expect(args[3]).to eq("--connection=ssh")
|
||||
expect(args[4]).to eq("--timeout=30")
|
||||
expect(args[1]).to eq("--user=#{machine.ssh_info[:username]}")
|
||||
expect(args[2]).to eq("--connection=ssh")
|
||||
expect(args[3]).to eq("--timeout=30")
|
||||
|
||||
inventory_count = args.count { |x| x =~ /^--inventory-file=.+$/ }
|
||||
expect(inventory_count).to be > 0
|
||||
|
@ -169,7 +168,7 @@ VF
|
|||
expect(config.inventory_path).to be_nil
|
||||
expect(File.exists?(generated_inventory_file)).to be_true
|
||||
inventory_content = File.read(generated_inventory_file)
|
||||
expect(inventory_content).to include("#{machine.name} ansible_ssh_host=#{machine.ssh_info[:host]} ansible_ssh_port=#{machine.ssh_info[:port]}\n")
|
||||
expect(inventory_content).to include("#{machine.name} ansible_ssh_host=#{machine.ssh_info[:host]} ansible_ssh_port=#{machine.ssh_info[:port]} ansible_ssh_private_key_file=#{machine.ssh_info[:private_key_path][0]}\n")
|
||||
expect(inventory_content).to include("# MISSING: '#{iso_env.machine_names[1]}' machine was probably removed without using Vagrant. This machine should be recreated.\n")
|
||||
}
|
||||
end
|
||||
|
@ -277,7 +276,7 @@ VF
|
|||
config.host_key_checking = true
|
||||
end
|
||||
|
||||
it_should_set_arguments_and_environment_variables 7, 4, true
|
||||
it_should_set_arguments_and_environment_variables 6, 4, true
|
||||
end
|
||||
|
||||
describe "with boolean (flag) options disabled" do
|
||||
|
@ -289,7 +288,7 @@ VF
|
|||
config.sudo_user = 'root'
|
||||
end
|
||||
|
||||
it_should_set_arguments_and_environment_variables 8
|
||||
it_should_set_arguments_and_environment_variables 7
|
||||
it_should_set_optional_arguments({ "sudo_user" => "--sudo-user=root" })
|
||||
|
||||
it "it does not set boolean flag when corresponding option is set to false" do
|
||||
|
@ -318,7 +317,7 @@ VF
|
|||
"--new-arg=yeah"]
|
||||
end
|
||||
|
||||
it_should_set_arguments_and_environment_variables 18, 4, false, "paramiko"
|
||||
it_should_set_arguments_and_environment_variables 17, 4, false, "paramiko"
|
||||
|
||||
it "sets all raw arguments" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||
|
@ -374,7 +373,7 @@ VF
|
|||
config.ask_vault_pass = true
|
||||
end
|
||||
|
||||
it_should_set_arguments_and_environment_variables 8
|
||||
it_should_set_arguments_and_environment_variables 7
|
||||
|
||||
it "should ask the vault password" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||
|
@ -388,7 +387,7 @@ VF
|
|||
config.vault_password_file = existing_file
|
||||
end
|
||||
|
||||
it_should_set_arguments_and_environment_variables 8
|
||||
it_should_set_arguments_and_environment_variables 7
|
||||
|
||||
it "uses the given vault password file" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||
|
@ -402,7 +401,7 @@ VF
|
|||
config.raw_ssh_args = ['-o ControlMaster=no', '-o ForwardAgent=no']
|
||||
end
|
||||
|
||||
it_should_set_arguments_and_environment_variables 7, 4
|
||||
it_should_set_arguments_and_environment_variables 6, 4
|
||||
it_should_explicitly_enable_ansible_ssh_control_persist_defaults
|
||||
|
||||
it "passes custom SSH options via ANSIBLE_SSH_ARGS with the highest priority" do
|
||||
|
@ -436,7 +435,7 @@ VF
|
|||
ssh_info[:private_key_path] = ['/path/to/my/key', '/an/other/identity', '/yet/an/other/key']
|
||||
end
|
||||
|
||||
it_should_set_arguments_and_environment_variables 7, 4
|
||||
it_should_set_arguments_and_environment_variables 6, 4
|
||||
it_should_explicitly_enable_ansible_ssh_control_persist_defaults
|
||||
|
||||
it "passes additional Identity Files via ANSIBLE_SSH_ARGS" do
|
||||
|
@ -453,7 +452,7 @@ VF
|
|||
ssh_info[:forward_agent] = true
|
||||
end
|
||||
|
||||
it_should_set_arguments_and_environment_variables 7, 4
|
||||
it_should_set_arguments_and_environment_variables 6, 4
|
||||
it_should_explicitly_enable_ansible_ssh_control_persist_defaults
|
||||
|
||||
it "enables SSH-Forwarding via ANSIBLE_SSH_ARGS" do
|
||||
|
@ -469,12 +468,12 @@ VF
|
|||
config.verbose = 'v'
|
||||
end
|
||||
|
||||
it_should_set_arguments_and_environment_variables 8
|
||||
it_should_set_arguments_and_environment_variables 7
|
||||
it_should_set_optional_arguments({ "verbose" => "-v" })
|
||||
|
||||
it "shows the ansible-playbook command" do
|
||||
expect(machine.env.ui).to receive(:detail).with { |full_command|
|
||||
expect(full_command).to eq("PYTHONUNBUFFERED=1 ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_FORCE_COLOR=true ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --private-key=/path/to/my/key --user=testuser --connection=ssh --timeout=30 --limit='machine1' --inventory-file=#{generated_inventory_dir} -v playbook.yml")
|
||||
expect(full_command).to eq("PYTHONUNBUFFERED=1 ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_FORCE_COLOR=true ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --user=testuser --connection=ssh --timeout=30 --limit='machine1' --inventory-file=#{generated_inventory_dir} -v playbook.yml")
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -514,7 +513,7 @@ VF
|
|||
config.skip_tags = %w(foo bar)
|
||||
config.limit = 'machine*:&vagrant:!that_one'
|
||||
config.start_at_task = 'an awesome task'
|
||||
config.raw_arguments = ["--why-not", "--su-user=foot", "--ask-su-pass", "--limit=all"]
|
||||
config.raw_arguments = ["--why-not", "--su-user=foot", "--ask-su-pass", "--limit=all", "--private-key=./myself.key"]
|
||||
|
||||
# environment variables
|
||||
config.host_key_checking = true
|
||||
|
@ -538,15 +537,17 @@ VF
|
|||
|
||||
it "also includes given raw arguments" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute).with { |*args|
|
||||
expect(args).to include("--why-not")
|
||||
expect(args).to include("--su-user=foot")
|
||||
expect(args).to include("--ask-su-pass")
|
||||
expect(args).to include("--why-not")
|
||||
expect(args).to include("--limit=all")
|
||||
expect(args).to include("--private-key=./myself.key")
|
||||
}
|
||||
end
|
||||
|
||||
it "shows the ansible-playbook command, with additional quotes when required" do
|
||||
expect(machine.env.ui).to receive(:detail).with { |full_command|
|
||||
expect(full_command).to eq("PYTHONUNBUFFERED=1 ANSIBLE_HOST_KEY_CHECKING=true ANSIBLE_FORCE_COLOR=true ANSIBLE_SSH_ARGS='-o IdentitiesOnly=yes -o IdentityFile=/my/key2 -o ForwardAgent=yes -o ControlMaster=no -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --private-key=/my/key1 --user=testuser --connection=ssh --timeout=30 --limit='machine*:&vagrant:!that_one' --inventory-file=#{generated_inventory_dir} --extra-vars=@#{File.expand_path(__FILE__)} --sudo --sudo-user=deployer -vvv --ask-sudo-pass --ask-vault-pass --vault-password-file=#{File.expand_path(__FILE__)} --tags=db,www --skip-tags=foo,bar --start-at-task='an awesome task' --why-not --su-user=foot --ask-su-pass --limit='all' playbook.yml")
|
||||
expect(full_command).to eq("PYTHONUNBUFFERED=1 ANSIBLE_HOST_KEY_CHECKING=true ANSIBLE_FORCE_COLOR=true ANSIBLE_SSH_ARGS='-o IdentitiesOnly=yes -o IdentityFile=/my/key1 -o IdentityFile=/my/key2 -o ForwardAgent=yes -o ControlMaster=no -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --user=testuser --connection=ssh --timeout=30 --limit='machine*:&vagrant:!that_one' --inventory-file=#{generated_inventory_dir} --extra-vars=@#{File.expand_path(__FILE__)} --sudo --sudo-user=deployer -vvv --ask-sudo-pass --ask-vault-pass --vault-password-file=#{File.expand_path(__FILE__)} --tags=db,www --skip-tags=foo,bar --start-at-task='an awesome task' --why-not --su-user=foot --ask-su-pass --limit='all' --private-key=./myself.key playbook.yml")
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -61,8 +61,8 @@ Vagrant would generate an inventory file that might look like:
|
|||
```
|
||||
# Generated by Vagrant
|
||||
|
||||
machine1 ansible_ssh_host=127.0.0.1 ansible_ssh_port=2200
|
||||
machine2 ansible_ssh_host=127.0.0.1 ansible_ssh_port=2201
|
||||
machine1 ansible_ssh_host=127.0.0.1 ansible_ssh_port=2200 ansible_ssh_private_key_file=/home/.../.vagrant/machines/machine1/virtualbox/private_key
|
||||
machine2 ansible_ssh_host=127.0.0.1 ansible_ssh_port=2201 ansible_ssh_private_key_file=/home/.../.vagrant/machines/machine2/virtualbox/private_key
|
||||
|
||||
[group1]
|
||||
machine1
|
||||
|
@ -79,6 +79,7 @@ group2
|
|||
|
||||
* The generation of group variables blocks (e.g. `[group1:vars]`) are intentionally not supported, as it is [not recommended to store group variables in the main inventory file](http://docs.ansible.com/intro_inventory.html#splitting-out-host-and-group-specific-data). A good practice is to store these group (or host) variables in `YAML` files stored in `group_vars/` or `host_vars/` directories in the playbook (or inventory) directory.
|
||||
* Unmanaged machines and undefined groups are not added to the inventory, to avoid useless Ansible errors (e.g. *unreachable host* or *undefined child group*)
|
||||
* Prior to Vagrant 1.7.3, the `ansible_ssh_private_key_file` variable was not set in generated inventory, but passed as command line argument to `ansible-playbook` command.
|
||||
|
||||
For example, `machine3`, `group3` and `group1:vars` in the example below would not be added to the generated inventory file:
|
||||
|
||||
|
@ -227,30 +228,47 @@ by the sudo command.
|
|||
Vagrant is designed to provision [multi-machine environments](/v2/multi-machine) in sequence, but the following configuration pattern can be used to take advantage of Ansible parallelism:
|
||||
|
||||
```
|
||||
# By default, Vagrant 1.7+ automatically inserts a different
|
||||
# insecure keypair for each new VM created. The easiest way
|
||||
# to use the same keypair for all the machines is to disable
|
||||
# this feature and rely on the legacy insecure key.
|
||||
config.ssh.insert_key = false
|
||||
# Vagrant 1.7+ automatically inserts a different
|
||||
# insecure keypair for each new VM created. The easiest way
|
||||
# to use the same keypair for all the machines is to disable
|
||||
# this feature and rely on the legacy insecure key.
|
||||
# config.ssh.insert_key = false
|
||||
#
|
||||
# Note:
|
||||
# As of Vagrant 1.7.3, it is no longer necessary to disable
|
||||
# the keypair creation when using the auto-generated inventory.
|
||||
|
||||
config.vm.define 'machine2' do |machine|
|
||||
machine.vm.hostname = 'machine2'
|
||||
machine.vm.network "private_network", ip: "192.168.77.22"
|
||||
end
|
||||
N = 3
|
||||
(1..N).each do |machine_id|
|
||||
config.vm.define "machine#{machine_id}" do |machine|
|
||||
|
||||
config.vm.define 'machine1' do |machine|
|
||||
machine.vm.hostname = 'machine1'
|
||||
machine.vm.network "private_network", ip: "192.168.77.21"
|
||||
machine.vm.hostname = "machine#{machine_id}"
|
||||
machine.vm.network "private_network", ip: "192.168.77.#{20+machine_id}"
|
||||
|
||||
machine.vm.provision :ansible do |ansible|
|
||||
ansible.playbook = "playbook.yml"
|
||||
if machine_id == N
|
||||
|
||||
# Disable default limit (required with Vagrant 1.5+)
|
||||
ansible.limit = 'all'
|
||||
# TODO add a comment
|
||||
machine.vm.provision :ansible do |ansible|
|
||||
ansible.playbook = "playbook.yml"
|
||||
|
||||
# Disable default limit (required with Vagrant 1.5+)
|
||||
ansible.limit = 'all'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
**Caveats:**
|
||||
|
||||
- Vagrant will only
|
||||
|
||||
|
||||
TODO: add a note about mixing parallel and custom inventory
|
||||
TODO: multiple keys are not supported with parallelism, except if you pass them as raw_ssh_args!
|
||||
|
||||
|
||||
### Provide a local `ansible.cfg` file
|
||||
|
||||
Certain settings in Ansible are (only) adjustable via a [configuration file](http://docs.ansible.com/intro_configuration.html), and you might want to ship such a file in your Vagrant project.
|
||||
|
@ -283,3 +301,26 @@ In a situation like the above, to override the `remote_user` specified in a play
|
|||
```
|
||||
ansible.extra_vars = { ansible_ssh_user: 'vagrant' }
|
||||
```
|
||||
|
||||
### Force Paramiko Connection Mode
|
||||
|
||||
The Ansible provisioner is implemented with native OpenSSH support in mind, and there is no official support for [paramiko](https://github.com/paramiko/paramiko/) (A native Python SSHv2 protocol library).
|
||||
|
||||
If you really need to use this connection mode, it is though possible to enable paramiko as illustrated in the following configuration examples:
|
||||
|
||||
|
||||
With auto-generated inventory:
|
||||
|
||||
```
|
||||
ansible.raw_arguments = ["--connection=paramiko"]
|
||||
```
|
||||
|
||||
With a custom inventory, the private key must be specified (e.g. via an `ansible.cfg` configuration file, `--private-key` argument, or as part of your inventory file):
|
||||
|
||||
```
|
||||
ansible.inventory_path = "./my-inventory"
|
||||
ansible.raw_arguments = [
|
||||
"--connection=paramiko",
|
||||
"--private-key=/home/.../.vagrant/machines/.../private_key"
|
||||
]
|
||||
```
|
Loading…
Reference in New Issue