Commit b18ced5b authored by Ilias Tsitsimpis's avatar Ilias Tsitsimpis

ci: Implement file lock mechanism for temp_config

Before writing to temp config file, acquire a lock
and re-read it's contents to make sure we don't
overwrite anything.
parent 0b6ccf4b
# filelocker.py - Cross-platform (posix/nt) API for flock-style file locking.
# Requires python 1.5.2 or better.
"""Cross-platform (posix/nt) API for flock-style file locking.
Synopsis:
import filelocker
with filelocker.lock("lockfile", filelocker.LOCK_EX):
print "Got it"
Methods:
lock ( file, flags, tries=10 )
Constants:
LOCK_EX
LOCK_SH
LOCK_NB
Exceptions:
LockException
Notes:
For the 'nt' platform, this module requires the Python Extensions for Windows.
Be aware that this may not work as expected on Windows 95/98/ME.
History:
I learned the win32 technique for locking files from sample code
provided by John Nielsen <nielsenjf@my-deja.com> in the documentation
that accompanies the win32 modules.
Author: Jonathan Feinberg <jdf@pobox.com>,
Lowell Alleman <lalleman@mfps.com>
Version: $Id: filelocker.py 5474 2008-05-16 20:53:50Z lowell $
Modified to work as a contextmanager
"""
import os
from contextlib import contextmanager
class LockException(Exception):
# Error codes:
LOCK_FAILED = 1
# Import modules for each supported platform
if os.name == 'nt':
import win32con
import win32file
import pywintypes
LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK
LOCK_SH = 0 # the default
LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
# is there any reason not to reuse the following structure?
__overlapped = pywintypes.OVERLAPPED()
elif os.name == 'posix':
import fcntl
LOCK_EX = fcntl.LOCK_EX
LOCK_SH = fcntl.LOCK_SH
LOCK_NB = fcntl.LOCK_NB
else:
raise RuntimeError("FileLocker only defined for nt and posix platforms")
# --------------------------------------
# Implementation for NT
if os.name == 'nt':
@contextmanager
def lock(filename, flags):
file = open(filename, "w+")
hfile = win32file._get_osfhandle(file.fileno())
try:
win32file.LockFileEx(hfile, flags, 0, -0x10000, __overlapped)
try:
yield
finally:
file.close()
except pywintypes.error, exc_value:
# error: (33, 'LockFileEx',
# 'The process cannot access the file because another
# process has locked a portion of the file.')
file.close()
if exc_value[0] == 33:
raise LockException(LockException.LOCK_FAILED, exc_value[2])
else:
# Q: Are there exceptions/codes we should be dealing with?
raise
# --------------------------------------
# Implementation for Posix
elif os.name == 'posix':
@contextmanager
def lock(filename, flags):
file = open(filename, "w+")
try:
fcntl.flock(file.fileno(), flags)
try:
yield
finally:
file.close()
except IOError, exc_value:
# IOError: [Errno 11] Resource temporarily unavailable
file.close()
if exc_value[0] == 11:
raise LockException(LockException.LOCK_FAILED, exc_value[1])
else:
raise
......@@ -19,6 +19,7 @@ from kamaki.clients.astakos import AstakosClient
from kamaki.clients.cyclades import CycladesClient
from kamaki.clients.image import ImageClient
from kamaki.clients.compute import ComputeClient
import filelocker
DEFAULT_CONFIG_FILE = "new_config"
# UUID of owner of system images
......@@ -147,10 +148,11 @@ class SynnefoCI(object):
self.config.read(config_file)
# Read temporary_config file
temp_config = self.config.get('Global', 'temporary_config')
self.temp_config_file = \
os.path.expanduser(self.config.get('Global', 'temporary_config'))
self.temp_config = ConfigParser()
self.temp_config.optionxform = str
self.temp_config.read(os.path.expanduser(temp_config))
self.temp_config.read(self.temp_config_file)
self.build_id = build_id
self.logger.info("Will use \"%s\" as build id" % _green(self.build_id))
......@@ -453,19 +455,27 @@ class SynnefoCI(object):
def write_temp_config(self, option, value):
"""Write changes back to config file"""
# If build_id section doesn't exist create a new one
try:
self.temp_config.add_section(str(self.build_id))
creation_time = time.strftime("%a, %d %b %Y %X", time.localtime())
self.write_temp_config("created", creation_time)
except DuplicateSectionError:
pass
self.temp_config.set(str(self.build_id), option, str(value))
curr_time = time.strftime("%a, %d %b %Y %X", time.localtime())
self.temp_config.set(str(self.build_id), "modified", curr_time)
temp_conf_file = self.config.get('Global', 'temporary_config')
with open(temp_conf_file, 'wb') as tcf:
self.temp_config.write(tcf)
# Acquire the lock to write to temp_config_file
with filelocker.lock("%s.lock" % self.temp_config_file,
filelocker.LOCK_EX):
# Read temp_config again to get any new entries
self.temp_config.read(self.temp_config_file)
# If build_id section doesn't exist create a new one
try:
self.temp_config.add_section(str(self.build_id))
creation_time = \
time.strftime("%a, %d %b %Y %X", time.localtime())
self.temp_config.set(str(self.build_id),
"created", str(creation_time))
except DuplicateSectionError:
pass
self.temp_config.set(str(self.build_id), option, str(value))
curr_time = time.strftime("%a, %d %b %Y %X", time.localtime())
self.temp_config.set(str(self.build_id), "modified", curr_time)
with open(self.temp_config_file, 'wb') as tcf:
self.temp_config.write(tcf)
def read_temp_config(self, option):
"""Read from temporary_config file"""
......@@ -545,6 +555,7 @@ class SynnefoCI(object):
# namely to disable host checking.
(temp_ssh_file_handle, temp_ssh_file) = tempfile.mkstemp()
os.close(temp_ssh_file_handle)
# XXX: git push doesn't read the password
cmd = """
echo 'exec ssh -o "StrictHostKeyChecking no" \
-o "UserKnownHostsFile /dev/null" \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment