mirror of
https://github.com/ansible/awx.git
synced 2026-05-11 19:37:38 -02:30
Merge pull request #6002 from chrismeyersfsu/fix-5984
lock projects on project sync
This commit is contained in:
@@ -208,6 +208,12 @@ class ProjectOptions(models.Model):
|
|||||||
results.append(smart_text(playbook))
|
results.append(smart_text(playbook))
|
||||||
return sorted(results, key=lambda x: smart_str(x).lower())
|
return sorted(results, key=lambda x: smart_str(x).lower())
|
||||||
|
|
||||||
|
def get_lock_file(self):
|
||||||
|
proj_path = self.get_project_path()
|
||||||
|
if not proj_path:
|
||||||
|
return None
|
||||||
|
return proj_path + '.lock'
|
||||||
|
|
||||||
|
|
||||||
class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin):
|
class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin):
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import uuid
|
|||||||
from distutils.version import LooseVersion as Version
|
from distutils.version import LooseVersion as Version
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import yaml
|
import yaml
|
||||||
|
import fcntl
|
||||||
try:
|
try:
|
||||||
import psutil
|
import psutil
|
||||||
except:
|
except:
|
||||||
@@ -1328,7 +1329,46 @@ class RunProjectUpdate(BaseTask):
|
|||||||
instance_actual.save()
|
instance_actual.save()
|
||||||
return OutputEventFilter(stdout_handle, raw_callback=raw_callback)
|
return OutputEventFilter(stdout_handle, raw_callback=raw_callback)
|
||||||
|
|
||||||
|
def release_lock(self, instance):
|
||||||
|
try:
|
||||||
|
fcntl.flock(self.lock_fd, fcntl.LOCK_UN)
|
||||||
|
except IOError as e:
|
||||||
|
logger.error("I/O error({0}) while trying to open lock file [{1}]: {2}".format(e.errno, instance.get_lock_file(), e.strerror))
|
||||||
|
os.close(self.lock_fd)
|
||||||
|
raise
|
||||||
|
|
||||||
|
os.close(self.lock_fd)
|
||||||
|
self.lock_fd = None
|
||||||
|
|
||||||
|
'''
|
||||||
|
Note: We don't support blocking=False
|
||||||
|
'''
|
||||||
|
def acquire_lock(self, instance, blocking=True):
|
||||||
|
lock_path = instance.get_lock_file()
|
||||||
|
if lock_path is None:
|
||||||
|
raise RuntimeError(u'Invalid lock file path')
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.lock_fd = os.open(lock_path, os.O_RDONLY | os.O_CREAT)
|
||||||
|
except OSError as e:
|
||||||
|
logger.error("I/O error({0}) while trying to open lock file [{1}]: {2}".format(e.errno, lock_path, e.strerror))
|
||||||
|
raise
|
||||||
|
|
||||||
|
try:
|
||||||
|
fcntl.flock(self.lock_fd, fcntl.LOCK_EX)
|
||||||
|
except IOError as e:
|
||||||
|
os.close(self.lock_fd)
|
||||||
|
logger.error("I/O error({0}) while trying to aquire lock on file [{1}]: {2}".format(e.errno, lock_path, e.strerror))
|
||||||
|
raise
|
||||||
|
|
||||||
|
def pre_run_hook(self, instance, **kwargs):
|
||||||
|
if instance.launch_type == 'sync':
|
||||||
|
self.acquire_lock(instance)
|
||||||
|
|
||||||
def post_run_hook(self, instance, status, **kwargs):
|
def post_run_hook(self, instance, status, **kwargs):
|
||||||
|
if instance.launch_type == 'sync':
|
||||||
|
self.release_lock(instance)
|
||||||
|
|
||||||
if instance.job_type == 'check' and status not in ('failed', 'canceled',):
|
if instance.job_type == 'check' and status not in ('failed', 'canceled',):
|
||||||
p = instance.project
|
p = instance.project
|
||||||
fd = open(self.revision_path, 'r')
|
fd = open(self.revision_path, 'r')
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
import os
|
||||||
|
import fcntl
|
||||||
import pytest
|
import pytest
|
||||||
|
import mock
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from awx.main.models import (
|
from awx.main.models import (
|
||||||
@@ -143,3 +146,63 @@ def test_openstack_client_config_generation_with_private_source_vars(mocker, sou
|
|||||||
'private': expected
|
'private': expected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_os_open_oserror():
|
||||||
|
with pytest.raises(OSError):
|
||||||
|
os.open('this_file_does_not_exist', os.O_RDONLY)
|
||||||
|
|
||||||
|
|
||||||
|
def test_fcntl_ioerror():
|
||||||
|
with pytest.raises(IOError):
|
||||||
|
fcntl.flock(99999, fcntl.LOCK_EX)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch('os.open')
|
||||||
|
@mock.patch('logging.getLogger')
|
||||||
|
def test_aquire_lock_open_fail_logged(logging_getLogger, os_open):
|
||||||
|
err = OSError()
|
||||||
|
err.errno = 3
|
||||||
|
err.strerror = 'dummy message'
|
||||||
|
|
||||||
|
instance = mock.Mock()
|
||||||
|
instance.get_lock_file.return_value = 'this_file_does_not_exist'
|
||||||
|
|
||||||
|
os_open.side_effect = err
|
||||||
|
|
||||||
|
logger = mock.Mock()
|
||||||
|
logging_getLogger.return_value = logger
|
||||||
|
|
||||||
|
ProjectUpdate = tasks.RunProjectUpdate()
|
||||||
|
|
||||||
|
with pytest.raises(OSError, errno=3, strerror='dummy message'):
|
||||||
|
ProjectUpdate.acquire_lock(instance)
|
||||||
|
assert logger.err.called_with("I/O error({0}) while trying to open lock file [{1}]: {2}".format(3, 'this_file_does_not_exist', 'dummy message'))
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch('os.open')
|
||||||
|
@mock.patch('os.close')
|
||||||
|
@mock.patch('logging.getLogger')
|
||||||
|
@mock.patch('fcntl.flock')
|
||||||
|
def test_aquire_lock_acquisition_fail_logged(fcntl_flock, logging_getLogger, os_close, os_open):
|
||||||
|
err = IOError()
|
||||||
|
err.errno = 3
|
||||||
|
err.strerror = 'dummy message'
|
||||||
|
|
||||||
|
instance = mock.Mock()
|
||||||
|
instance.get_lock_file.return_value = 'this_file_does_not_exist'
|
||||||
|
|
||||||
|
os_open.return_value = 3
|
||||||
|
|
||||||
|
logger = mock.Mock()
|
||||||
|
logging_getLogger.return_value = logger
|
||||||
|
|
||||||
|
fcntl_flock.side_effect = err
|
||||||
|
|
||||||
|
ProjectUpdate = tasks.RunProjectUpdate()
|
||||||
|
|
||||||
|
with pytest.raises(IOError, errno=3, strerror='dummy message'):
|
||||||
|
ProjectUpdate.acquire_lock(instance)
|
||||||
|
os_close.assert_called_with(3)
|
||||||
|
assert logger.err.called_with("I/O error({0}) while trying to aquire lock on file [{1}]: {2}".format(3, 'this_file_does_not_exist', 'dummy message'))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user