summaryrefslogtreecommitdiff
path: root/morphlib
diff options
context:
space:
mode:
authorRichard Maw <richard.maw@codethink.co.uk>2012-05-14 17:34:50 +0000
committerRichard Maw <richard.maw@codethink.co.uk>2012-05-14 17:34:50 +0000
commit41c647ec20d3e58348535e5d2f76f80e977b47ce (patch)
treec249faf7f1c5c8047213b67619448b605a7d1690 /morphlib
parentcd8170c8f7a6c8c82d43f8b992c07041fa22a31b (diff)
downloadmorph-41c647ec20d3e58348535e5d2f76f80e977b47ce.tar.gz
builder: use the same tar extract logic for all artifacts
Before only the staging area had symbolic links handled, now strata and systems will have the same logic.
Diffstat (limited to 'morphlib')
-rw-r--r--morphlib/bins.py91
-rw-r--r--morphlib/bins_tests.py4
-rw-r--r--morphlib/builder2.py2
-rw-r--r--morphlib/stagingarea.py84
4 files changed, 86 insertions, 95 deletions
diff --git a/morphlib/bins.py b/morphlib/bins.py
index 8fd01182..3947e28d 100644
--- a/morphlib/bins.py
+++ b/morphlib/bins.py
@@ -24,6 +24,7 @@ Binaries are chunks, strata, and system images.
import logging
import os
import re
+import stat
import tarfile
@@ -109,17 +110,87 @@ def unpack_binary_from_file(f, dirname): # pragma: no cover
'''
tf = tarfile.open(fileobj=f)
- tf.extractall(path=dirname)
- tf.close()
-
-
-def unpack_binary(filename, dirname, ex):
- '''Unpack a binary into a directory.
+
+ # This is evil, but necessary. For some reason Python's system
+ # call wrappers (os.mknod and such) do not (always?) set the
+ # filename attribute of the OSError exception they raise. We
+ # fix that by monkey patching the tf instance with wrappers
+ # for the relevant methods to add things. The wrapper further
+ # ignores EEXIST errors, since we do not (currently!) care about
+ # overwriting files.
- The directory must exist already.
+ def follow_symlink(path): # pragma: no cover
+ try:
+ return os.stat(path)
+ except OSError:
+ return None
- '''
+ def prepare_extract(tarinfo, targetpath): # pragma: no cover
+ '''Prepare to extract a tar file member onto targetpath?
+
+ If the target already exist, and we can live with it or
+ remove it, we do so. Otherwise, raise an error.
+
+ It's OK to extract if:
+
+ * the target does not exist
+ * the member is a directory a directory and the
+ target is a directory or a symlink to a directory
+ (just extract, no need to remove)
+ * the member is not a directory, and the target is not a directory
+ or a symlink to a directory (remove target, then extract)
+
+ '''
+
+ try:
+ existing = os.lstat(targetpath)
+ except OSError:
+ return True # target does not exist
+
+ if tarinfo.isdir():
+ if stat.S_ISDIR(existing.st_mode):
+ return True
+ elif stat.S_ISLNK(existing.st_mode):
+ st = follow_symlink(targetpath)
+ return st and stat.S_ISDIR(st.st_mode)
+ else:
+ if stat.S_ISDIR(existing.st_mode):
+ return False
+ elif stat.S_ISLNK(existing.st_mode):
+ st = follow_symlink(targetpath)
+ if st and not stat.S_ISDIR(st.st_mode):
+ os.remove(targetpath)
+ return True
+ else:
+ os.remove(targetpath)
+ return True
+ return False
+
+ def monkey_patcher(real):
+ def make_something(tarinfo, targetpath): # pragma: no cover
+ prepare_extract(tarinfo, targetpath)
+ try:
+ return real(tarinfo, targetpath)
+ except OSError, e:
+ if e.errno != errno.EEXIST:
+ if e.filename is None:
+ e.filename = targetpath
+ raise e
+ else:
+ raise
+ return make_something
+
+ tf.makedir = monkey_patcher(tf.makedir)
+ tf.makefile = monkey_patcher(tf.makefile)
+ tf.makeunknown = monkey_patcher(tf.makeunknown)
+ tf.makefifo = monkey_patcher(tf.makefifo)
+ tf.makedev = monkey_patcher(tf.makedev)
+ tf.makelink = monkey_patcher(tf.makelink)
- logging.debug('Unpacking %s into %s' % (filename, dirname))
- ex.runv(['tar', '-C', dirname, '-xvhf', filename])
+ tf.extractall(path=dirname)
+ tf.close
+def unpack_binary(filename, dirname):
+ f = open(filename, "rb")
+ unpack_binary_from_file(f, dirname)
+ f.close()
diff --git a/morphlib/bins_tests.py b/morphlib/bins_tests.py
index 82ceb246..dd2fb886 100644
--- a/morphlib/bins_tests.py
+++ b/morphlib/bins_tests.py
@@ -113,7 +113,7 @@ class ChunkTests(BinsTest):
def unpack_chunk(self):
os.mkdir(self.unpacked)
- morphlib.bins.unpack_binary(self.chunk_file, self.unpacked, self.ex)
+ morphlib.bins.unpack_binary(self.chunk_file, self.unpacked)
def test_empties_everything(self):
self.create_chunk(['.'])
@@ -158,7 +158,7 @@ class StratumTests(BinsTest):
morphlib.bins.create_stratum(self.instdir, self.stratum_f, self.ex)
self.stratum_f.flush()
os.mkdir(self.unpacked)
- morphlib.bins.unpack_binary(self.stratum_file, self.unpacked, self.ex)
+ morphlib.bins.unpack_binary(self.stratum_file, self.unpacked)
self.assertEqual(self.recursive_lstat(self.instdir),
self.recursive_lstat(self.unpacked))
diff --git a/morphlib/builder2.py b/morphlib/builder2.py
index 2a7ca2d2..62e484f4 100644
--- a/morphlib/builder2.py
+++ b/morphlib/builder2.py
@@ -81,7 +81,7 @@ def check_overlap(artifact, constituents, lac): #pragma: no cover
logging.warning('Overlaps in artifact %s detected' % artifact.name)
for overlapping, files in sorted(overlaps.iteritems()):
logging.warning(' Artifacts %s overlap with files:' %
- ', '.join(a.name for a in sorted(overlapping))
+ ', '.join(sorted(a.name for a in overlapping))
)
for filename in sorted(files):
logging.warning(' %s' % filename)
diff --git a/morphlib/stagingarea.py b/morphlib/stagingarea.py
index 8c84ac36..4f6d76bf 100644
--- a/morphlib/stagingarea.py
+++ b/morphlib/stagingarea.py
@@ -18,8 +18,6 @@ import errno
import logging
import os
import shutil
-import stat
-import tarfile
import morphlib
@@ -91,86 +89,8 @@ class StagingArea(object):
'''
logging.debug('Installing artifact %s' %
- getattr(handle, 'name', 'unknown name'))
- tf = tarfile.open(fileobj=handle)
-
- # This is evil, but necessary. For some reason Python's system
- # call wrappers (os.mknod and such) do not (always?) set the
- # filename attribute of the OSError exception they raise. We
- # fix that by monkey patching the tf instance with wrappers
- # for the relevant methods to add things. The wrapper further
- # ignores EEXIST errors, since we do not (currently!) care about
- # overwriting files.
-
- def follow_symlink(path): # pragma: no cover
- try:
- return os.stat(path)
- except OSError:
- return None
-
- def prepare_extract(tarinfo, targetpath): # pragma: no cover
- '''Prepare to extract a tar file member onto targetpath?
-
- If the target already exist, and we can live with it or
- remove it, we do so. Otherwise, raise an error.
-
- It's OK to extract if:
-
- * the target does not exist
- * the member is a directory a directory and the
- target is a directory or a symlink to a directory
- (just extract, no need to remove)
- * the member is not a directory, and the target is not a directory
- or a symlink to a directory (remove target, then extract)
-
- '''
-
- try:
- existing = os.lstat(targetpath)
- except OSError:
- return True # target does not exist
-
- if tarinfo.isdir():
- if stat.S_ISDIR(existing.st_mode):
- return True
- elif stat.S_ISLNK(existing.st_mode):
- st = follow_symlink(targetpath)
- return st and stat.S_ISDIR(st.st_mode)
- else:
- if stat.S_ISDIR(existing.st_mode):
- return False
- elif stat.S_ISLNK(existing.st_mode):
- st = follow_symlink(targetpath)
- if st and not stat.S_ISDIR(st.st_mode):
- os.remove(targetpath)
- return True
- else:
- os.remove(targetpath)
- return True
- return False
-
- def monkey_patcher(real):
- def make_something(tarinfo, targetpath): # pragma: no cover
- prepare_extract(tarinfo, targetpath)
- try:
- return real(tarinfo, targetpath)
- except OSError, e:
- if e.errno != errno.EEXIST:
- if e.filename is None:
- e.filename = targetpath
- raise e
- else:
- raise
- return make_something
-
- tf.makedir = monkey_patcher(tf.makedir)
- tf.makefile = monkey_patcher(tf.makefile)
- tf.makeunknown = monkey_patcher(tf.makeunknown)
- tf.makefifo = monkey_patcher(tf.makefifo)
- tf.makedev = monkey_patcher(tf.makedev)
- tf.makelink = monkey_patcher(tf.makelink)
-
- tf.extractall(path=self.dirname)
+ getattr(handle, 'name', 'unknown name'))
+ morphlib.bins.unpack_binary_from_file(handle, self.dirname)
def remove(self):
'''Remove the entire staging area.