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.
... | ... | @@ -49,24 +49,22 @@ class Recipe: |
'exclusive.') | ||
self.parts = 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 shared and buildout['buildout'].get('shared-parts', None): | ||
if shared: | ||
shared_part = buildout['buildout'].get('shared-parts', None) | ||
top_location = options.get('top_location', '') | ||
shared = os.path.join(shared_part.strip().rstrip('/'), top_location, name) | ||
if not os.path.exists(shared): | ||
os.makedirs(shared) | ||
self._debug_signature_text = [] | ||
m = md5() | ||
self._signature = Signature('.slapos.recipe.build.signature') | ||
profile_base_location = options.get('_profile_base_location_', '') | ||
for k, v in sorted(options.items()): | ||
if profile_base_location: | ||
v = v.replace(profile_base_location, '${:_profile_base_location_}') | ||
option_signature = ('%r: %r' % (k, v)).encode() | ||
self._debug_signature_text.append(option_signature) | ||
m.update(option_signature) | ||
shared = os.path.join(shared, m.hexdigest()) | ||
self._signature.update(k, v) | ||
shared = os.path.join(shared, self._signature.hexdigest()) | ||
self.parts = shared | ||
self.logger.info('shared directory %s set for %s', shared, name) | ||
else: | ||
... | ... | @@ -99,7 +97,7 @@ class Recipe: |
def install(self): | ||
if self.shared: | ||
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') | ||
return [] | ||
if self.parts is not None: | ||
... | ... | @@ -160,8 +158,7 @@ class Recipe: |
self.options['url'], self.destination) | ||
if self.shared: | ||
with open(os.path.join(self.parts, ".slapos.recipe.build.signature"), 'w') as f: | ||
f.write('\n'.join(self._debug_signature_text)) | ||
self._signature.save(self.parts) | ||
make_read_only_recursively(self.parts) | ||
return [] | ||
if self.parts is not None: | ||
... | ... | @@ -236,3 +233,33 @@ def make_read_only_recursively(path): |
make_read_only(os.path.join(root, dir)) | ||
for file_ in file_list: | ||
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()) |