Commit 0a186771 authored by Yusei Tahara's avatar Yusei Tahara

download, downloadunpacked: Check shared directory is not enough. Shared...

download, downloadunpacked: Check shared directory is not enough. Shared directory may be created before completing installation. Check signature file too.
parent 01a8dc2c
...@@ -30,7 +30,7 @@ import shutil ...@@ -30,7 +30,7 @@ import shutil
import zc.buildout import zc.buildout
import logging import logging
from hashlib import md5 from hashlib import md5
from downloadunpacked import make_read_only_recursively from downloadunpacked import make_read_only_recursively, Signature
class Recipe(object): class Recipe(object):
_parts = None _parts = None
...@@ -44,7 +44,8 @@ class Recipe(object): ...@@ -44,7 +44,8 @@ class Recipe(object):
self._name = name self._name = name
mode = options.get('mode') mode = options.get('mode')
log = logging.getLogger(name) log = logging.getLogger(name)
self._shared = shared = (options.get('shared', '').lower() == 'true') self._shared = shared = ((options.get('shared', '').lower() == 'true') and
buildout['buildout'].get('shared-parts', None))
if mode is not None: if mode is not None:
mode = int(mode, 8) mode = int(mode, 8)
self._mode = mode self._mode = mode
...@@ -54,21 +55,18 @@ class Recipe(object): ...@@ -54,21 +55,18 @@ class Recipe(object):
destination = options.get('destination', None) destination = options.get('destination', None)
if destination is None: if destination is None:
if shared and buildout['buildout'].get('shared-parts', None): if shared:
shared_part = buildout['buildout'].get('shared-parts', None) shared_part = buildout['buildout'].get('shared-parts', None)
shared = os.path.join(shared_part.strip().rstrip('/'), name) shared = os.path.join(shared_part.strip().rstrip('/'), name)
if not os.path.exists(shared): if not os.path.exists(shared):
os.makedirs(shared) os.makedirs(shared)
self._debug_signature_text = [] self._signature = Signature('.slapos.recipe.build.signature')
m = md5()
profile_base_location = options.get('_profile_base_location_', '') profile_base_location = options.get('_profile_base_location_', '')
for k, v in sorted(options.items()): for k, v in sorted(options.items()):
if profile_base_location: if profile_base_location:
v = v.replace(profile_base_location, '${:_profile_base_location_}') v = v.replace(profile_base_location, '${:_profile_base_location_}')
option_signature = ('%r: %r' % (k, v)).encode() self._signature.update(k, v)
self._debug_signature_text.append(option_signature) shared = os.path.join(shared, self._signature.hexdigest())
m.update(option_signature)
shared = os.path.join(shared, m.hexdigest())
self._parts = parts = shared self._parts = parts = shared
log.info('shared directory %s set for %s', shared, name) log.info('shared directory %s set for %s', shared, name)
else: else:
...@@ -87,7 +85,7 @@ class Recipe(object): ...@@ -87,7 +85,7 @@ class Recipe(object):
log = logging.getLogger(self._name) log = logging.getLogger(self._name)
if self._shared: if self._shared:
log.info('Checking whether package is installed at shared path: %s', destination) log.info('Checking whether package is installed at shared path: %s', destination)
if os.path.exists(destination): if self._signature.test(self._parts):
log.info('This shared package has been installed by other package') log.info('This shared package has been installed by other package')
return [] return []
...@@ -109,8 +107,7 @@ class Recipe(object): ...@@ -109,8 +107,7 @@ class Recipe(object):
shutil.copyfileobj(fsrc, fdst) shutil.copyfileobj(fsrc, fdst)
if self._shared: if self._shared:
with open(os.path.join(parts, ".slapos.recipe.build.signature"), 'w') as f: self._signature.save(parts)
f.write('\n'.join(self._debug_signature_text))
make_read_only_recursively(self._parts) make_read_only_recursively(self._parts)
return result return result
......
...@@ -49,24 +49,22 @@ class Recipe: ...@@ -49,24 +49,22 @@ class Recipe:
'exclusive.') 'exclusive.')
self.parts = None self.parts = None
self.destination = self.options.get('destination', None) self.destination = self.options.get('destination', None)
self.shared = shared = is_true(options.get('shared', 'false').lower()) self.shared = shared = (is_true(options.get('shared', 'false').lower()) and
buildout['buildout'].get('shared-parts', None))
if self.destination is None: if self.destination is None:
if shared and buildout['buildout'].get('shared-parts', None): if shared:
shared_part = buildout['buildout'].get('shared-parts', None) shared_part = buildout['buildout'].get('shared-parts', None)
top_location = options.get('top_location', '') top_location = options.get('top_location', '')
shared = os.path.join(shared_part.strip().rstrip('/'), top_location, name) shared = os.path.join(shared_part.strip().rstrip('/'), top_location, name)
if not os.path.exists(shared): if not os.path.exists(shared):
os.makedirs(shared) os.makedirs(shared)
self._debug_signature_text = [] self._signature = Signature('.slapos.recipe.build.signature')
m = md5()
profile_base_location = options.get('_profile_base_location_', '') profile_base_location = options.get('_profile_base_location_', '')
for k, v in sorted(options.items()): for k, v in sorted(options.items()):
if profile_base_location: if profile_base_location:
v = v.replace(profile_base_location, '${:_profile_base_location_}') v = v.replace(profile_base_location, '${:_profile_base_location_}')
option_signature = ('%r: %r' % (k, v)).encode() self._signature.update(k, v)
self._debug_signature_text.append(option_signature) shared = os.path.join(shared, self._signature.hexdigest())
m.update(option_signature)
shared = os.path.join(shared, m.hexdigest())
self.parts = shared self.parts = shared
self.logger.info('shared directory %s set for %s', shared, name) self.logger.info('shared directory %s set for %s', shared, name)
else: else:
...@@ -99,7 +97,7 @@ class Recipe: ...@@ -99,7 +97,7 @@ class Recipe:
def install(self): def install(self):
if self.shared: if self.shared:
self.logger.info('Checking whether package is installed at shared path : %s', self.destination) self.logger.info('Checking whether package is installed at shared path : %s', self.destination)
if os.path.exists(self.destination): if self._signature.test(self.destination):
self.logger.info('This shared package has been installed by other package') self.logger.info('This shared package has been installed by other package')
return [] return []
if self.parts is not None: if self.parts is not None:
...@@ -160,8 +158,7 @@ class Recipe: ...@@ -160,8 +158,7 @@ class Recipe:
self.options['url'], self.destination) self.options['url'], self.destination)
if self.shared: if self.shared:
with open(os.path.join(self.parts, ".slapos.recipe.build.signature"), 'w') as f: self._signature.save(self.parts)
f.write('\n'.join(self._debug_signature_text))
make_read_only_recursively(self.parts) make_read_only_recursively(self.parts)
return [] return []
if self.parts is not None: if self.parts is not None:
...@@ -236,3 +233,33 @@ def make_read_only_recursively(path): ...@@ -236,3 +233,33 @@ def make_read_only_recursively(path):
make_read_only(os.path.join(root, dir)) make_read_only(os.path.join(root, dir))
for file_ in file_list: for file_ in file_list:
make_read_only(os.path.join(root, file_)) make_read_only(os.path.join(root, file_))
class Signature:
def __init__(self, filename):
self.filename = filename
self.item_list = []
def update(self, key, value):
self.item_list.append(('%r: %r' % (key, value)).encode())
def hexdigest(self):
m = md5()
for item in self.item_list:
m.update(item)
return m.hexdigest()
def dumps(self):
return '\n'.join(self.item_list)
def test(self, folder_path):
digest = self.hexdigest()
if os.path.basename(folder_path) == digest:
target_path = os.path.join(folder_path, self.filename)
if os.path.exists(target_path) and open(target_path).read() == self.dumps():
return True
return False
def save(self, folder_path):
with open(os.path.join(folder_path, self.filename), 'w') as f:
f.write(self.dumps())
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