diff --git a/ci/test.py b/ci/test.py index df6d028..7b58548 100755 --- a/ci/test.py +++ b/ci/test.py @@ -582,6 +582,32 @@ def test_bad_bind_option(self): ) self.assertIn("Error: bad option(s) 'bad-option' for bind", s) + def test_bind_dest(self): + temp_dir = tempfile.mkdtemp("-pyrex") + self.addCleanup(shutil.rmtree, temp_dir) + + temp_file = os.path.join(temp_dir, "data") + + conf = self.get_config() + conf["run"]["bind"] += " %s:/foo" % (temp_dir) + conf.write_conf() + + self.assertPyrexContainerShellCommand("echo 123 > /foo/data") + + with open(temp_file, "r") as f: + self.assertEqual(f.read(), "123\n") + + def test_malformed_bind_dest(self): + + b = "/src:/dst:illegal-token" + + conf = self.get_config() + conf["run"]["bind"] += " %s" % b + conf.write_conf() + + s = self.assertPyrexContainerShellCommand("true", capture=True, returncode=1) + self.assertIn("Error: too many colons in run.bind entry '%s'." % b, s) + def test_bad_confversion(self): # Verify that a bad config is an error conf = self.get_config() diff --git a/pyrex.py b/pyrex.py index 00cb16c..43d6952 100755 --- a/pyrex.py +++ b/pyrex.py @@ -59,6 +59,10 @@ def __init__(self, *args, **kwargs): self.optionxform = lambda option: option +class ParsingError(Exception): + pass + + def read_default_config(keep_defaults): with open(os.path.join(PYREX_ROOT, "pyrex.ini"), "r") as f: line = f.read().replace("@CONFVERSION@", PYREX_CONFVERSION) @@ -346,7 +350,23 @@ def parse_bind_options(bind): else: bad_options.append(opt) - return bind, options, bad_options + if bad_options: + raise ParsingError( + "bad option(s) '%s' for bind %s" % (" ".join(bad_options), bind) + ) + + binds = bind.split(":") + + if len(binds) > 2: + raise ParsingError("too many colons in run.bind entry '%s'." % bind) + elif len(binds) == 1: + src = bind + dst = bind + else: + src = binds[0] + dst = binds[1] + + return src, dst, options def prep_container( @@ -528,22 +548,23 @@ def prep_container( + extra_bind ) for b in set(binds): - b, options, bad_options = parse_bind_options(b) - if bad_options: - print("Error: bad option(s) '%s' for bind %s" % (" ".join(bad_options), b)) + try: + src, dst, options = parse_bind_options(b) + except ParsingError as e: + print("Error: %s" % e) return [] - if not os.path.exists(b): + if not os.path.exists(src): if options.optional: continue - print("Error: bind source path {b} does not exist".format(b=b)) + print("Error: bind source path {src} does not exist".format(src=src)) return [] engine_args.extend( [ "--mount", - "type=bind,src={b},dst={b}{ro}".format( - b=b, ro=",readonly" if options.readonly else "" + "type=bind,src={src},dst={dst}{ro}".format( + src=src, dst=dst, ro=",readonly" if options.readonly else "" ), ] )