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
|
||||
async_status_args = dict(_async_dir=async_dir)
|
||||
confirm_cmd = 'rm -f %s' % module_args['_back']
|
||||
starter_cmd = 'touch %s.starter' % module_args['_back']
|
||||
remaining_time = max(task_async, max_timeout)
|
||||
|
||||
# do work!
|
||||
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":
|
||||
# - reset connection to ensure a persistent one will not be reused
|
||||
# - confirm the restored state by removing the backup on the remote
|
||||
# - retrieve the results of the asynchronous task to return them
|
||||
# 1. Catch early errors of the module (in asynchronous task) if any.
|
||||
# Touch a file on the target to signal the module to process now.
|
||||
# 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:
|
||||
async_status_args['jid'] = result.get('ansible_job_id', None)
|
||||
if async_status_args['jid'] is None:
|
||||
|
@ -143,12 +146,18 @@ class ActionModule(ActionBase):
|
|||
# option type/value, missing required system command, etc.
|
||||
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']:
|
||||
try:
|
||||
self._connection.reset()
|
||||
display.v("%s: reset connection" % (module_name))
|
||||
except AttributeError:
|
||||
display.warning("Connection plugin does not allow to reset the connection.")
|
||||
pass
|
||||
|
||||
for x in range(max_timeout):
|
||||
time.sleep(1)
|
||||
|
|
|
@ -551,6 +551,18 @@ def main():
|
|||
restored_state = state_to_restore
|
||||
|
||||
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)
|
||||
if 'Another app is currently holding the xtables lock' in stderr:
|
||||
module.fail_json(
|
||||
|
|
Loading…
Reference in a new issue