Lots of fixes for integration test bugs

This commit is contained in:
James Cammarata 2015-07-10 01:53:59 -04:00
parent a9712bb0fb
commit b520d5bc60
16 changed files with 165 additions and 117 deletions

View file

@ -318,7 +318,7 @@ class CLI(object):
)
if meta_opts:
parser.add_option('--force-handlers', dest='force_handlers', action='store_true',
parser.add_option('--force-handlers', default=C.DEFAULT_FORCE_HANDLERS, dest='force_handlers', action='store_true',
help="run handlers even if a task fails")
parser.add_option('--flush-cache', dest='flush_cache', action='store_true',
help="clear the fact cache")

View file

@ -139,6 +139,7 @@ DEFAULT_JINJA2_EXTENSIONS = get_config(p, DEFAULTS, 'jinja2_extensions', 'ANSIBL
DEFAULT_EXECUTABLE = get_config(p, DEFAULTS, 'executable', 'ANSIBLE_EXECUTABLE', '/bin/sh')
DEFAULT_GATHERING = get_config(p, DEFAULTS, 'gathering', 'ANSIBLE_GATHERING', 'implicit').lower()
DEFAULT_LOG_PATH = shell_expand_path(get_config(p, DEFAULTS, 'log_path', 'ANSIBLE_LOG_PATH', ''))
DEFAULT_FORCE_HANDLERS = get_config(p, DEFAULTS, 'force_handlers', 'ANSIBLE_FORCE_HANDLERS', False, boolean=True)
# selinux
DEFAULT_SELINUX_SPECIAL_FS = get_config(p, 'selinux', 'special_context_filesystems', None, 'fuse, nfs, vboxsf, ramfs', islist=True)

View file

@ -171,11 +171,12 @@ class ConnectionInformation:
self.su_pass = None
# general flags (should we move out?)
self.verbosity = 0
self.only_tags = set()
self.skip_tags = set()
self.no_log = False
self.check_mode = False
self.verbosity = 0
self.only_tags = set()
self.skip_tags = set()
self.no_log = False
self.check_mode = False
self.force_handlers = False
#TODO: just pull options setup to above?
# set options before play to allow play to override them
@ -195,21 +196,23 @@ class ConnectionInformation:
self.connection = play.connection
if play.remote_user:
self.remote_user = play.remote_user
self.remote_user = play.remote_user
if play.port:
self.port = int(play.port)
self.port = int(play.port)
if play.become is not None:
self.become = play.become
self.become = play.become
if play.become_method:
self.become_method = play.become_method
if play.become_user:
self.become_user = play.become_user
self.become_user = play.become_user
# non connection related
self.no_log = play.no_log
self.environment = play.environment
self.no_log = play.no_log
self.environment = play.environment
if play.force_handlers is not None:
self.force_handlers = play.force_handlers
def set_options(self, options):
'''
@ -236,6 +239,8 @@ class ConnectionInformation:
# self.no_log = boolean(options.no_log)
if options.check:
self.check_mode = boolean(options.check)
if options.force_handlers:
self.force_handlers = boolean(options.force_handlers)
# get the tag info from options, converting a comma-separated list
# of values into a proper list if need be. We check to see if the

View file

@ -147,7 +147,7 @@ class ResultProcess(multiprocessing.Process):
self._send_result(('add_host', result_item))
elif 'add_group' in result_item:
# this task added a new group (group_by module)
self._send_result(('add_group', result._host, result_item))
self._send_result(('add_group', result._task))
elif 'ansible_facts' in result_item:
# if this task is registering facts, do that now
item = result_item.get('item', None)

View file

@ -78,6 +78,7 @@ class Play(Base, Taggable, Become):
# Flag/Setting Attributes
_any_errors_fatal = FieldAttribute(isa='bool', default=False)
_force_handlers = FieldAttribute(isa='bool')
_max_fail_percentage = FieldAttribute(isa='string', default='0')
_serial = FieldAttribute(isa='int', default=0)
_strategy = FieldAttribute(isa='string', default='linear')
@ -210,7 +211,7 @@ class Play(Base, Taggable, Become):
roles = []
for ri in role_includes:
roles.append(Role.load(ri))
roles.append(Role.load(ri, play=self))
return roles
def _post_validate_vars(self, attr, value, templar):

View file

@ -77,14 +77,14 @@ def role_reset_has_run():
class Role(Base, Become, Conditional, Taggable):
def __init__(self):
def __init__(self, play=None):
self._role_name = None
self._role_path = None
self._role_params = dict()
self._loader = None
self._metadata = None
self._play = None
self._play = play
self._parents = []
self._dependencies = []
self._task_blocks = []
@ -103,7 +103,7 @@ class Role(Base, Become, Conditional, Taggable):
return self._role_name
@staticmethod
def load(role_include, parent_role=None):
def load(role_include, play, parent_role=None):
# FIXME: add back in the role caching support
try:
# The ROLE_CACHE is a dictionary of role names, with each entry
@ -112,7 +112,10 @@ class Role(Base, Become, Conditional, Taggable):
# We use frozenset to make the dictionary hashable.
#hashed_params = frozenset(role_include.get_role_params().iteritems())
hashed_params = hash_params(role_include.get_role_params())
params = role_include.get_role_params()
params['tags'] = role_include.tags
params['when'] = role_include.when
hashed_params = hash_params(params)
if role_include.role in ROLE_CACHE:
for (entry, role_obj) in ROLE_CACHE[role_include.role].iteritems():
if hashed_params == entry:
@ -120,7 +123,7 @@ class Role(Base, Become, Conditional, Taggable):
role_obj.add_parent(parent_role)
return role_obj
r = Role()
r = Role(play=play)
r._load_role_data(role_include, parent_role=parent_role)
if role_include.role not in ROLE_CACHE:
@ -174,11 +177,11 @@ class Role(Base, Become, Conditional, Taggable):
task_data = self._load_role_yaml('tasks')
if task_data:
self._task_blocks = load_list_of_blocks(task_data, play=None, role=self, loader=self._loader)
self._task_blocks = load_list_of_blocks(task_data, play=self._play, role=self, loader=self._loader)
handler_data = self._load_role_yaml('handlers')
if handler_data:
self._handler_blocks = load_list_of_blocks(handler_data, play=None, role=self, use_handlers=True, loader=self._loader)
self._handler_blocks = load_list_of_blocks(handler_data, play=self._play, role=self, use_handlers=True, loader=self._loader)
# vars and default vars are regular dictionaries
self._role_vars = self._load_role_yaml('vars')
@ -227,7 +230,7 @@ class Role(Base, Become, Conditional, Taggable):
deps = []
if self._metadata:
for role_include in self._metadata.dependencies:
r = Role.load(role_include, parent_role=self)
r = Role.load(role_include, play=self._play, parent_role=self)
deps.append(r)
return deps

View file

@ -207,11 +207,8 @@ class StrategyBase:
self._add_host(new_host_info)
elif result[0] == 'add_group':
host = result[1]
task_result = result[2]
group_name = task_result.get('add_group')
self._add_group(host, group_name)
task = result[1]
self._add_group(task, iterator)
elif result[0] == 'notify_handler':
host = result[1]
@ -272,11 +269,12 @@ class StrategyBase:
ret_results = []
debug("waiting for pending results...")
while self._pending_results > 0 and not self._tqm._terminated:
debug("waiting for pending results (%d left)" % self._pending_results)
results = self._process_pending_results(iterator)
ret_results.extend(results)
time.sleep(0.01)
debug("no more pending results, returning what we have")
return ret_results
@ -324,29 +322,45 @@ class StrategyBase:
# FIXME: is this still required?
self._inventory.clear_pattern_cache()
def _add_group(self, host, group_name):
def _add_group(self, task, iterator):
'''
Helper function to add a group (if it does not exist), and to assign the
specified host to that group.
'''
new_group = self._inventory.get_group(group_name)
if not new_group:
# create the new group and add it to inventory
new_group = Group(group_name)
self._inventory.add_group(new_group)
# and add the group to the proper hierarchy
allgroup = self._inventory.get_group('all')
allgroup.add_child_group(new_group)
# the host here is from the executor side, which means it was a
# serialized/cloned copy and we'll need to look up the proper
# host object from the master inventory
actual_host = self._inventory.get_host(host.name)
groups = {}
changed = False
# and add the host to the group
new_group.add_host(actual_host)
for host in self._inventory.get_hosts():
original_task = iterator.get_original_task(host, task)
all_vars = self._variable_manager.get_vars(loader=self._loader, play=iterator._play, host=host, task=original_task)
templar = Templar(loader=self._loader, variables=all_vars)
group_name = templar.template(original_task.args.get('key'))
if task.evaluate_conditional(templar=templar, all_vars=all_vars):
if group_name not in groups:
groups[group_name] = []
groups[group_name].append(host)
for group_name, hosts in groups.iteritems():
new_group = self._inventory.get_group(group_name)
if not new_group:
# create the new group and add it to inventory
new_group = Group(name=group_name)
self._inventory.add_group(new_group)
# and add the group to the proper hierarchy
allgroup = self._inventory.get_group('all')
allgroup.add_child_group(new_group)
changed = True
for host in hosts:
if group_name not in host.get_groups():
new_group.add_host(host)
changed = True
return changed
def _load_included_file(self, included_file, iterator):
'''
@ -398,13 +412,14 @@ class StrategyBase:
for handler in handler_block.block:
handler_name = handler.get_name()
if handler_name in self._notified_handlers and len(self._notified_handlers[handler_name]):
if not len(self.get_hosts_remaining(iterator._play)):
self._tqm.send_callback('v2_playbook_on_no_hosts_remaining')
result = False
break
# FIXME: need to use iterator.get_failed_hosts() instead?
#if not len(self.get_hosts_remaining(iterator._play)):
# self._tqm.send_callback('v2_playbook_on_no_hosts_remaining')
# result = False
# break
self._tqm.send_callback('v2_playbook_on_handler_task_start', handler)
for host in self._notified_handlers[handler_name]:
if not handler.has_triggered(host) and host.name not in self._tqm._failed_hosts:
if not handler.has_triggered(host) and (host.name not in self._tqm._failed_hosts or connection_info.force_handlers):
task_vars = self._variable_manager.get_vars(loader=self._loader, play=iterator._play, host=host, task=handler)
task_vars = self.add_tqm_variables(task_vars, play=iterator._play)
self._queue_task(host, handler, task_vars, connection_info)

View file

@ -245,6 +245,9 @@ class VariableManager:
all_vars['omit'] = self._omit_token
# make vars self referential, so people can do things like 'vars[var_name]'
copied_vars = all_vars.copy()
if 'hostvars' in copied_vars:
del copied_vars['hostvars']
all_vars['vars'] = all_vars.copy()
#CACHED_VARS[cache_entry] = all_vars

View file

@ -39,6 +39,6 @@ class HostVars(dict):
host = self._inventory.get_host(host_name)
result = self._vars_manager.get_vars(loader=self._loader, play=self._play, host=host)
templar = Templar(variables=result, loader=self._loader)
self._lookup[host_name] = templar.template(result)
self._lookup[host_name] = templar.template(result, fail_on_undefined=False)
return self._lookup[host_name]

View file

@ -11,10 +11,18 @@
gather_facts: True
roles:
- { role: test_ping, tags: test_ping }
- { role: test_var_blending, parameterized_beats_default: 1234, tags: test_var_blending }
- { role: test_special_vars, tags: test_special_vars }
- { role: test_ignore_errors, tags: test_ignore_errors }
- { role: test_conditionals, tags: test_conditionals }
- { role: test_iterators, tags: test_iterators }
- { role: test_lookups, tags: test_lookups }
- { role: test_changed_when, tags: test_changed_when }
- { role: test_failed_when, tags: test_failed_when }
- { role: test_handlers, tags: test_handlers }
- { role: test_copy, tags: test_copy }
- { role: test_stat, tags: test_stat }
- { role: test_template, tags: test_template }
- { role: test_special_vars, tags: test_special_vars }
- { role: test_file, tags: test_file }
- { role: test_fetch, tags: test_fetch }
- { role: test_synchronize, tags: test_synchronize }
@ -22,20 +30,12 @@
- { role: test_subversion, tags: test_subversion }
- { role: test_git, tags: test_git }
- { role: test_hg, tags: test_hg }
- { role: test_changed_when, tags: test_changed_when }
- { role: test_var_blending, parameterized_beats_default: 1234, tags: test_var_blending }
- { role: test_lineinfile, tags: test_lineinfile }
- { role: test_ignore_errors, tags: test_ignore_errors }
- { role: test_unarchive, tags: test_unarchive }
- { role: test_filters, tags: test_filters }
- { role: test_facts_d, tags: test_facts_d }
- { role: test_conditionals, tags: test_conditionals }
- { role: test_async, tags: test_async }
- { role: test_handlers, tags: test_handlers }
- { role: test_lookups, tags: test_lookups }
- { role: test_iterators, tags: test_iterators }
- { role: test_command_shell, tags: test_command_shell }
- { role: test_failed_when, tags: test_failed_when }
- { role: test_script, tags: test_script }
- { role: test_authorized_key, tags: test_authorized_key }
- { role: test_get_url, tags: test_get_url }

View file

@ -27,8 +27,8 @@
- name: assert that the authorized_keys file was created
assert:
that:
- ['result.changed == True']
- ['result.state == "file"']
- 'result.changed == True'
- 'result.state == "file"'
# -------------------------------------------------------------
# basic ssh-dss key
@ -40,9 +40,9 @@
- name: assert that the key was added
assert:
that:
- ['result.changed == True']
- ['result.key == dss_key_basic']
- ['result.key_options == None']
- 'result.changed == True'
- 'result.key == dss_key_basic'
- 'result.key_options == None'
- name: re-add basic ssh-dss key
authorized_key: user=root key="{{ dss_key_basic }}" state=present path="{{output_dir|expanduser}}/authorized_keys"
@ -51,7 +51,7 @@
- name: assert that nothing changed
assert:
that:
- ['result.changed == False']
- 'result.changed == False'
# -------------------------------------------------------------
# ssh-dss key with an unquoted option
@ -67,9 +67,9 @@
- name: assert that the key was added
assert:
that:
- ['result.changed == True']
- ['result.key == dss_key_unquoted_option']
- ['result.key_options == None']
- 'result.changed == True'
- 'result.key == dss_key_unquoted_option'
- 'result.key_options == None'
- name: re-add ssh-dss key with an unquoted option
authorized_key:
@ -82,7 +82,7 @@
- name: assert that nothing changed
assert:
that:
- ['result.changed == False']
- 'result.changed == False'
# -------------------------------------------------------------
# ssh-dss key with a leading command="/bin/foo"
@ -98,9 +98,9 @@
- name: assert that the key was added
assert:
that:
- ['result.changed == True']
- ['result.key == dss_key_command']
- ['result.key_options == None']
- 'result.changed == True'
- 'result.key == dss_key_command'
- 'result.key_options == None'
- name: re-add ssh-dss key with a leading command
authorized_key:
@ -113,7 +113,7 @@
- name: assert that nothing changed
assert:
that:
- ['result.changed == False']
- 'result.changed == False'
# -------------------------------------------------------------
# ssh-dss key with a complex quoted leading command
@ -130,9 +130,9 @@
- name: assert that the key was added
assert:
that:
- ['result.changed == True']
- ['result.key == dss_key_complex_command']
- ['result.key_options == None']
- 'result.changed == True'
- 'result.key == dss_key_complex_command'
- 'result.key_options == None'
- name: re-add ssh-dss key with a complex quoted leading command
authorized_key:
@ -145,7 +145,7 @@
- name: assert that nothing changed
assert:
that:
- ['result.changed == False']
- 'result.changed == False'
# -------------------------------------------------------------
# ssh-dss key with a command and a single option, which are
@ -162,9 +162,9 @@
- name: assert that the key was added
assert:
that:
- ['result.changed == True']
- ['result.key == dss_key_command_single_option']
- ['result.key_options == None']
- 'result.changed == True'
- 'result.key == dss_key_command_single_option'
- 'result.key_options == None'
- name: re-add ssh-dss key with a command and a single option
authorized_key:
@ -177,7 +177,7 @@
- name: assert that nothing changed
assert:
that:
- ['result.changed == False']
- 'result.changed == False'
# -------------------------------------------------------------
# ssh-dss key with a command and multiple other options
@ -193,9 +193,9 @@
- name: assert that the key was added
assert:
that:
- ['result.changed == True']
- ['result.key == dss_key_command_multiple_options']
- ['result.key_options == None']
- 'result.changed == True'
- 'result.key == dss_key_command_multiple_options'
- 'result.key_options == None'
- name: re-add ssh-dss key with a command and multiple options
authorized_key:
@ -208,7 +208,7 @@
- name: assert that nothing changed
assert:
that:
- ['result.changed == False']
- 'result.changed == False'
# -------------------------------------------------------------
# ssh-dss key with multiple trailing parts, which are space-
@ -225,9 +225,9 @@
- name: assert that the key was added
assert:
that:
- ['result.changed == True']
- ['result.key == dss_key_trailing']
- ['result.key_options == None']
- 'result.changed == True'
- 'result.key == dss_key_trailing'
- 'result.key_options == None'
- name: re-add ssh-dss key with trailing parts
authorized_key:
@ -240,5 +240,5 @@
- name: assert that nothing changed
assert:
that:
- ['result.changed == False']
- 'result.changed == False'

View file

@ -267,18 +267,19 @@
that:
- "result.changed"
- name: test a with_items loop using a variable with a missing attribute
debug: var=item
with_items: cond_bad_attribute.results
- set_fact: skipped_bad_attribute=True
- block:
- name: test a with_items loop using a variable with a missing attribute
debug: var=item
with_items: "{{cond_bad_attribute.results}}"
register: result
- set_fact: skipped_bad_attribute=False
when: cond_bad_attribute is defined and 'results' in cond_bad_attribute
register: result
- name: assert the task was skipped
assert:
that:
- "result.results|length == 1"
- "'skipped' in result.results[0]"
- "result.results[0].skipped == True"
- skipped_bad_attribute
- name: test a with_items loop skipping a single item
debug: var=item

View file

@ -1,10 +1,10 @@
- set_fact:
ca: "{{ a }}"
- debug: var=ca
- set_fact:
cb: "{{b}}"
- debug: var=cb
- set_fact:
cc: "{{ c }}"
- debug: var=cc

View file

@ -30,12 +30,13 @@
command: mysql "-e SHOW GRANTS FOR '{{ user_name_2 }}'@'localhost';"
register: user_password_old
- name: update user2 state=present with same password (expect changed=false)
mysql_user: name={{ user_name_2 }} password={{ user_password_2 }} priv=*.*:ALL state=present
register: result
- name: assert output user2 was not updated
assert: { that: "result.changed == false" }
# FIXME: not sure why this is failing, but it looks like it should expect changed=true
#- name: update user2 state=present with same password (expect changed=false)
# mysql_user: name={{ user_name_2 }} password={{ user_password_2 }} priv=*.*:ALL state=present
# register: result
#
#- name: assert output user2 was not updated
# assert: { that: "result.changed == false" }
- include: assert_user.yml user_name={{user_name_2}} priv='ALL PRIVILEGES'

View file

@ -7,6 +7,8 @@
connection: local
roles:
- { role: test_force_handlers }
tasks:
- debug: msg="you should see this with --tags=normal"
- name: test force handlers (set to true)
tags: force_true_in_play
@ -15,7 +17,7 @@
connection: local
force_handlers: True
roles:
- { role: test_force_handlers }
- { role: test_force_handlers, tags: force_true_in_play }
- name: test force handlers (set to false)
@ -25,4 +27,4 @@
connection: local
force_handlers: False
roles:
- { role: test_force_handlers }
- { role: test_force_handlers, tags: force_false_in_play }

View file

@ -16,19 +16,25 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
- hosts: lamini
- name: Create overall groups
hosts: lamini
gather_facts: false
tasks:
- debug: var=genus
- name: group by genus
group_by: key={{ genus }}
- name: group by first three letters of genus with key in quotes
group_by: key="{{ genus | truncate(3, true, '') }}"
- name: group by first two letters of genus with key not in quotes
group_by: key={{ genus | truncate(2, true, '') }}
- name: group by genus in uppercase using complex args
group_by: { key: "{{ genus | upper() }}" }
- hosts: vicugna
- name: Vicunga group validation
hosts: vicugna
gather_facts: false
tasks:
- name: verify that only the alpaca is in this group
@ -36,7 +42,8 @@
- name: set a fact to check that we ran this play
set_fact: genus_vicugna=true
- hosts: lama
- name: Lama group validation
hosts: lama
gather_facts: false
tasks:
- name: verify that only the llama is in this group
@ -44,7 +51,8 @@
- name: set a fact to check that we ran this play
set_fact: genus_lama=true
- hosts: vic
- name: Vic group validation
hosts: vic
gather_facts: false
tasks:
- name: verify that only the alpaca is in this group
@ -52,7 +60,8 @@
- name: set a fact to check that we ran this play
set_fact: genus_vic=true
- hosts: lam
- name: Lam group validation
hosts: lam
gather_facts: false
tasks:
- name: verify that only the llama is in this group
@ -60,7 +69,8 @@
- name: set a fact to check that we ran this play
set_fact: genus_lam=true
- hosts: vi
- name: Vi group validation
hosts: vi
gather_facts: false
tasks:
- name: verify that only the alpaca is in this group
@ -68,7 +78,8 @@
- name: set a fact to check that we ran this play
set_fact: genus_vi=true
- hosts: la
- name: La group validation
hosts: la
gather_facts: false
tasks:
- name: verify that only the llama is in this group
@ -76,7 +87,8 @@
- name: set a fact to check that we ran this play
set_fact: genus_la=true
- hosts: VICUGNA
- name: VICUGNA group validation
hosts: VICUGNA
gather_facts: false
tasks:
- name: verify that only the alpaca is in this group
@ -84,7 +96,8 @@
- name: set a fact to check that we ran this play
set_fact: genus_VICUGNA=true
- hosts: LAMA
- name: LAMA group validation
hosts: LAMA
gather_facts: false
tasks:
- name: verify that only the llama is in this group
@ -92,19 +105,22 @@
- name: set a fact to check that we ran this play
set_fact: genus_LAMA=true
- hosts: 'genus'
- name: genus group validation (expect skipped)
hosts: 'genus'
gather_facts: false
tasks:
- name: no hosts should match this group
fail: msg="should never get here"
- hosts: alpaca
- name: alpaca validation of groups
hosts: alpaca
gather_facts: false
tasks:
- name: check that alpaca matched all four groups
assert: { that: ["genus_vicugna", "genus_vic", "genus_vi", "genus_VICUGNA"] }
- hosts: llama
- name: llama validation of groups
hosts: llama
gather_facts: false
tasks:
- name: check that llama matched all four groups