Skip to content

Commit 8163e2a

Browse files
committed
Implement new envt. variables approach
We now load native libraries and executables from: 1. an envt. variable path 2. OR a locatin provider plugin 3. OR the PATH or we fail Signed-off-by: Philippe Ombredanne <pombredanne@nexb.com>
1 parent ab707b5 commit 8163e2a

File tree

3 files changed

+71
-49
lines changed

3 files changed

+71
-49
lines changed

src/extractcode/libarchive2.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from commoncode import fileutils
3838
from commoncode import paths
3939
from commoncode import text
40+
from commoncode.system import on_windows
4041

4142
import extractcode
4243
from extractcode import ExtractError
@@ -83,26 +84,41 @@
8384
"""
8485

8586
# keys for plugin-provided locations
86-
EXTRACTCODE_LIBARCHIVE_LIBDIR = 'extractcode.libarchive.libdir'
8787
EXTRACTCODE_LIBARCHIVE_DLL = 'extractcode.libarchive.dll'
8888

89+
EXTRACTCODE_LIBARCHIVE_PATH_ENVVAR = 'EXTRACTCODE_LIBARCHIVE_PATH'
90+
8991

9092
def load_lib():
9193
"""
92-
Return the loaded libarchive shared library object from plugin-provided path.
94+
Return the libarchive shared library object loaded from either:
95+
- an environment variable ``EXTRACTCODE_LIBARCHIVE_PATH``
96+
- a plugin-provided path,
97+
- the system PATH.
98+
Raise an Exception if no libarchive can be found.
9399
"""
94100
from plugincode.location_provider import get_location
95101

96-
dll = get_location(EXTRACTCODE_LIBARCHIVE_DLL)
97-
libdir = get_location(EXTRACTCODE_LIBARCHIVE_LIBDIR)
98-
if not (dll and libdir) or not os.path.isfile(dll) or not os.path.isdir(libdir):
102+
# try the environment first
103+
dll_loc = os.environ.get(EXTRACTCODE_LIBARCHIVE_PATH_ENVVAR)
104+
105+
# try a plugin-provided path second
106+
if not dll_loc:
107+
dll_loc = get_location(EXTRACTCODE_LIBARCHIVE_DLL)
108+
109+
# try the PATH
110+
if not dll_loc:
111+
dll = 'libarchive.dll' if on_windows else 'libarchive.so'
112+
dll_loc = command.find_in_path(dll)
113+
114+
if not dll_loc or not os.path.isfile(dll_loc):
99115
raise Exception(
100116
'CRITICAL: libarchive DLL is not installed. '
101117
'Unable to continue: you need to install a valid extractcode-libarchive '
102-
'plugin with a valid libarchive DLL available.'
118+
'plugin with a valid libarchive DLL available. '
119+
f'OR set the {EXTRACTCODE_LIBARCHIVE_PATH_ENVVAR} environment variable.'
103120
)
104-
return command.load_shared_library(dll, libdir)
105-
121+
return command.load_shared_library(dll_loc)
106122

107123

108124
def set_env_with_tz():

src/extractcode/sevenzip.py

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,11 @@
5656
logging.basicConfig(stream=sys.stdout)
5757
logger.setLevel(logging.DEBUG)
5858

59-
# keys for plugin-provided locations
60-
EXTRACTCODE_7ZIP_LIBDIR = 'extractcode.sevenzip.libdir'
59+
# key of a plugin-provided location
6160
EXTRACTCODE_7ZIP_EXE = 'extractcode.sevenzip.exe'
6261

62+
EXTRACTCODE_7ZIP_PATH_ENVVAR = 'EXTRACTCODE_7Z_PATH'
63+
6364
sevenzip_errors = [
6465
('unsupported method', 'Unsupported archive or broken archive'),
6566
('wrong password', 'Password protected archive, unable to extract'),
@@ -71,22 +72,40 @@
7172
UNKNOWN_ERROR = 'Unknown extraction error'
7273

7374

74-
def get_bin_locations():
75+
def get_command_location(_cache=[]):
7576
"""
76-
Return a tuple of (lib_dir, cmd_loc) for 7zip loaded from plugin-provided path.
77+
Return the location of a 7zip loaded from either:
78+
- an environment variable ``EXTRACTCODE_7Z_PATH``,
79+
- a plugin-provided path,
80+
- the system PATH.
81+
Raise an Exception if no 7Zip command can be found.
7782
"""
83+
if _cache:
84+
return _cache[0]
85+
7886
from plugincode.location_provider import get_location
7987

80-
cmd_loc = get_location(EXTRACTCODE_7ZIP_EXE)
81-
libdir = get_location(EXTRACTCODE_7ZIP_LIBDIR)
82-
if not (cmd_loc and libdir) or not os.path.isfile(cmd_loc) or not os.path.isdir(libdir):
88+
# try the environment first
89+
cmd_loc = os.environ.get(EXTRACTCODE_7ZIP_PATH_ENVVAR)
90+
91+
# try a plugin-provided path second
92+
if not cmd_loc:
93+
cmd_loc = get_location(EXTRACTCODE_7ZIP_EXE)
94+
95+
# try the PATH
96+
if not cmd_loc:
97+
cmd = '7z.exe' if on_windows else '7z'
98+
cmd_loc = command.find_in_path(cmd)
99+
100+
if not cmd_loc or not os.path.isfile(cmd_loc):
83101
raise Exception(
84102
'CRITICAL: 7zip executable is not installed. '
85103
'Unable to continue: you need to install a valid extractcode-7z '
86-
'plugin with a valid executable available.'
104+
'plugin with a valid executable available. '
105+
'OR set the EXTRACTCODE_7ZIP_PATH environment variable.'
87106
)
88-
89-
return libdir, cmd_loc
107+
_cache.append(cmd_loc)
108+
return cmd_loc
90109

91110

92111
def get_7z_errors(stdout, stderr):
@@ -315,12 +334,11 @@ def build_7z_extract_command(location, target_dir, single_entry=None, arch_type=
315334
if single_entry:
316335
args += [shlex_quote(single_entry.path)]
317336

318-
lib_dir, cmd_loc = get_bin_locations()
337+
cmd_loc = get_command_location()
319338

320339
ex_args = dict(
321340
cmd_loc=cmd_loc,
322341
args=args,
323-
lib_dir=lib_dir,
324342
cwd=target_dir,
325343
env=timezone,
326344
)
@@ -387,7 +405,7 @@ def extract_file_by_file(location, target_dir, arch_type='*', skip_symlinks=True
387405
single_entry=entry,
388406
arch_type=arch_type,
389407
)
390-
rc, stdout, stderr = command.execute2(**ex_args)
408+
rc, stdout, stderr = command.execute(**ex_args)
391409

392410
error = get_7z_errors(stdout, stderr)
393411
if error or rc != 0:
@@ -488,7 +506,7 @@ def list_entries(location, arch_type='*'):
488506
abs_location,
489507
]
490508

491-
lib_dir, cmd_loc = get_bin_locations()
509+
cmd_loc = get_command_location()
492510

493511
rc, stdout, stderr = command.execute2(
494512
cmd_loc=cmd_loc,

src/extractcode/vmimage.py

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,6 @@
3838
Works only if libguestfs tool guestfish is in the path.
3939
4040
See https://libguestfs.org/
41-
42-
On Ubuntu, you may face this issue when running guestfish:
43-
44-
- https://bugs.launchpad.net/ubuntu/+source/linux/+bug/759725
45-
- https://bugs.launchpad.net/ubuntu/+source/libguestfs/+bug/1813662
46-
- https://unix.stackexchange.com/a/642914/185837
4741
"""
4842

4943
logger = logging.getLogger(__name__)
@@ -59,9 +53,16 @@
5953
'WARNING: guestfish executable is not installed. '
6054
'Unable to extract virtual machine image: you need to install the '
6155
'guestfish tool from libguestfs and extra FS drivers if needed. '
62-
'See https://libguestfs.org/ for details.'
56+
'See the ExtractCode README.rst and https://libguestfs.org/ for details.'
6357
)
6458

59+
GUESTFISH_KERNEL_NOT_READABLE = (
60+
'''libguestfs requires the kernel executable to be readable.
61+
This is the case by default on most Linux distributions except on Ubuntu.
62+
Please follow the instructions in ExtractCode installation guide to make this happen.
63+
See the ExtractCode README.rst for details.
64+
''')
65+
6566
EXTRACTCODE_GUESTFISH_PATH_ENVVAR = 'EXTRACTCODE_GUESTFISH_PATH'
6667

6768

@@ -89,26 +90,15 @@ def check_linux_kernel_is_readable():
8990
- https://bugzilla.redhat.com/show_bug.cgi?id=1670790
9091
- https://bugs.launchpad.net/ubuntu/+source/libguestfs/+bug/1813662
9192
"""
92-
error = (
93-
'libguestfs requires the kernel executable to be readable. '
94-
'This is the case on most Linux distribution except on Ubuntu.\n'
95-
'Run this command as a temporary fix:\n'
96-
' for k in /boot/vmlinuz-*\n'
97-
' do sudo dpkg-statoverride --add --update root root 0644 /boot/vmlinuz-$(uname -r)\n'
98-
' done\n'
99-
'or:\n'
100-
' sudo chmod +r /boot/vmlinuz-*\n\n',
101-
'For a permanent fix see: '
102-
'https://bugs.launchpad.net/ubuntu/+source/libguestfs/+bug/1813662/comments/21'
103-
)
93+
10494
if on_linux:
10595
kernels = list(pathlib.Path('/boot').glob('vmlinuz-*'))
10696
if not kernels:
107-
raise ExtractErrorFailedToExtract(error)
97+
raise ExtractErrorFailedToExtract(GUESTFISH_KERNEL_NOT_READABLE)
10898
for kern in kernels:
10999
if not os.access(kern, os.R_OK):
110100
raise ExtractErrorFailedToExtract(
111-
f'Unable to read kernel at: {kern}.\n{error}')
101+
f'Unable to read kernel at: {kern}.\n{GUESTFISH_KERNEL_NOT_READABLE}')
112102

113103

114104
@attr.s
@@ -250,7 +240,7 @@ def run_guestfish(self, args, timeout=None):
250240
return as_unicode(stdout)
251241

252242

253-
def extract(location, target_dir, as_tarballs=True):
243+
def extract(location, target_dir, as_tarballs=False):
254244
"""
255245
Extract all files from a guestfish-supported VM image archive file at
256246
location in the target_dir directory. Optionally only extract the
@@ -306,8 +296,7 @@ def extract(location, target_dir, as_tarballs=True):
306296
# we can safely extract this to a root / dir as we have only one partition
307297
partition, _parttype = partitions[0]
308298
if not as_tarballs:
309-
intermediate_dir = fileutils.get_temp_dir(prefix='extractcode-vmimage')
310-
tdir = intermediate_dir
299+
tdir = fileutils.get_temp_dir(prefix='extractcode-vmimage')
311300
else:
312301
tdir = target_dir
313302

@@ -329,8 +318,7 @@ def extract(location, target_dir, as_tarballs=True):
329318
base_name = partition.replace('/', '-')
330319

331320
if not as_tarballs:
332-
intermediate_dir = fileutils.get_temp_dir(prefix='extractcode-vmimage')
333-
tdir = intermediate_dir
321+
tdir = fileutils.get_temp_dir(prefix='extractcode-vmimage')
334322
else:
335323
tdir = target_dir
336324

@@ -344,7 +332,7 @@ def extract(location, target_dir, as_tarballs=True):
344332
fileutils.create_dir(partition_target_dir)
345333
warns = extract_image_tarball(
346334
tarball=target_tarball,
347-
target_dir=target_dir,
335+
target_dir=partition_target_dir,
348336
skip_symlinks=False)
349337
warnings.extend(warns)
350338

0 commit comments

Comments
 (0)