2014-03-21 11:02:23 +00:00
require_relative " ../../../base "
2015-02-10 14:28:00 +00:00
require Vagrant . source_root . join ( " plugins/provisioners/ansible/config/host " )
require Vagrant . source_root . join ( " plugins/provisioners/ansible/provisioner/host " )
2014-03-21 11:02:23 +00:00
2014-03-29 08:19:26 +00:00
#
# Helper Functions
#
def find_last_argument_after ( ref_index , ansible_playbook_args , arg_pattern )
subset = ansible_playbook_args [ ( ref_index + 1 ) .. ( ansible_playbook_args . length - 2 ) ] . reverse
subset . each do | i |
return true if i =~ arg_pattern
end
return false
end
2015-02-10 14:28:00 +00:00
describe VagrantPlugins :: Ansible :: Provisioner :: Host do
2014-03-21 11:02:23 +00:00
include_context " unit "
subject { described_class . new ( machine , config ) }
let ( :iso_env ) do
2014-03-29 08:19:26 +00:00
# We have to create a Vagrantfile so there is a Vagrant Environment to provide:
# - a location for the generated inventory
# - multi-machines configuration
2014-03-21 11:02:23 +00:00
env = isolated_environment
2014-03-29 08:19:26 +00:00
env . vagrantfile ( <<-VF)
Vagrant . configure ( " 2 " ) do | config |
config . vm . box = " base "
config . vm . define :machine1
config . vm . define :machine2
end
VF
2014-03-21 11:02:23 +00:00
env . create_vagrant_env
end
let ( :machine ) { iso_env . machine ( iso_env . machine_names [ 0 ] , :dummy ) }
2015-02-10 14:28:00 +00:00
let ( :config ) { VagrantPlugins :: Ansible :: Config :: Host . new }
2014-03-21 11:02:23 +00:00
let ( :ssh_info ) { {
private_key_path : [ '/path/to/my/key' ] ,
2017-04-11 11:40:14 +00:00
keys_only : true ,
2014-03-29 08:19:26 +00:00
username : 'testuser' ,
host : '127.0.0.1' ,
port : 2223
2014-03-21 11:02:23 +00:00
} }
2014-03-29 08:19:26 +00:00
let ( :existing_file ) { File . expand_path ( __FILE__ ) }
2014-04-12 06:29:40 +00:00
let ( :generated_inventory_dir ) { File . join ( machine . env . local_data_path , %w( provisioners ansible inventory ) ) }
let ( :generated_inventory_file ) { File . join ( generated_inventory_dir , 'vagrant_ansible_inventory' ) }
2014-03-29 08:19:26 +00:00
2014-03-21 11:02:23 +00:00
before do
2015-01-12 07:58:29 +00:00
Vagrant :: Util :: Platform . stub ( solaris? : false )
2014-11-25 07:12:43 +00:00
2014-03-21 11:02:23 +00:00
machine . stub ( ssh_info : ssh_info )
2014-03-29 08:19:26 +00:00
machine . env . stub ( active_machines : [ [ iso_env . machine_names [ 0 ] , :dummy ] , [ iso_env . machine_names [ 1 ] , :dummy ] ] )
2014-03-21 11:02:23 +00:00
2015-06-01 06:36:09 +00:00
stubbed_ui = Vagrant :: UI :: Colored . new
stubbed_ui . stub ( detail : " " )
machine . env . stub ( ui : stubbed_ui )
2014-03-22 16:18:00 +00:00
config . playbook = 'playbook.yml'
2014-03-21 11:02:23 +00:00
end
2014-03-29 08:19:26 +00:00
#
# Class methods for code reuse across examples
#
provisioners/ansible: force --connection=ssh
When `--connection` argument is not specified, Ansible will use the
'smart' mode, which can either use `ssh` or `paramiko` transports,
depending of the version of OpenSSH available. If OpenSSH version is new
enough to support ControlPersist technology, `ssh` will be used.
See also http://docs.ansible.com/intro_configuration.html#transport.
In order to support some advanced features of Vagrant (e.g. multiple ssh
private key identities or ssh forwarding), the Ansible provisioner
already must force `ssh` connection mode.
Having to deal with the possible fallback to `paramiko` increase the
burden of special cases that Ansible provisioner must handle, without
any added value, as Vagrant is based on OpenSSH and its users are
usually using modern operating systems.
With this change, the Ansible provisioner will officially only support
`ssh`. It will still be possible to switch to another connection mode
via `raw_arguments`, but it will breach the "contract", and no
(community) support can be expected in such use case.
ref #3900, #3396
2014-11-29 22:29:46 +00:00
def self . it_should_set_arguments_and_environment_variables (
2015-11-02 08:03:15 +00:00
expected_args_count = 5 ,
expected_vars_count = 4 ,
expected_host_key_checking = false ,
expected_transport_mode = " ssh " )
provisioners/ansible: force --connection=ssh
When `--connection` argument is not specified, Ansible will use the
'smart' mode, which can either use `ssh` or `paramiko` transports,
depending of the version of OpenSSH available. If OpenSSH version is new
enough to support ControlPersist technology, `ssh` will be used.
See also http://docs.ansible.com/intro_configuration.html#transport.
In order to support some advanced features of Vagrant (e.g. multiple ssh
private key identities or ssh forwarding), the Ansible provisioner
already must force `ssh` connection mode.
Having to deal with the possible fallback to `paramiko` increase the
burden of special cases that Ansible provisioner must handle, without
any added value, as Vagrant is based on OpenSSH and its users are
usually using modern operating systems.
With this change, the Ansible provisioner will officially only support
`ssh`. It will still be possible to switch to another connection mode
via `raw_arguments`, but it will breach the "contract", and no
(community) support can be expected in such use case.
ref #3900, #3396
2014-11-29 22:29:46 +00:00
2014-03-29 08:19:26 +00:00
it " sets implicit arguments in a specific order " do
2014-03-21 11:02:23 +00:00
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
2014-03-22 16:18:00 +00:00
expect ( args [ 0 ] ) . to eq ( " ansible-playbook " )
2015-11-02 08:03:15 +00:00
expect ( args [ 1 ] ) . to eq ( " --connection=ssh " )
expect ( args [ 2 ] ) . to eq ( " --timeout=30 " )
2014-03-21 11:02:23 +00:00
2014-05-05 07:08:10 +00:00
inventory_count = args . count { | x | x =~ / ^--inventory-file=.+$ / }
2014-03-29 08:19:26 +00:00
expect ( inventory_count ) . to be > 0
2014-03-21 11:02:23 +00:00
2014-03-22 16:18:00 +00:00
expect ( args [ args . length - 2 ] ) . to eq ( " playbook.yml " )
2014-03-21 11:02:23 +00:00
}
end
2014-04-26 12:08:10 +00:00
it " sets --limit argument " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
2014-11-30 01:02:29 +00:00
all_limits = args . select { | x | x =~ / ^(--limit=|-l) / }
2014-04-26 12:08:10 +00:00
if config . raw_arguments
2014-05-05 07:08:10 +00:00
raw_limits = config . raw_arguments . select { | x | x =~ / ^(--limit=|-l) / }
2014-11-30 01:02:29 +00:00
expect ( all_limits . length - raw_limits . length ) . to eq ( 1 )
expect ( all_limits . last ) . to eq ( raw_limits . last )
2014-04-26 12:08:10 +00:00
else
2014-11-30 01:02:29 +00:00
if config . limit
limit = config . limit . kind_of? ( Array ) ? config . limit . join ( ',' ) : config . limit
expect ( all_limits . last ) . to eq ( " --limit= #{ limit } " )
else
expect ( all_limits . first ) . to eq ( " --limit= #{ machine . name } " )
end
2014-04-26 12:08:10 +00:00
end
}
end
2014-03-29 08:19:26 +00:00
it " exports environment variables " do
2014-03-21 11:02:23 +00:00
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
cmd_opts = args . last
2014-11-30 05:55:21 +00:00
if expected_host_key_checking
2015-01-12 07:58:29 +00:00
expect ( cmd_opts [ :env ] [ 'ANSIBLE_SSH_ARGS' ] ) . to_not include ( " -o UserKnownHostsFile=/dev/null " )
2014-11-30 05:55:21 +00:00
else
expect ( cmd_opts [ :env ] [ 'ANSIBLE_SSH_ARGS' ] ) . to include ( " -o UserKnownHostsFile=/dev/null " )
end
2015-01-12 07:58:29 +00:00
expect ( cmd_opts [ :env ] [ 'ANSIBLE_SSH_ARGS' ] ) . to include ( " -o IdentitiesOnly=yes " )
2015-06-01 06:36:09 +00:00
expect ( cmd_opts [ :env ] [ 'ANSIBLE_FORCE_COLOR' ] ) . to eql ( " true " )
expect ( cmd_opts [ :env ] ) . to_not include ( " ANSIBLE_NOCOLOR " )
2014-03-29 08:19:26 +00:00
expect ( cmd_opts [ :env ] [ 'ANSIBLE_HOST_KEY_CHECKING' ] ) . to eql ( expected_host_key_checking . to_s )
2014-03-21 11:02:23 +00:00
expect ( cmd_opts [ :env ] [ 'PYTHONUNBUFFERED' ] ) . to eql ( 1 )
}
end
2014-03-29 08:19:26 +00:00
# "roughly" verify that only expected args/vars have been defined by the provisioner
it " sets the expected number of arguments and environment variables " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
expect ( args . length - 2 ) . to eq ( expected_args_count )
expect ( args . last [ :env ] . length ) . to eq ( expected_vars_count )
}
end
provisioners/ansible: force --connection=ssh
When `--connection` argument is not specified, Ansible will use the
'smart' mode, which can either use `ssh` or `paramiko` transports,
depending of the version of OpenSSH available. If OpenSSH version is new
enough to support ControlPersist technology, `ssh` will be used.
See also http://docs.ansible.com/intro_configuration.html#transport.
In order to support some advanced features of Vagrant (e.g. multiple ssh
private key identities or ssh forwarding), the Ansible provisioner
already must force `ssh` connection mode.
Having to deal with the possible fallback to `paramiko` increase the
burden of special cases that Ansible provisioner must handle, without
any added value, as Vagrant is based on OpenSSH and its users are
usually using modern operating systems.
With this change, the Ansible provisioner will officially only support
`ssh`. It will still be possible to switch to another connection mode
via `raw_arguments`, but it will breach the "contract", and no
(community) support can be expected in such use case.
ref #3900, #3396
2014-11-29 22:29:46 +00:00
2015-11-22 19:48:21 +00:00
it " enables ' #{ expected_transport_mode } ' as default transport mode " do
provisioners/ansible: force --connection=ssh
When `--connection` argument is not specified, Ansible will use the
'smart' mode, which can either use `ssh` or `paramiko` transports,
depending of the version of OpenSSH available. If OpenSSH version is new
enough to support ControlPersist technology, `ssh` will be used.
See also http://docs.ansible.com/intro_configuration.html#transport.
In order to support some advanced features of Vagrant (e.g. multiple ssh
private key identities or ssh forwarding), the Ansible provisioner
already must force `ssh` connection mode.
Having to deal with the possible fallback to `paramiko` increase the
burden of special cases that Ansible provisioner must handle, without
any added value, as Vagrant is based on OpenSSH and its users are
usually using modern operating systems.
With this change, the Ansible provisioner will officially only support
`ssh`. It will still be possible to switch to another connection mode
via `raw_arguments`, but it will breach the "contract", and no
(community) support can be expected in such use case.
ref #3900, #3396
2014-11-29 22:29:46 +00:00
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
index = args . rindex ( " --connection= #{ expected_transport_mode } " )
expect ( index ) . to be > 0
expect ( find_last_argument_after ( index , args , / --connection= \ w+ / ) ) . to be_false
}
end
2014-03-21 11:02:23 +00:00
end
2014-03-29 08:19:26 +00:00
def self . it_should_set_optional_arguments ( arg_map )
it " sets optional arguments " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
arg_map . each_pair do | vagrant_option , ansible_argument |
index = args . index ( ansible_argument )
if config . send ( vagrant_option )
expect ( index ) . to be > 0
else
expect ( index ) . to be_nil
end
end
}
end
end
provisioners/ansible: force --connection=ssh
When `--connection` argument is not specified, Ansible will use the
'smart' mode, which can either use `ssh` or `paramiko` transports,
depending of the version of OpenSSH available. If OpenSSH version is new
enough to support ControlPersist technology, `ssh` will be used.
See also http://docs.ansible.com/intro_configuration.html#transport.
In order to support some advanced features of Vagrant (e.g. multiple ssh
private key identities or ssh forwarding), the Ansible provisioner
already must force `ssh` connection mode.
Having to deal with the possible fallback to `paramiko` increase the
burden of special cases that Ansible provisioner must handle, without
any added value, as Vagrant is based on OpenSSH and its users are
usually using modern operating systems.
With this change, the Ansible provisioner will officially only support
`ssh`. It will still be possible to switch to another connection mode
via `raw_arguments`, but it will breach the "contract", and no
(community) support can be expected in such use case.
ref #3900, #3396
2014-11-29 22:29:46 +00:00
def self . it_should_explicitly_enable_ansible_ssh_control_persist_defaults
2014-03-29 08:19:26 +00:00
it " configures ControlPersist (like Ansible defaults) via ANSIBLE_SSH_ARGS " do
2014-03-21 11:02:23 +00:00
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
cmd_opts = args . last
expect ( cmd_opts [ :env ] [ 'ANSIBLE_SSH_ARGS' ] ) . to include ( " -o ControlMaster=auto " )
expect ( cmd_opts [ :env ] [ 'ANSIBLE_SSH_ARGS' ] ) . to include ( " -o ControlPersist=60s " )
}
end
end
2015-11-02 08:03:15 +00:00
def self . it_should_create_and_use_generated_inventory ( with_ssh_user = true )
2014-03-29 08:19:26 +00:00
it " generates an inventory with all active machines " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
expect ( config . inventory_path ) . to be_nil
expect ( File . exists? ( generated_inventory_file ) ) . to be_true
inventory_content = File . read ( generated_inventory_file )
2015-11-02 08:03:15 +00:00
if with_ssh_user
expect ( inventory_content ) . to include ( " #{ machine . name } ansible_ssh_host= #{ machine . ssh_info [ :host ] } ansible_ssh_port= #{ machine . ssh_info [ :port ] } ansible_ssh_user=' #{ machine . ssh_info [ :username ] } ' ansible_ssh_private_key_file=' #{ machine . ssh_info [ :private_key_path ] [ 0 ] } ' \n " )
else
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 " )
end
2014-03-29 08:19:26 +00:00
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
2014-04-12 06:29:40 +00:00
it " sets as ansible inventory the directory containing the auto-generated inventory file " do
2014-03-29 08:19:26 +00:00
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
2014-04-12 06:29:40 +00:00
inventory_index = args . rindex ( " --inventory-file= #{ generated_inventory_dir } " )
2014-03-29 08:19:26 +00:00
expect ( inventory_index ) . to be > 0
expect ( find_last_argument_after ( inventory_index , args , / --inventory-file= \ w+ / ) ) . to be_false
}
end
end
2014-03-21 11:02:23 +00:00
describe " # provision " do
before do
2014-03-22 16:18:00 +00:00
unless example . metadata [ :skip_before ]
config . finalize!
2016-09-20 20:58:41 +00:00
2014-03-22 16:18:00 +00:00
Vagrant :: Util :: Subprocess . stub ( execute : Vagrant :: Util :: Subprocess :: Result . new ( 0 , " " , " " ) )
2016-09-20 20:58:41 +00:00
subject . stub ( :check_path )
2014-03-22 16:18:00 +00:00
end
end
after do
unless example . metadata [ :skip_after ]
subject . provision
end
2014-03-21 11:02:23 +00:00
end
provisioners/ansible(both): fix ansible config files presence checks
With this change, the presence of Ansible configuration files (like
playbook file, inventory path, galaxy role file, etc.) is no longer
performed by the `config` classes, but by the `provisioner` classes
(at the beginning of the provision command).
This change fixes several issues:
- Resolve #6984 as `provision` method are only executed when remote
(ssh) communication with the guest machine is possible.
- Resolve #6763 in a better way than 4e451c6 initially did.
- Improve the general provisioner speed since the `config` checks are
actually triggered by many vagrant actions (e.g. `destroy`,...), and
can also be triggered multiple times during a vagrant run (e.g. on
callback request made by the machine provider).
Unlike the former `config`-based checks, the provision action won't
collect all the invalid options, but only report the first invalid
option found and abort the execution.
Some unit tests were not implemented yet to save my scarce "open source
contribution time" for other important issues, but they should be done
at last via GH-6633.
2016-05-31 22:30:07 +00:00
describe 'checking existence of Ansible configuration files' do
2016-09-20 20:58:41 +00:00
STUBBED_INVALID_PATH = " /test/239nfmd/invalid_path " . freeze
provisioners/ansible(both): fix ansible config files presence checks
With this change, the presence of Ansible configuration files (like
playbook file, inventory path, galaxy role file, etc.) is no longer
performed by the `config` classes, but by the `provisioner` classes
(at the beginning of the provision command).
This change fixes several issues:
- Resolve #6984 as `provision` method are only executed when remote
(ssh) communication with the guest machine is possible.
- Resolve #6763 in a better way than 4e451c6 initially did.
- Improve the general provisioner speed since the `config` checks are
actually triggered by many vagrant actions (e.g. `destroy`,...), and
can also be triggered multiple times during a vagrant run (e.g. on
callback request made by the machine provider).
Unlike the former `config`-based checks, the provision action won't
collect all the invalid options, but only report the first invalid
option found and abort the execution.
Some unit tests were not implemented yet to save my scarce "open source
contribution time" for other important issues, but they should be done
at last via GH-6633.
2016-05-31 22:30:07 +00:00
2016-09-20 20:58:41 +00:00
it 'raises an error when the `playbook` file does not exist' , skip_before : true , skip_after : true do
subject . stub ( :check_path ) . and_raise ( VagrantPlugins :: Ansible :: Errors :: AnsibleError ,
_key : :config_file_not_found ,
config_option : " playbook " ,
path : STUBBED_INVALID_PATH ,
system : " host " )
provisioners/ansible(both): fix ansible config files presence checks
With this change, the presence of Ansible configuration files (like
playbook file, inventory path, galaxy role file, etc.) is no longer
performed by the `config` classes, but by the `provisioner` classes
(at the beginning of the provision command).
This change fixes several issues:
- Resolve #6984 as `provision` method are only executed when remote
(ssh) communication with the guest machine is possible.
- Resolve #6763 in a better way than 4e451c6 initially did.
- Improve the general provisioner speed since the `config` checks are
actually triggered by many vagrant actions (e.g. `destroy`,...), and
can also be triggered multiple times during a vagrant run (e.g. on
callback request made by the machine provider).
Unlike the former `config`-based checks, the provision action won't
collect all the invalid options, but only report the first invalid
option found and abort the execution.
Some unit tests were not implemented yet to save my scarce "open source
contribution time" for other important issues, but they should be done
at last via GH-6633.
2016-05-31 22:30:07 +00:00
2016-09-20 20:58:41 +00:00
config . playbook = STUBBED_INVALID_PATH
config . finalize!
provisioners/ansible(both): fix ansible config files presence checks
With this change, the presence of Ansible configuration files (like
playbook file, inventory path, galaxy role file, etc.) is no longer
performed by the `config` classes, but by the `provisioner` classes
(at the beginning of the provision command).
This change fixes several issues:
- Resolve #6984 as `provision` method are only executed when remote
(ssh) communication with the guest machine is possible.
- Resolve #6763 in a better way than 4e451c6 initially did.
- Improve the general provisioner speed since the `config` checks are
actually triggered by many vagrant actions (e.g. `destroy`,...), and
can also be triggered multiple times during a vagrant run (e.g. on
callback request made by the machine provider).
Unlike the former `config`-based checks, the provision action won't
collect all the invalid options, but only report the first invalid
option found and abort the execution.
Some unit tests were not implemented yet to save my scarce "open source
contribution time" for other important issues, but they should be done
at last via GH-6633.
2016-05-31 22:30:07 +00:00
2016-09-20 20:58:41 +00:00
expect { subject . provision } . to raise_error ( VagrantPlugins :: Ansible :: Errors :: AnsibleError ,
" `playbook` does not exist on the host: #{ STUBBED_INVALID_PATH } " )
provisioners/ansible(both): fix ansible config files presence checks
With this change, the presence of Ansible configuration files (like
playbook file, inventory path, galaxy role file, etc.) is no longer
performed by the `config` classes, but by the `provisioner` classes
(at the beginning of the provision command).
This change fixes several issues:
- Resolve #6984 as `provision` method are only executed when remote
(ssh) communication with the guest machine is possible.
- Resolve #6763 in a better way than 4e451c6 initially did.
- Improve the general provisioner speed since the `config` checks are
actually triggered by many vagrant actions (e.g. `destroy`,...), and
can also be triggered multiple times during a vagrant run (e.g. on
callback request made by the machine provider).
Unlike the former `config`-based checks, the provision action won't
collect all the invalid options, but only report the first invalid
option found and abort the execution.
Some unit tests were not implemented yet to save my scarce "open source
contribution time" for other important issues, but they should be done
at last via GH-6633.
2016-05-31 22:30:07 +00:00
end
2016-09-20 20:58:41 +00:00
%w( config_file extra_vars inventory_path galaxy_role_file vault_password_file ) . each do | option_name |
it " raises an error when the ' #{ option_name } ' does not exist " , skip_before : true , skip_after : true do
Vagrant :: Util :: Subprocess . stub ( execute : Vagrant :: Util :: Subprocess :: Result . new ( 0 , " " , " " ) )
provisioners/ansible(both): fix ansible config files presence checks
With this change, the presence of Ansible configuration files (like
playbook file, inventory path, galaxy role file, etc.) is no longer
performed by the `config` classes, but by the `provisioner` classes
(at the beginning of the provision command).
This change fixes several issues:
- Resolve #6984 as `provision` method are only executed when remote
(ssh) communication with the guest machine is possible.
- Resolve #6763 in a better way than 4e451c6 initially did.
- Improve the general provisioner speed since the `config` checks are
actually triggered by many vagrant actions (e.g. `destroy`,...), and
can also be triggered multiple times during a vagrant run (e.g. on
callback request made by the machine provider).
Unlike the former `config`-based checks, the provision action won't
collect all the invalid options, but only report the first invalid
option found and abort the execution.
Some unit tests were not implemented yet to save my scarce "open source
contribution time" for other important issues, but they should be done
at last via GH-6633.
2016-05-31 22:30:07 +00:00
2016-09-20 20:58:41 +00:00
config . playbook = existing_file
config . send ( option_name + '=' , STUBBED_INVALID_PATH )
if option_name == 'extra_vars'
# little trick to auto-append the '@' prefix, which is a duty of the config validator...
config . validate ( machine )
end
config . finalize!
provisioners/ansible(both): fix ansible config files presence checks
With this change, the presence of Ansible configuration files (like
playbook file, inventory path, galaxy role file, etc.) is no longer
performed by the `config` classes, but by the `provisioner` classes
(at the beginning of the provision command).
This change fixes several issues:
- Resolve #6984 as `provision` method are only executed when remote
(ssh) communication with the guest machine is possible.
- Resolve #6763 in a better way than 4e451c6 initially did.
- Improve the general provisioner speed since the `config` checks are
actually triggered by many vagrant actions (e.g. `destroy`,...), and
can also be triggered multiple times during a vagrant run (e.g. on
callback request made by the machine provider).
Unlike the former `config`-based checks, the provision action won't
collect all the invalid options, but only report the first invalid
option found and abort the execution.
Some unit tests were not implemented yet to save my scarce "open source
contribution time" for other important issues, but they should be done
at last via GH-6633.
2016-05-31 22:30:07 +00:00
2016-09-20 20:58:41 +00:00
expect { subject . provision } . to raise_error ( VagrantPlugins :: Ansible :: Errors :: AnsibleError ,
" ` #{ option_name } ` does not exist on the host: #{ STUBBED_INVALID_PATH } " )
end
provisioners/ansible(both): fix ansible config files presence checks
With this change, the presence of Ansible configuration files (like
playbook file, inventory path, galaxy role file, etc.) is no longer
performed by the `config` classes, but by the `provisioner` classes
(at the beginning of the provision command).
This change fixes several issues:
- Resolve #6984 as `provision` method are only executed when remote
(ssh) communication with the guest machine is possible.
- Resolve #6763 in a better way than 4e451c6 initially did.
- Improve the general provisioner speed since the `config` checks are
actually triggered by many vagrant actions (e.g. `destroy`,...), and
can also be triggered multiple times during a vagrant run (e.g. on
callback request made by the machine provider).
Unlike the former `config`-based checks, the provision action won't
collect all the invalid options, but only report the first invalid
option found and abort the execution.
Some unit tests were not implemented yet to save my scarce "open source
contribution time" for other important issues, but they should be done
at last via GH-6633.
2016-05-31 22:30:07 +00:00
end
end
2014-03-29 08:19:26 +00:00
describe 'when ansible-playbook fails' do
it " raises an error " , skip_before : true , skip_after : true do
config . finalize!
2016-09-20 20:58:41 +00:00
subject . stub ( :check_path )
2014-03-29 08:19:26 +00:00
Vagrant :: Util :: Subprocess . stub ( execute : Vagrant :: Util :: Subprocess :: Result . new ( 1 , " " , " " ) )
2015-11-17 21:06:06 +00:00
expect { subject . provision } . to raise_error ( VagrantPlugins :: Ansible :: Errors :: AnsibleCommandFailed )
2014-03-29 08:19:26 +00:00
end
2014-03-21 11:02:23 +00:00
end
2014-03-29 08:19:26 +00:00
describe " with default options " do
it_should_set_arguments_and_environment_variables
it_should_create_and_use_generated_inventory
2014-03-21 11:02:23 +00:00
2014-03-29 08:19:26 +00:00
it " does not add any group section to the generated inventory " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
inventory_content = File . read ( generated_inventory_file )
expect ( inventory_content ) . to_not match ( / ^ \ s* \ [^ \\ + \ ] \ s*$ / )
2015-02-10 14:28:00 +00:00
# Ending this block with a negative expectation (to_not / not_to)
# would lead to a failure of the above expectation.
true
2014-03-29 08:19:26 +00:00
}
end
2014-03-31 14:25:16 +00:00
2015-02-10 14:28:00 +00:00
it " doesn't show the ansible-playbook command " do
2014-03-31 14:25:16 +00:00
expect ( machine . env . ui ) . not_to receive ( :detail ) . with { | full_command |
expect ( full_command ) . to include ( " ansible-playbook " )
}
end
2014-03-21 11:02:23 +00:00
end
2016-10-10 14:56:36 +00:00
describe " with playbook_command option " do
before do
config . playbook_command = " custom-ansible-playbook "
end
it " uses custom playbook_command to run playbooks " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
expect ( args [ 0 ] ) . to eq ( " custom-ansible-playbook " )
}
end
end
2015-12-01 16:04:56 +00:00
describe " with host_vars option " do
it_should_create_and_use_generated_inventory
2015-12-02 07:42:44 +00:00
it " adds host variables (given in Hash format) to the generated inventory " do
2015-12-01 16:04:56 +00:00
config . host_vars = {
2016-12-28 22:20:49 +00:00
machine1 : { " http_port " = > 80 , " comments " = > " 'some text with spaces' " }
2015-12-02 07:42:44 +00:00
}
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
inventory_content = File . read ( generated_inventory_file )
2016-12-28 22:20:49 +00:00
expect ( inventory_content ) . to match ( " ^ " + Regexp . quote ( machine . name ) + " .+http_port=80 comments='some text with spaces'$ " )
2015-12-02 07:42:44 +00:00
}
end
it " adds host variables (given in Array format) to the generated inventory " do
config . host_vars = {
machine1 : [ " http_port=80 " , " maxRequestsPerChild=808 " ]
}
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
inventory_content = File . read ( generated_inventory_file )
expect ( inventory_content ) . to match ( " ^ " + Regexp . quote ( machine . name ) + " .+http_port=80 maxRequestsPerChild=808 " )
}
end
it " adds host variables (given in String format) to the generated inventory " do
config . host_vars = {
:machine1 = > " http_port=80 maxRequestsPerChild=808 "
2015-12-01 16:04:56 +00:00
}
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
inventory_content = File . read ( generated_inventory_file )
expect ( inventory_content ) . to match ( " ^ " + Regexp . quote ( machine . name ) + " .+http_port=80 maxRequestsPerChild=808 " )
2015-12-02 07:37:41 +00:00
}
end
it " retrieves the host variables by machine name, also in String format " do
config . host_vars = {
" machine1 " = > " http_port=80 maxRequestsPerChild=808 "
}
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
inventory_content = File . read ( generated_inventory_file )
expect ( inventory_content ) . to match ( " ^ " + Regexp . quote ( machine . name ) + " .+http_port=80 maxRequestsPerChild=808 " )
2015-12-01 16:04:56 +00:00
}
end
end
2014-03-29 08:19:26 +00:00
describe " with groups option " do
it_should_create_and_use_generated_inventory
it " adds group sections to the generated inventory " do
config . groups = {
2015-12-03 08:39:12 +00:00
" group1 " = > " machine1 " ,
" group1:children " = > 'bar group3' ,
2014-04-25 20:11:13 +00:00
" group2 " = > [ iso_env . machine_names [ 1 ] ] ,
2014-03-29 08:19:26 +00:00
" group3 " = > [ " unknown " , " #{ machine . name } " ] ,
2015-12-03 20:07:05 +00:00
" group4 " = > [ " machine[1:2] " , " machine[a:f] " ] ,
2015-12-04 01:16:01 +00:00
" group6 " = > [ machine . name ] ,
2014-03-29 08:19:26 +00:00
" bar " = > [ " #{ machine . name } " , " group3 " ] ,
2015-12-03 08:39:12 +00:00
" bar:children " = > [ " group1 " , " group2 " , " group3 " , " group5 " ] ,
2014-03-29 08:19:26 +00:00
}
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
inventory_content = File . read ( generated_inventory_file )
2015-12-03 08:39:12 +00:00
# Accept String instead of Array for group member list
expect ( inventory_content ) . to include ( " [group1] \n machine1 \n \n " )
expect ( inventory_content ) . to include ( " [group1:children] \n bar \n group3 \n \n " )
2014-03-29 08:19:26 +00:00
# Skip "lost" machines
expect ( inventory_content ) . to include ( " [group2] \n \n " )
2014-03-22 16:18:00 +00:00
2014-03-29 08:19:26 +00:00
# Skip "unknown" machines
2015-12-03 08:39:12 +00:00
expect ( inventory_content ) . to include ( " [group3] \n #{ machine . name } \n \n " )
2014-03-21 11:02:23 +00:00
2015-12-03 08:34:53 +00:00
# Accept Symbol datatype for group names
2015-12-04 01:16:01 +00:00
expect ( inventory_content ) . to include ( " [group6] \n #{ machine . name } \n \n " )
2015-12-03 08:34:53 +00:00
2015-12-03 20:07:05 +00:00
# Accept host range patterns
expect ( inventory_content ) . to include ( " [group4] \n machine[1:2] \n machine[a:f] \n " )
2014-03-29 08:19:26 +00:00
# Don't mix group names and host names
2015-12-03 08:39:12 +00:00
expect ( inventory_content ) . to include ( " [bar] \n #{ machine . name } \n \n " )
2014-03-29 08:19:26 +00:00
# A group of groups only includes declared groups
2015-12-03 08:39:12 +00:00
expect ( inventory_content ) . not_to include ( " group5 " )
expect ( inventory_content ) . to match ( Regexp . quote ( " [bar:children] \n group1 \n group2 \n group3 \n " ) + " $ " )
2014-03-29 08:19:26 +00:00
}
end
2015-12-01 13:25:41 +00:00
it " adds group vars to the generated inventory " do
config . groups = {
" group1 " = > [ machine . name ] ,
" group2 " = > [ machine . name ] ,
" group3 " = > [ machine . name ] ,
" group1:vars " = > { " hashvar1 " = > " hashvalue1 " , " hashvar2 " = > " hashvalue2 " } ,
" group2:vars " = > [ " arrayvar1=arrayvalue1 " , " arrayvar2=arrayvalue2 " ] ,
" group3:vars " = > " stringvar1=stringvalue1 stringvar2=stringvalue2 " ,
}
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
inventory_content = File . read ( generated_inventory_file )
# Hash syntax
expect ( inventory_content ) . to include ( " [group1:vars] \n hashvar1=hashvalue1 \n hashvar2=hashvalue2 \n " )
# Array syntax
expect ( inventory_content ) . to include ( " [group2:vars] \n arrayvar1=arrayvalue1 \n arrayvar2=arrayvalue2 \n " )
# Single string syntax
expect ( inventory_content ) . to include ( " [group3:vars] \n stringvar1=stringvalue1 \n stringvar2=stringvalue2 \n " )
2015-12-04 09:05:07 +00:00
}
2015-12-01 13:25:41 +00:00
end
2014-03-29 08:19:26 +00:00
end
describe " with host_key_checking option enabled " do
before do
2014-04-12 09:00:34 +00:00
config . host_key_checking = true
2014-03-29 08:19:26 +00:00
end
2015-11-02 08:03:15 +00:00
it_should_set_arguments_and_environment_variables 5 , 4 , true
2014-03-29 08:19:26 +00:00
end
describe " with boolean (flag) options disabled " do
before do
config . sudo = false
config . ask_sudo_pass = false
2014-04-14 08:29:10 +00:00
config . ask_vault_pass = false
2014-03-29 08:19:26 +00:00
config . sudo_user = 'root'
end
2015-11-02 08:03:15 +00:00
it_should_set_arguments_and_environment_variables 6
2014-03-29 08:19:26 +00:00
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
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
expect ( args . index ( " --sudo " ) ) . to be_nil
expect ( args . index ( " --ask-sudo-pass " ) ) . to be_nil
2014-04-14 08:29:10 +00:00
expect ( args . index ( " --ask-vault-pass " ) ) . to be_nil
2014-03-29 08:19:26 +00:00
}
end
end
describe " with raw_arguments option " do
before do
config . sudo = false
2015-11-02 08:03:15 +00:00
config . force_remote_user = false
2014-03-29 08:19:26 +00:00
config . skip_tags = %w( foo bar )
2014-11-30 01:02:29 +00:00
config . limit = " all "
2014-03-29 08:19:26 +00:00
config . raw_arguments = [ " --connection=paramiko " ,
" --skip-tags=ignored " ,
" --module-path=/other/modules " ,
" --sudo " ,
2014-04-26 12:08:10 +00:00
" -l localhost " ,
" --limit=foo " ,
" --limit=bar " ,
2014-03-29 08:19:26 +00:00
" --inventory-file=/forget/it/my/friend " ,
2014-11-30 01:02:29 +00:00
" --user=lion " ,
2014-03-29 08:19:26 +00:00
" --new-arg=yeah " ]
end
2014-12-22 04:13:14 +00:00
it_should_set_arguments_and_environment_variables 17 , 4 , false , " paramiko "
2014-03-29 08:19:26 +00:00
it " sets all raw arguments " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
config . raw_arguments . each do | raw_arg |
expect ( args ) . to include ( raw_arg )
end
}
end
2014-11-30 01:02:29 +00:00
it " sets raw arguments after arguments related to supported options " do
2014-03-29 08:19:26 +00:00
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
2014-11-30 01:02:29 +00:00
expect ( args . index ( " --user=lion " ) ) . to be > args . index ( " --user=testuser " )
expect ( args . index ( " --inventory-file=/forget/it/my/friend " ) ) . to be > args . index ( " --inventory-file= #{ generated_inventory_dir } " )
expect ( args . index ( " --limit=bar " ) ) . to be > args . index ( " --limit=all " )
expect ( args . index ( " --skip-tags=ignored " ) ) . to be > args . index ( " --skip-tags=foo,bar " )
2014-03-29 08:19:26 +00:00
}
end
it " sets boolean flag (e.g. --sudo) defined in raw_arguments, even if corresponding option is set to false " do
2014-03-21 11:02:23 +00:00
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
2014-03-29 08:19:26 +00:00
expect ( args ) . to include ( '--sudo' )
}
end
end
describe " with limit option " do
before do
config . limit = %w( foo !bar )
end
2014-04-26 12:08:10 +00:00
it_should_set_arguments_and_environment_variables
2014-03-22 16:18:00 +00:00
end
2015-11-02 08:03:15 +00:00
context " with force_remote_user option disabled " do
before do
config . force_remote_user = false
end
it_should_create_and_use_generated_inventory false # i.e. without setting ansible_ssh_user in inventory
it_should_set_arguments_and_environment_variables 6
it " uses a --user argument to set a default remote user " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
expect ( args ) . not_to include ( " --extra-vars=ansible_ssh_user=' #{ machine . ssh_info [ :username ] } ' " )
expect ( args ) . to include ( " --user= #{ machine . ssh_info [ :username ] } " )
}
end
end
2015-11-22 19:48:21 +00:00
context " with winrm communicator " do
let ( :iso_winrm_env ) do
env = isolated_environment
env . vagrantfile <<-VF
Vagrant . configure ( " 2 " ) do | config |
config . winrm . username = 'winner'
config . winrm . password = 'winword'
config . winrm . transport = :ssl
config . vm . define :machine1 do | machine |
machine . vm . box = " winbox "
machine . vm . communicator = :winrm
end
end
VF
env . create_vagrant_env
end
let ( :machine ) { iso_winrm_env . machine ( iso_winrm_env . machine_names [ 0 ] , :dummy ) }
it_should_set_arguments_and_environment_variables
it " generates an inventory with winrm connection settings " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
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 ( " machine1 ansible_connection=winrm ansible_ssh_host=127.0.0.1 ansible_ssh_port=55986 ansible_ssh_user='winner' ansible_ssh_pass='winword' \n " )
}
end
2015-11-23 08:05:36 +00:00
describe " with force_remote_user option disabled " do
before do
config . force_remote_user = false
end
2015-11-23 08:12:56 +00:00
it " doesn't set the ansible remote user in inventory and use '--user' argument with the vagrant ssh username " do
2015-11-23 08:05:36 +00:00
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
inventory_content = File . read ( generated_inventory_file )
expect ( inventory_content ) . to include ( " machine1 ansible_connection=winrm ansible_ssh_host=127.0.0.1 ansible_ssh_port=55986 ansible_ssh_pass='winword' \n " )
expect ( args ) . to include ( " --user=testuser " )
}
end
end
2015-11-22 19:48:21 +00:00
end
2014-03-22 16:18:00 +00:00
describe " with inventory_path option " do
before do
2014-03-29 08:19:26 +00:00
config . inventory_path = existing_file
end
2015-11-02 08:03:15 +00:00
it_should_set_arguments_and_environment_variables 6
2014-03-29 08:19:26 +00:00
it " does not generate the inventory and uses given inventory path instead " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
expect ( args ) . to include ( " --inventory-file= #{ existing_file } " )
expect ( args ) . not_to include ( " --inventory-file= #{ generated_inventory_file } " )
expect ( File . exists? ( generated_inventory_file ) ) . to be_false
}
end
2015-11-02 08:03:15 +00:00
it " uses an --extra-vars argument to force ansible_ssh_user parameter " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
expect ( args ) . not_to include ( " --user= #{ machine . ssh_info [ :username ] } " )
expect ( args ) . to include ( " --extra-vars=ansible_ssh_user=' #{ machine . ssh_info [ :username ] } ' " )
}
end
describe " with force_remote_user option disabled " do
before do
config . force_remote_user = false
end
it " uses a --user argument to set a default remote user " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
expect ( args ) . not_to include ( " --extra-vars=ansible_ssh_user=' #{ machine . ssh_info [ :username ] } ' " )
expect ( args ) . to include ( " --user= #{ machine . ssh_info [ :username ] } " )
}
end
end
2014-03-29 08:19:26 +00:00
end
2016-09-20 20:58:41 +00:00
context " with config_file option defined " do
before do
config . config_file = existing_file
end
it " sets ANSIBLE_CONFIG environment variable " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
cmd_opts = args . last
expect ( cmd_opts [ :env ] ) . to include ( " ANSIBLE_CONFIG " )
expect ( cmd_opts [ :env ] [ 'ANSIBLE_CONFIG' ] ) . to eql ( existing_file )
}
end
end
2014-04-14 08:29:10 +00:00
describe " with ask_vault_pass option " do
before do
config . ask_vault_pass = true
end
2015-11-02 08:03:15 +00:00
it_should_set_arguments_and_environment_variables 6
2014-04-14 08:29:10 +00:00
it " should ask the vault password " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
expect ( args ) . to include ( " --ask-vault-pass " )
}
end
end
describe " with vault_password_file option " do
before do
config . vault_password_file = existing_file
end
2015-11-02 08:03:15 +00:00
it_should_set_arguments_and_environment_variables 6
2014-04-14 08:29:10 +00:00
it " uses the given vault password file " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
expect ( args ) . to include ( " --vault-password-file= #{ existing_file } " )
}
end
end
2014-03-29 08:19:26 +00:00
describe " with raw_ssh_args " do
before do
2014-05-04 19:25:43 +00:00
config . raw_ssh_args = [ '-o ControlMaster=no' , '-o ForwardAgent=no' ]
2014-03-22 16:18:00 +00:00
end
2015-11-02 08:03:15 +00:00
it_should_set_arguments_and_environment_variables
provisioners/ansible: force --connection=ssh
When `--connection` argument is not specified, Ansible will use the
'smart' mode, which can either use `ssh` or `paramiko` transports,
depending of the version of OpenSSH available. If OpenSSH version is new
enough to support ControlPersist technology, `ssh` will be used.
See also http://docs.ansible.com/intro_configuration.html#transport.
In order to support some advanced features of Vagrant (e.g. multiple ssh
private key identities or ssh forwarding), the Ansible provisioner
already must force `ssh` connection mode.
Having to deal with the possible fallback to `paramiko` increase the
burden of special cases that Ansible provisioner must handle, without
any added value, as Vagrant is based on OpenSSH and its users are
usually using modern operating systems.
With this change, the Ansible provisioner will officially only support
`ssh`. It will still be possible to switch to another connection mode
via `raw_arguments`, but it will breach the "contract", and no
(community) support can be expected in such use case.
ref #3900, #3396
2014-11-29 22:29:46 +00:00
it_should_explicitly_enable_ansible_ssh_control_persist_defaults
2014-03-22 16:18:00 +00:00
2014-03-29 08:19:26 +00:00
it " passes custom SSH options via ANSIBLE_SSH_ARGS with the highest priority " do
2014-03-22 16:18:00 +00:00
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
2014-03-29 08:19:26 +00:00
cmd_opts = args . last
raw_opt_index = cmd_opts [ :env ] [ 'ANSIBLE_SSH_ARGS' ] . index ( " -o ControlMaster=no " )
default_opt_index = cmd_opts [ :env ] [ 'ANSIBLE_SSH_ARGS' ] . index ( " -o ControlMaster=auto " )
expect ( raw_opt_index ) . to be < default_opt_index
2014-03-21 11:02:23 +00:00
}
end
2014-03-29 08:19:26 +00:00
2014-05-04 19:25:43 +00:00
describe " and with ssh forwarding enabled " do
before do
ssh_info [ :forward_agent ] = true
end
it " sets '-o ForwardAgent=yes' via ANSIBLE_SSH_ARGS with higher priority than raw_ssh_args values " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
cmd_opts = args . last
forwardAgentYes = cmd_opts [ :env ] [ 'ANSIBLE_SSH_ARGS' ] . index ( " -o ForwardAgent=yes " )
forwardAgentNo = cmd_opts [ :env ] [ 'ANSIBLE_SSH_ARGS' ] . index ( " -o ForwardAgent=no " )
expect ( forwardAgentYes ) . to be < forwardAgentNo
}
end
end
2014-03-21 11:02:23 +00:00
end
describe " with multiple SSH identities " do
before do
ssh_info [ :private_key_path ] = [ '/path/to/my/key' , '/an/other/identity' , '/yet/an/other/key' ]
end
2015-11-02 08:03:15 +00:00
it_should_set_arguments_and_environment_variables
provisioners/ansible: force --connection=ssh
When `--connection` argument is not specified, Ansible will use the
'smart' mode, which can either use `ssh` or `paramiko` transports,
depending of the version of OpenSSH available. If OpenSSH version is new
enough to support ControlPersist technology, `ssh` will be used.
See also http://docs.ansible.com/intro_configuration.html#transport.
In order to support some advanced features of Vagrant (e.g. multiple ssh
private key identities or ssh forwarding), the Ansible provisioner
already must force `ssh` connection mode.
Having to deal with the possible fallback to `paramiko` increase the
burden of special cases that Ansible provisioner must handle, without
any added value, as Vagrant is based on OpenSSH and its users are
usually using modern operating systems.
With this change, the Ansible provisioner will officially only support
`ssh`. It will still be possible to switch to another connection mode
via `raw_arguments`, but it will breach the "contract", and no
(community) support can be expected in such use case.
ref #3900, #3396
2014-11-29 22:29:46 +00:00
it_should_explicitly_enable_ansible_ssh_control_persist_defaults
2014-03-21 11:02:23 +00:00
it " passes additional Identity Files via ANSIBLE_SSH_ARGS " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
cmd_opts = args . last
2016-10-04 11:15:04 +00:00
expect ( cmd_opts [ :env ] [ 'ANSIBLE_SSH_ARGS' ] ) . to include ( " -o 'IdentityFile=/an/other/identity' " )
expect ( cmd_opts [ :env ] [ 'ANSIBLE_SSH_ARGS' ] ) . to include ( " -o 'IdentityFile=/yet/an/other/key' " )
2014-03-21 11:02:23 +00:00
}
end
end
describe " with ssh forwarding enabled " do
before do
ssh_info [ :forward_agent ] = true
end
2015-11-02 08:03:15 +00:00
it_should_set_arguments_and_environment_variables
provisioners/ansible: force --connection=ssh
When `--connection` argument is not specified, Ansible will use the
'smart' mode, which can either use `ssh` or `paramiko` transports,
depending of the version of OpenSSH available. If OpenSSH version is new
enough to support ControlPersist technology, `ssh` will be used.
See also http://docs.ansible.com/intro_configuration.html#transport.
In order to support some advanced features of Vagrant (e.g. multiple ssh
private key identities or ssh forwarding), the Ansible provisioner
already must force `ssh` connection mode.
Having to deal with the possible fallback to `paramiko` increase the
burden of special cases that Ansible provisioner must handle, without
any added value, as Vagrant is based on OpenSSH and its users are
usually using modern operating systems.
With this change, the Ansible provisioner will officially only support
`ssh`. It will still be possible to switch to another connection mode
via `raw_arguments`, but it will breach the "contract", and no
(community) support can be expected in such use case.
ref #3900, #3396
2014-11-29 22:29:46 +00:00
it_should_explicitly_enable_ansible_ssh_control_persist_defaults
2014-03-21 11:02:23 +00:00
it " enables SSH-Forwarding via ANSIBLE_SSH_ARGS " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
cmd_opts = args . last
expect ( cmd_opts [ :env ] [ 'ANSIBLE_SSH_ARGS' ] ) . to include ( " -o ForwardAgent=yes " )
}
end
2016-09-21 21:19:55 +00:00
end
describe " with an ssh proxy command configured " do
before do
ssh_info [ :proxy_command ] = " ssh -W %h:%p -q user@remote_libvirt_host "
end
it " sets '-o ProxyCommand' via ANSIBLE_SSH_ARGS " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
cmd_opts = args . last
expect ( cmd_opts [ :env ] [ 'ANSIBLE_SSH_ARGS' ] ) . to include ( " -o ProxyCommand='ssh -W %h:%p -q user@remote_libvirt_host' " )
}
end
2014-03-21 11:02:23 +00:00
end
2015-02-10 14:28:00 +00:00
context " with verbose option defined " do
%w( vv vvvv ) . each do | verbose_option |
describe " with a value of ' #{ verbose_option } ' " do
before do
config . verbose = verbose_option
end
2015-11-08 13:01:23 +00:00
it_should_set_arguments_and_environment_variables 6
2015-02-10 14:28:00 +00:00
it_should_set_optional_arguments ( { " verbose " = > " - #{ verbose_option } " } )
it " shows the ansible-playbook command and set verbosity to '- #{ verbose_option } ' level " do
expect ( machine . env . ui ) . to receive ( :detail ) . with { | full_command |
2016-03-05 16:15:28 +00:00
expect ( full_command ) . to eq ( " PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --connection=ssh --timeout=30 --limit= \" machine1 \" --inventory-file= #{ generated_inventory_dir } - #{ verbose_option } playbook.yml " )
2015-02-10 14:28:00 +00:00
}
end
end
describe " with a value of '- #{ verbose_option } ' " do
before do
config . verbose = " - #{ verbose_option } "
end
2015-11-08 13:01:23 +00:00
it_should_set_arguments_and_environment_variables 6
2015-02-10 14:28:00 +00:00
it_should_set_optional_arguments ( { " verbose " = > " - #{ verbose_option } " } )
it " shows the ansible-playbook command and set verbosity to '- #{ verbose_option } ' level " do
expect ( machine . env . ui ) . to receive ( :detail ) . with { | full_command |
2016-03-05 16:15:28 +00:00
expect ( full_command ) . to eq ( " PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --connection=ssh --timeout=30 --limit= \" machine1 \" --inventory-file= #{ generated_inventory_dir } - #{ verbose_option } playbook.yml " )
2015-02-10 14:28:00 +00:00
}
end
end
2014-03-31 14:25:16 +00:00
end
2015-02-10 14:28:00 +00:00
describe " with an invalid string " do
before do
config . verbose = " wrong "
end
2014-03-31 14:25:16 +00:00
2015-11-08 13:01:23 +00:00
it_should_set_arguments_and_environment_variables 6
2015-02-10 14:28:00 +00:00
it_should_set_optional_arguments ( { " verbose " = > " -v " } )
it " shows the ansible-playbook command and set verbosity to '-v' level " do
expect ( machine . env . ui ) . to receive ( :detail ) . with { | full_command |
2016-03-05 16:15:28 +00:00
expect ( full_command ) . to eq ( " PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true ANSIBLE_HOST_KEY_CHECKING=false ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o ControlMaster=auto -o ControlPersist=60s' ansible-playbook --connection=ssh --timeout=30 --limit= \" machine1 \" --inventory-file= #{ generated_inventory_dir } -v playbook.yml " )
2015-02-10 14:28:00 +00:00
}
end
2015-06-01 06:36:09 +00:00
end
2015-02-10 14:28:00 +00:00
describe " with an empty string " do
before do
config . verbose = " "
end
it_should_set_arguments_and_environment_variables
it " doesn't show the ansible-playbook command " do
expect ( machine . env . ui ) . not_to receive ( :detail ) . with { | full_command |
expect ( full_command ) . to include ( " ansible-playbook " )
}
end
end
2015-06-01 06:36:09 +00:00
end
describe " without colorized output " do
before do
machine . env . stub ( ui : Vagrant :: UI :: Basic . new )
end
it " disables ansible-playbook colored output " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
cmd_opts = args . last
expect ( cmd_opts [ :env ] ) . to_not include ( " ANSIBLE_FORCE_COLOR " )
expect ( cmd_opts [ :env ] [ 'ANSIBLE_NOCOLOR' ] ) . to eql ( " true " )
2014-03-31 14:25:16 +00:00
}
end
end
2015-11-17 21:06:06 +00:00
describe " with galaxy support " do
before do
config . galaxy_role_file = existing_file
end
it " raises an error when ansible-galaxy command fails " , skip_before : true , skip_after : true do
config . finalize!
2016-09-20 20:58:41 +00:00
subject . stub ( :check_path )
2015-11-17 21:06:06 +00:00
Vagrant :: Util :: Subprocess . stub ( execute : Vagrant :: Util :: Subprocess :: Result . new ( 1 , " " , " " ) )
expect { subject . provision } . to raise_error ( VagrantPlugins :: Ansible :: Errors :: AnsibleCommandFailed )
end
it " execute ansible-galaxy and ansible-playbook " do
# TODO: to be improved, but I'm currenty facing some issues, maybe only present in RSpec 2.14...
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . twice
end
describe " with verbose option enabled " do
before do
config . verbose = true
end
xit " shows the ansible-galaxy command in use "
end
end
2016-06-11 05:28:05 +00:00
context " with galaxy_roles_path option defined " do
before do
config . galaxy_roles_path = " my-roles "
end
it " sets ANSIBLE_ROLES_PATH with corresponding absolute path " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
cmd_opts = args . last
expect ( cmd_opts [ :env ] ) . to include ( " ANSIBLE_ROLES_PATH " )
expect ( cmd_opts [ :env ] [ 'ANSIBLE_ROLES_PATH' ] ) . to eql ( File . join ( machine . env . root_path , " my-roles " ) )
}
end
end
2016-03-05 15:59:38 +00:00
context " with extra_vars option defined " do
describe " with a hash value " do
before do
config . extra_vars = { var1 : %Q( string with 'apostrophes', \\ , " and = ) , var2 : { x : 42 } }
end
it_should_set_optional_arguments ( { " extra_vars " = > " --extra-vars={ \" var1 \" : \" string with 'apostrophes', \\ \\ , \\ \" and = \" , \" var2 \" :{ \" x \" :42}} " } )
end
describe " with a string value referring to file path (with the '@' prefix) " do
before do
config . extra_vars = " @ #{ existing_file } "
end
it_should_set_optional_arguments ( { " extra_vars " = > " --extra-vars=@ #{ File . expand_path ( __FILE__ ) } " } )
end
end
2015-02-10 14:28:00 +00:00
# The Vagrant Ansible provisioner does not validate the coherency of
# argument combinations, and let ansible-playbook complain.
2014-03-29 08:19:26 +00:00
describe " with a maximum of options " do
before do
2014-03-31 14:25:16 +00:00
# vagrant general options
ssh_info [ :forward_agent ] = true
ssh_info [ :private_key_path ] = [ '/my/key1' , '/my/key2' ]
2014-03-29 08:19:26 +00:00
# command line arguments
2016-06-11 05:28:05 +00:00
config . galaxy_roles_path = " /up/to the stars "
2016-03-05 15:59:38 +00:00
config . extra_vars = { var1 : %Q( string with 'apostrophes', \\ , " and = ) , var2 : { x : 42 } }
2014-03-29 08:19:26 +00:00
config . sudo = true
config . sudo_user = 'deployer'
config . verbose = " vvv "
config . ask_sudo_pass = true
2014-04-25 19:59:39 +00:00
config . ask_vault_pass = true
config . vault_password_file = existing_file
2014-03-29 08:19:26 +00:00
config . tags = %w( db www )
config . skip_tags = %w( foo bar )
config . limit = 'machine*:&vagrant:!that_one'
2016-03-05 16:15:28 +00:00
config . start_at_task = " joe's awesome task "
2016-04-20 16:05:28 +00:00
config . raw_arguments = [ " --why-not " , " --su-user=foot " , " --ask-su-pass " , " --limit=all " , " --private-key=./myself.key " , " --extra-vars='{ \" var3 \" : \" foo \" }' " ]
2014-03-29 08:19:26 +00:00
# environment variables
2016-09-20 20:58:41 +00:00
config . config_file = existing_file
2014-03-29 08:19:26 +00:00
config . host_key_checking = true
config . raw_ssh_args = [ '-o ControlMaster=no' ]
end
2016-09-20 20:58:41 +00:00
it_should_set_arguments_and_environment_variables 21 , 6 , true
provisioners/ansible: force --connection=ssh
When `--connection` argument is not specified, Ansible will use the
'smart' mode, which can either use `ssh` or `paramiko` transports,
depending of the version of OpenSSH available. If OpenSSH version is new
enough to support ControlPersist technology, `ssh` will be used.
See also http://docs.ansible.com/intro_configuration.html#transport.
In order to support some advanced features of Vagrant (e.g. multiple ssh
private key identities or ssh forwarding), the Ansible provisioner
already must force `ssh` connection mode.
Having to deal with the possible fallback to `paramiko` increase the
burden of special cases that Ansible provisioner must handle, without
any added value, as Vagrant is based on OpenSSH and its users are
usually using modern operating systems.
With this change, the Ansible provisioner will officially only support
`ssh`. It will still be possible to switch to another connection mode
via `raw_arguments`, but it will breach the "contract", and no
(community) support can be expected in such use case.
ref #3900, #3396
2014-11-29 22:29:46 +00:00
it_should_explicitly_enable_ansible_ssh_control_persist_defaults
2016-03-05 15:59:38 +00:00
it_should_set_optional_arguments ( { " extra_vars " = > " --extra-vars={ \" var1 \" : \" string with 'apostrophes', \\ \\ , \\ \" and = \" , \" var2 \" :{ \" x \" :42}} " ,
2014-04-25 19:59:39 +00:00
" sudo " = > " --sudo " ,
" sudo_user " = > " --sudo-user=deployer " ,
" verbose " = > " -vvv " ,
" ask_sudo_pass " = > " --ask-sudo-pass " ,
" ask_vault_pass " = > " --ask-vault-pass " ,
" vault_password_file " = > " --vault-password-file= #{ File . expand_path ( __FILE__ ) } " ,
" tags " = > " --tags=db,www " ,
" skip_tags " = > " --skip-tags=foo,bar " ,
" limit " = > " --limit=machine*:&vagrant:!that_one " ,
2016-03-05 16:15:28 +00:00
" start_at_task " = > " --start-at-task=joe's awesome task " ,
2014-03-29 08:19:26 +00:00
} )
provisioners/ansible: force --connection=ssh
When `--connection` argument is not specified, Ansible will use the
'smart' mode, which can either use `ssh` or `paramiko` transports,
depending of the version of OpenSSH available. If OpenSSH version is new
enough to support ControlPersist technology, `ssh` will be used.
See also http://docs.ansible.com/intro_configuration.html#transport.
In order to support some advanced features of Vagrant (e.g. multiple ssh
private key identities or ssh forwarding), the Ansible provisioner
already must force `ssh` connection mode.
Having to deal with the possible fallback to `paramiko` increase the
burden of special cases that Ansible provisioner must handle, without
any added value, as Vagrant is based on OpenSSH and its users are
usually using modern operating systems.
With this change, the Ansible provisioner will officially only support
`ssh`. It will still be possible to switch to another connection mode
via `raw_arguments`, but it will breach the "contract", and no
(community) support can be expected in such use case.
ref #3900, #3396
2014-11-29 22:29:46 +00:00
2014-04-25 19:59:39 +00:00
it " also includes given raw arguments " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
2015-05-29 08:18:21 +00:00
expect ( args ) . to include ( " --why-not " )
2014-04-25 19:59:39 +00:00
expect ( args ) . to include ( " --su-user=foot " )
expect ( args ) . to include ( " --ask-su-pass " )
2015-05-29 08:18:21 +00:00
expect ( args ) . to include ( " --limit=all " )
expect ( args ) . to include ( " --private-key=./myself.key " )
2014-04-25 19:59:39 +00:00
}
end
2014-03-31 14:25:16 +00:00
it " shows the ansible-playbook command, with additional quotes when required " do
expect ( machine . env . ui ) . to receive ( :detail ) . with { | full_command |
2016-10-04 11:15:04 +00:00
expect ( full_command ) . to eq ( %Q( PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true ANSIBLE_ROLES_PATH='/up/to the stars' ANSIBLE_HOST_KEY_CHECKING=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 --connection=ssh --timeout=30 --ask-sudo-pass --ask-vault-pass --limit="machine*:&vagrant:!that_one" --inventory-file= #{ generated_inventory_dir } --extra-vars="{ \\ "var1 \\ ": \\ "string with 'apostrophes', \\ \\ \\ \\ , \\ \\ \\ " and = \\ ", \\ "var2 \\ ":{ \\ "x \\ ":42}}" --sudo --sudo-user=deployer -vvv --vault-password-file= #{ File . expand_path ( __FILE__ ) } --tags=db,www --skip-tags=foo,bar --start-at-task="joe's awesome task" --why-not --su-user=foot --ask-su-pass --limit=all --private-key=./myself.key --extra-vars='{ \" var3 \" : \" foo \" }' playbook.yml ) )
2014-03-31 14:25:16 +00:00
}
end
2014-03-29 08:19:26 +00:00
end
2015-01-12 08:37:16 +00:00
#
# Special cases related to the VM provider context
#
context " with Docker provider on a non-Linux host " do
2015-05-29 08:36:17 +00:00
2015-01-12 08:37:16 +00:00
let ( :fake_host_ssh_info ) { {
private_key_path : [ '/path/to/docker/host/key' ] ,
username : 'boot9docker' ,
host : '127.0.0.1' ,
port : 2299
} }
let ( :fake_host_vm ) {
double ( " host_vm " ) . tap do | h |
h . stub ( ssh_info : fake_host_ssh_info )
2015-05-29 08:36:17 +00:00
end
2015-01-12 08:37:16 +00:00
}
before do
machine . stub ( provider_name : :docker )
machine . provider . stub ( host_vm? : true )
machine . provider . stub ( host_vm : fake_host_vm )
end
it " uses an SSH ProxyCommand to reach the VM " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
cmd_opts = args . last
expect ( cmd_opts [ :env ] [ 'ANSIBLE_SSH_ARGS' ] ) . to include ( " -o ProxyCommand='ssh boot9docker@127.0.0.1 -p 2299 -i /path/to/docker/host/key -o Compression=yes -o ConnectTimeout=5 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no exec nc %h %p 2>/dev/null' " )
}
end
end
2015-01-12 07:58:29 +00:00
#
# Special cases related to the Vagrant Host operating system in use
#
2015-11-17 07:00:24 +00:00
context " on a Windows host " do
before do
Vagrant :: Util :: Platform . stub ( windows? : true )
machine . ui . stub ( :warn )
end
it " warns that Windows is not officially supported for the Ansible control machine " do
expect ( machine . env . ui ) . to receive ( :warn ) . with { | warning |
expect ( warning ) . to eq ( I18n . t ( " vagrant.provisioners.ansible.windows_not_supported_for_control_machine " ) )
}
end
end
context " on a Solaris-like host " do
2015-01-12 07:58:29 +00:00
before do
Vagrant :: Util :: Platform . stub ( solaris? : true )
end
it " does not set IdentitiesOnly=yes in ANSIBLE_SSH_ARGS " do
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
cmd_opts = args . last
expect ( cmd_opts [ :env ] [ 'ANSIBLE_SSH_ARGS' ] ) . to_not include ( " -o IdentitiesOnly=yes " )
2015-02-10 14:28:00 +00:00
# Ending this block with a negative expectation (to_not / not_to)
# would lead to a failure of the above expectation.
true
2015-01-12 07:58:29 +00:00
}
end
describe " and with host_key_checking option enabled " do
it " does not set ANSIBLE_SSH_ARGS environment variable " do
config . host_key_checking = true
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
cmd_opts = args . last
expect ( cmd_opts [ :env ] ) . to_not include ( 'ANSIBLE_SSH_ARGS' )
2015-02-10 14:28:00 +00:00
# Ending this block with a negative expectation (to_not / not_to)
# would lead to a failure of the above expectation.
true
2015-01-12 07:58:29 +00:00
}
end
end
end
2017-04-11 11:40:14 +00:00
describe 'with config.ssh.keys_only = false' do
it 'does not set IdentitiesOnly=yes in ANSIBLE_SSH_ARGS' do
ssh_info [ :keys_only ] = false
expect ( Vagrant :: Util :: Subprocess ) . to receive ( :execute ) . with { | * args |
cmd_opts = args . last
expect ( cmd_opts [ :env ] [ 'ANSIBLE_SSH_ARGS' ] ) . to_not include ( " -o IdentitiesOnly=yes " )
# Ending this block with a negative expectation (to_not / not_to)
# would lead to a failure of the above expectation.
true
}
end
end
2014-03-21 11:02:23 +00:00
end
end