mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
* fix race condition between module and its action plugin
See https://github.com/ansible-collections/community.general/issues/1136.
Also remove irrelevant/unneeded display.v() and display.warning() around
connection reset.
* do not check for cookie if not in async mode
* add changelog fragment
(cherry picked from commit 3bc31f286e
)
Co-authored-by: quidame <quidame@poivron.org>
This commit is contained in:
parent
c826a81b40
commit
7da1f3ffea
3 changed files with 30 additions and 5 deletions
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
bugfixes:
|
||||||
|
- iptables_state - fix race condition between module and its action plugin
|
||||||
|
(https://github.com/ansible-collections/community.general/issues/1136).
|
|
@ -125,15 +125,18 @@ class ActionModule(ActionBase):
|
||||||
module_args['_back'] = '%s/iptables.state' % async_dir
|
module_args['_back'] = '%s/iptables.state' % async_dir
|
||||||
async_status_args = dict(_async_dir=async_dir)
|
async_status_args = dict(_async_dir=async_dir)
|
||||||
confirm_cmd = 'rm -f %s' % module_args['_back']
|
confirm_cmd = 'rm -f %s' % module_args['_back']
|
||||||
|
starter_cmd = 'touch %s.starter' % module_args['_back']
|
||||||
remaining_time = max(task_async, max_timeout)
|
remaining_time = max(task_async, max_timeout)
|
||||||
|
|
||||||
# do work!
|
# do work!
|
||||||
result = merge_hash(result, self._execute_module(module_args=module_args, task_vars=task_vars, wrap_async=wrap_async))
|
result = merge_hash(result, self._execute_module(module_args=module_args, task_vars=task_vars, wrap_async=wrap_async))
|
||||||
|
|
||||||
# Then the 3-steps "go ahead or rollback":
|
# Then the 3-steps "go ahead or rollback":
|
||||||
# - reset connection to ensure a persistent one will not be reused
|
# 1. Catch early errors of the module (in asynchronous task) if any.
|
||||||
# - confirm the restored state by removing the backup on the remote
|
# Touch a file on the target to signal the module to process now.
|
||||||
# - retrieve the results of the asynchronous task to return them
|
# 2. Reset connection to ensure a persistent one will not be reused.
|
||||||
|
# 3. Confirm the restored state by removing the backup on the remote.
|
||||||
|
# Retrieve the results of the asynchronous task to return them.
|
||||||
if '_back' in module_args:
|
if '_back' in module_args:
|
||||||
async_status_args['jid'] = result.get('ansible_job_id', None)
|
async_status_args['jid'] = result.get('ansible_job_id', None)
|
||||||
if async_status_args['jid'] is None:
|
if async_status_args['jid'] is None:
|
||||||
|
@ -143,12 +146,18 @@ class ActionModule(ActionBase):
|
||||||
# option type/value, missing required system command, etc.
|
# option type/value, missing required system command, etc.
|
||||||
result = merge_hash(result, self._async_result(async_status_args, task_vars, 0))
|
result = merge_hash(result, self._async_result(async_status_args, task_vars, 0))
|
||||||
|
|
||||||
|
# The module is aware to not process the main iptables-restore
|
||||||
|
# command before finding (and deleting) the 'starter' cookie on
|
||||||
|
# the host, so the previous query will not reach ssh timeout.
|
||||||
|
garbage = self._low_level_execute_command(starter_cmd, sudoable=self.DEFAULT_SUDOABLE)
|
||||||
|
|
||||||
|
# As the main command is not yet executed on the target, here
|
||||||
|
# 'finished' means 'failed before main command be executed'.
|
||||||
if not result['finished']:
|
if not result['finished']:
|
||||||
try:
|
try:
|
||||||
self._connection.reset()
|
self._connection.reset()
|
||||||
display.v("%s: reset connection" % (module_name))
|
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
display.warning("Connection plugin does not allow to reset the connection.")
|
pass
|
||||||
|
|
||||||
for x in range(max_timeout):
|
for x in range(max_timeout):
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
|
@ -551,6 +551,18 @@ def main():
|
||||||
restored_state = state_to_restore
|
restored_state = state_to_restore
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
# Let time enough to the plugin to retrieve async status of the module
|
||||||
|
# in case of bad option type/value and the like.
|
||||||
|
if _back is not None:
|
||||||
|
b_starter = to_bytes('%s.starter' % _back, errors='surrogate_or_strict')
|
||||||
|
while True:
|
||||||
|
if os.path.exists(b_starter):
|
||||||
|
os.remove(b_starter)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
time.sleep(0.01)
|
||||||
|
continue
|
||||||
|
|
||||||
(rc, stdout, stderr) = module.run_command(MAINCOMMAND)
|
(rc, stdout, stderr) = module.run_command(MAINCOMMAND)
|
||||||
if 'Another app is currently holding the xtables lock' in stderr:
|
if 'Another app is currently holding the xtables lock' in stderr:
|
||||||
module.fail_json(
|
module.fail_json(
|
||||||
|
|
Loading…
Reference in a new issue