Merge lp:~lool/linaro-image-tools/missing-sbin into lp:linaro-image-tools/11.11

Proposed by Loïc Minier
Status: Merged
Merged at revision: 305
Proposed branch: lp:~lool/linaro-image-tools/missing-sbin
Merge into: lp:linaro-image-tools/11.11
Diff against target: 117 lines (+41/-5)
4 files modified
linaro_image_tools/cmd_runner.py (+14/-0)
linaro_image_tools/hwpack/packages.py (+5/-3)
linaro_image_tools/tests/test_cmd_runner.py (+21/-0)
linaro_image_tools/utils.py (+1/-2)
To merge this branch: bzr merge lp:~lool/linaro-image-tools/missing-sbin
Reviewer Review Type Date Requested Status
Guilherme Salgado (community) Approve
Review via email: mp+54693@code.launchpad.net

Description of the change

Fixes runs of the testsuite (didn't check actually running linaro-media-create or other helpers) when /sbin isn't in the PATH, or when PATH is empty, or when PATH isn't set; bug #709517.

This is a very crude fix, but it seems safe; please shout if you think this is too ugly and we should go with the other approach I describe in the bug.

I tested this with:
# /sbin missing from PATH
PATH=/usr/local/bin:/usr/bin:/bin testr run
# PATH set but empty
PATH="" /usr/bin/python -m subunit.run linaro_image_tools.tests.test_suite|grep -v ^test:|grep -v ^successful:
# PATH unset
env -i testr run
env -u PATH /usr/bin/python -m subunit.run linaro_image_tools.tests.test_suite|grep -v ^test:|grep -v ^successful:

To post a comment you must log in.
Revision history for this message
Loïc Minier (lool) wrote :

I've pushed a quite different approach now; the bad part is that the hwpack testsuite doesn't pass anymore when PATH is broken, this is because the hwpack code uses subprocess.Popen directly.

What I would propose is that we convert the hwpack code to use cmd_runner.

I also need to add tests for sanitize_path, but wouldn't mind some feedback on the overall approach.

Revision history for this message
Guilherme Salgado (salgado) wrote :

I don't quite like the fact that we have to "sanitize" both the env passed to Popen() and os.environ, but I don't see a better way to solve this problem now. Also, why do we need the sanitize() call in linaro_image_tools/media_create/utils.py?

I'd like to know what James thinks of this

Revision history for this message
Loïc Minier (lool) wrote :

On Thu, Mar 24, 2011, Guilherme Salgado wrote:
> Also, why do we need the sanitize() call in
> linaro_image_tools/media_create/utils.py?

 find_command was using custom code to make sure PATH was set and had
 some directories in it; it was more generic to simply make sure the
 PATH is correct (which has a global effect on os.environ the first time
 you create a Popen() anyway).

 This reminds me that I'd like to propose we move find_command to
 cmd_runner() as it relates to PATH / command handling too.

--
Loïc Minier

Revision history for this message
James Westby (james-w) wrote :

On Thu, 24 Mar 2011 14:44:26 -0000, Guilherme Salgado <email address hidden> wrote:
> I don't quite like the fact that we have to "sanitize" both the env
> passed to Popen() and os.environ, but I don't see a better way to
> solve this problem now. Also, why do we need the sanitize() call in
> linaro_image_tools/media_create/utils.py?

> I'd like to know what James thinks of this

I don't have a problem with it, though I share your concern.

I wonder what the hwpack failures are though.

Thanks,

James

Revision history for this message
Loïc Minier (lool) wrote :

On Thu, Mar 24, 2011, James Westby wrote:
> I wonder what the hwpack failures are though.

 It's when running dpkg-deb for instance; it wont be found if PATH
 doesn't contain /usr/bin.

 I find very unlikely that someone would screw up the PATH like this,
 but saw that while testing for broken PATH and since we're fixing the
 PATH for /sbin, it's just as easy to fix it for the other standard
 dirs.

--
Loïc Minier

Revision history for this message
Guilherme Salgado (salgado) wrote :

 review approve

On Thu, 2011-03-24 at 14:13 +0000, Loïc Minier wrote:
> === modified file 'linaro_image_tools/media_create/cmd_runner.py'
> --- linaro_image_tools/media_create/cmd_runner.py 2011-03-09 15:11:28 +0000
> +++ linaro_image_tools/media_create/cmd_runner.py 2011-03-24 14:11:34 +0000
> @@ -20,6 +20,14 @@
> import os
> import subprocess
>
> +def sanitize_path(env):
> + """Makes sure PATH is set and has important directories"""
> + default = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
> + dirs = env.get('PATH', default).split(os.pathsep)
> + for d in default.split(os.pathsep):
> + if d not in dirs:
> + dirs.append(d)
> + env['PATH'] = os.pathsep.join(dirs)

This can be slightly simplified if you use a set():

  if 'PATH' in env:
    dirs = env.get('PATH').split(os.pathsep)
  else:
    dirs = []
  default = ['/usr/local/sbin', ...]
  env['PATH'] = os.pathsep.join(set(dirs).union(default))

It doesn't simplify much but at least allows us to get rid of the
hard-coded pathsep in the string assigned to 'default'.

review: Approve
Revision history for this message
James Westby (james-w) wrote :

On Thu, 24 Mar 2011 17:49:15 -0000, Guilherme Salgado <email address hidden> wrote:
> This can be slightly simplified if you use a set():
>
> if 'PATH' in env:
> dirs = env.get('PATH').split(os.pathsep)
> else:
> dirs = []
> default = ['/usr/local/sbin', ...]
> env['PATH'] = os.pathsep.join(set(dirs).union(default))
>
> It doesn't simplify much but at least allows us to get rid of the
> hard-coded pathsep in the string assigned to 'default'.

set() is no good as it will shuffle the entries in PATH won't it?
That would lead to it picking commands to run at random.

Thanks,

James

Revision history for this message
Guilherme Salgado (salgado) wrote :

Yeah, good catch; we definitely don't want to use set() here.

309. By Loïc Minier

Use cmd_runner instead of subprocess, this ensures that a sane PATH is used.

Revision history for this message
Loïc Minier (lool) wrote :

To illustrate the hwpack failures, one can run:
PATH=/foo /usr/bin/python -m subunit.run linaro_image_tools.tests.test_suite|grep -v ^test:|grep -v ^successful:
this gave output like:
error: linaro_image_tools.hwpack.tests.test_builder.HardwarePackBuilderTests.test_builds_correct_contents [ multipart
Content-Type: text/x-traceback;charset=utf8,language=python
traceback
449
Traceback (most recent call last):
  File "/usr/lib/python2.6/dist-packages/testtools/runtest.py", line 169, in _run_user
    return fn(*args, **kwargs)
  File "/usr/lib/python2.6/dist-packages/testtools/testcase.py", line 499, in _run_test_method
    return self._get_test_method()()
  File "linaro_image_tools/hwpack/tests/test_builder.py", line 129, in test_builds_correct_contents
    builder.build()
  File "linaro_image_tools/hwpack/builder.py", line 78, in build
    local_packages, LOCAL_ARCHIVE_LABEL))
  File "linaro_image_tools/hwpack/packages.py", line 199, in sources_entry_for_debs
    stdout=open(os.path.join(tmpdir, 'Release'), 'w'))
  File "/usr/lib/python2.6/subprocess.py", line 483, in check_call
    retcode = call(*popenargs, **kwargs)
  File "/usr/lib/python2.6/subprocess.py", line 470, in call
    return Popen(*popenargs, **kwargs).wait()
  File "/usr/lib/python2.6/subprocess.py", line 623, in __init__
    errread, errwrite)
  File "/usr/lib/python2.6/subprocess.py", line 1141, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory
0
]

I've just changed one file to use cmd_runner, and kept the testing code using subprocess.

310. By Loïc Minier

Merge lp:linaro-image-tools; update cmd_runner import path.

311. By Loïc Minier

Expose default path of sanitize_path() as to help testing and add tests for
sanitize_path().

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'linaro_image_tools/cmd_runner.py'
2--- linaro_image_tools/cmd_runner.py 2011-03-24 10:48:18 +0000
3+++ linaro_image_tools/cmd_runner.py 2011-03-24 22:15:56 +0000
4@@ -21,9 +21,19 @@
5 import subprocess
6
7
8+DEFAULT_PATH = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
9 SUDO_ARGS = ['sudo', '-E']
10
11
12+def sanitize_path(env):
13+ """Makes sure PATH is set and has important directories"""
14+ dirs = env.get('PATH', DEFAULT_PATH).split(os.pathsep)
15+ for d in DEFAULT_PATH.split(os.pathsep):
16+ if d not in dirs:
17+ dirs.append(d)
18+ env['PATH'] = os.pathsep.join(dirs)
19+
20+
21 def run(args, as_root=False, stdin=None, stdout=None, stderr=None):
22 """Run the given command as a sub process.
23
24@@ -59,6 +69,10 @@
25 if env is None:
26 env = os.environ.copy()
27 env['LC_ALL'] = 'C'
28+ # ensure a proper PATH before calling Popen
29+ sanitize_path(os.environ)
30+ # and for subcommands
31+ sanitize_path(env)
32 super(Popen, self).__init__(args, env=env, **kwargs)
33
34 def wait(self):
35
36=== modified file 'linaro_image_tools/hwpack/packages.py'
37--- linaro_image_tools/hwpack/packages.py 2011-01-28 19:50:48 +0000
38+++ linaro_image_tools/hwpack/packages.py 2011-03-24 22:15:56 +0000
39@@ -34,6 +34,8 @@
40
41 from debian.debfile import DebFile
42
43+from linaro_image_tools import cmd_runner
44+
45
46 logger = logging.getLogger(__name__)
47
48@@ -191,12 +193,12 @@
49 with open(os.path.join(tmpdir, 'Packages'), 'w') as packages_file:
50 packages_file.write(get_packages_file(local_debs, rel_to=tmpdir))
51 if label:
52- subprocess.check_call(
53+ proc = cmd_runner.run(
54 ['apt-ftparchive',
55 '-oAPT::FTPArchive::Release::Label=%s' % label,
56 'release',
57 tmpdir],
58- stdout=open(os.path.join(tmpdir, 'Release'), 'w'))
59+ stdout=open(os.path.join(tmpdir, 'Release'), 'w')).wait()
60 return 'file://%s ./' % (tmpdir, )
61
62
63@@ -245,7 +247,7 @@
64 env = os.environ
65 env['LC_ALL'] = 'C'
66 env['NO_PKG_MANGLE'] = '1'
67- proc = subprocess.Popen(
68+ proc = cmd_runner.Popen(
69 ['dpkg-deb', '-b', packaging_dir],
70 env=env,
71 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
72
73=== modified file 'linaro_image_tools/tests/test_cmd_runner.py'
74--- linaro_image_tools/tests/test_cmd_runner.py 2011-03-24 17:58:26 +0000
75+++ linaro_image_tools/tests/test_cmd_runner.py 2011-03-24 22:15:56 +0000
76@@ -30,6 +30,27 @@
77 sudo_args = " ".join(cmd_runner.SUDO_ARGS)
78
79
80+class TestSanitizePath(TestCaseWithFixtures):
81+ def setUp(self):
82+ super(TestSanitizePath, self).setUp()
83+ self.env = {}
84+
85+ def test_path_unset(self):
86+ cmd_runner.sanitize_path(self.env)
87+ self.assertEqual(cmd_runner.DEFAULT_PATH, self.env['PATH'])
88+
89+ def test_path_missing_dirs(self):
90+ path = '/bin:/sbin:/foo:/usr/local/sbin'
91+ self.env['PATH'] = path
92+ cmd_runner.sanitize_path(self.env)
93+ expected = '%s:/usr/local/bin:/usr/sbin:/usr/bin' % path
94+ self.assertEqual(expected, self.env['PATH'])
95+
96+ def test_idempotent(self):
97+ self.env['PATH'] = cmd_runner.DEFAULT_PATH
98+ cmd_runner.sanitize_path(self.env)
99+ self.assertEqual(cmd_runner.DEFAULT_PATH, self.env['PATH'])
100+
101 class TestCmdRunner(TestCaseWithFixtures):
102
103 def test_run(self):
104
105=== modified file 'linaro_image_tools/utils.py'
106--- linaro_image_tools/utils.py 2011-03-24 18:41:59 +0000
107+++ linaro_image_tools/utils.py 2011-03-24 22:15:56 +0000
108@@ -73,8 +73,7 @@
109 assert name != ""
110 assert os.path.dirname(name) == ""
111
112- if not os.environ.has_key("PATH"):
113- os.environ["PATH"] = ":/bin:usr/bin"
114+ cmd_runner.sanitize_path(os.environ)
115
116 # default to searching in current directory when running from a bzr
117 # checkout

Subscribers

People subscribed via source and target branches