Merge lp:~doanac/linaro-image-tools/slash-proc-spam-version2 into lp:linaro-image-tools/11.11
- slash-proc-spam-version2
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 281 |
Proposed branch: | lp:~doanac/linaro-image-tools/slash-proc-spam-version2 |
Merge into: | lp:linaro-image-tools/11.11 |
Diff against target: |
104 lines (+61/-10) 2 files modified
linaro_media_create/hwpack.py (+26/-10) linaro_media_create/tests/test_media_create.py (+35/-0) |
To merge this branch: | bzr merge lp:~doanac/linaro-image-tools/slash-proc-spam-version2 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Guilherme Salgado (community) | Approve | ||
Review via email: mp+47436@code.launchpad.net |
Commit message
Description of the change
This is version 2 of the patch I discussed with Loic to fix the error messages.
Guilherme Salgado (salgado) wrote : | # |
Loïc Minier (lool) wrote : | # |
But if your interrupt during mount, then it might not get unmounted?
How about we ignore errors from umount? Or test whether it's mounted before unmounting
Andy Doan (doanac) wrote : | # |
On 01/25/2011 03:15 PM, Guilherme Salgado wrote:
> I don't think we want to register this on local_atexit before mounting
> the proc fs because if for some reason we fail to mount it, this will be
> called and will also fail, maybe preventing other cleanup functions from
> being called.
We sort of have a timing thing here. Loic actually suggested I make this
change.
If you leave the umount registration *after* the call to mount then you
have a small window where a user could hit ctrl-c between the mount and
the call to local_atext.append
I'm not a python expert, but would it be a smart approach to put the
atexit calls in a try/finally block? ie:
def run_local_
# Run the funcs in LIFO order, just like atexit does.
while len(local_atexit) > 0:
- local_atexit.
+ try:
+ local_atexit.
+ finally:
Then we can safely call the umount even if it has failed?
Guilherme Salgado (salgado) wrote : | # |
On Tue, 2011-01-25 at 23:31 +0000, Andy Doan wrote:
> On 01/25/2011 03:15 PM, Guilherme Salgado wrote:
> > I don't think we want to register this on local_atexit before mounting
> > the proc fs because if for some reason we fail to mount it, this will be
> > called and will also fail, maybe preventing other cleanup functions from
> > being called.
>
> We sort of have a timing thing here. Loic actually suggested I make this
> change.
>
> If you leave the umount registration *after* the call to mount then you
> have a small window where a user could hit ctrl-c between the mount and
> the call to local_atext.append
>
> I'm not a python expert, but would it be a smart approach to put the
> atexit calls in a try/finally block? ie:
> def run_local_
> # Run the funcs in LIFO order, just like atexit does.
> while len(local_atexit) > 0:
> - local_atexit.
> + try:
> + local_atexit.
> + finally:
>
> Then we can safely call the umount even if it has failed?
I like that. Another option would be to use subprocess.Popen() in the
cleanup functions as that doesn't raise an exception when the subprocess
ends with a non-zero return code, but I think using a try/finally is
better at ensuring all cleanup functions are executed.
--
Guilherme Salgado <https:/
James Westby (james-w) wrote : | # |
On Tue, 25 Jan 2011 23:31:04 -0000, Andy Doan <email address hidden> wrote:
> I'm not a python expert, but would it be a smart approach to put the
> atexit calls in a try/finally block? ie:
> def run_local_
> # Run the funcs in LIFO order, just like atexit does.
> while len(local_atexit) > 0:
> - local_atexit.
> + try:
> + local_atexit.
> + finally:
>
> Then we can safely call the umount even if it has failed?
The thing to be careful of here is reporting errors. If one fails and
then another is run it can fail with a knock-on error. We want both to
be reported, not just the last, as it can save hours of head scratching.
Thanks,
James
Andy Doan (doanac) wrote : | # |
On 01/26/2011 07:13 AM, James Westby wrote:
> The thing to be careful of here is reporting errors. If one fails and
> then another is run it can fail with a knock-on error. We want both to
> be reported, not just the last, as it can save hours of head scratching.
Good point. I'm not sure if this is poor form in Python, but in Java
I've seen this type of approach (not elegant, but subtle bugs don't get
ignored by accident):
#for main loop logic:
try:
mount_
for hwpack_file in hwpack_files:
except Exception as e:
print e
finally:
run_
#cleanup logic:
def run_local_
# Run the funcs in LIFO order, just like atexit does.
while len(local_atexit) > 0:
try:
except Exception as e:
print e
Another alternative: I know you guys are about to make some changes to
l-m-c so maybe we should drop this for now?
James Westby (james-w) wrote : | # |
On Wed, 26 Jan 2011 18:58:41 -0000, Andy Doan <email address hidden> wrote:
> Good point. I'm not sure if this is poor form in Python, but in Java
> I've seen this type of approach (not elegant, but subtle bugs don't get
> ignored by accident):
Pretty much what I'd go for.
> Another alternative: I know you guys are about to make some changes to
> l-m-c so maybe we should drop this for now?
I don't know if we have anything planned in this area.
Thanks,
James
Guilherme Salgado (salgado) wrote : | # |
On Wed, 2011-01-26 at 18:58 +0000, Andy Doan wrote:
> On 01/26/2011 07:13 AM, James Westby wrote:
> > The thing to be careful of here is reporting errors. If one fails and
> > then another is run it can fail with a knock-on error. We want both to
> > be reported, not just the last, as it can save hours of head scratching
Guilherme Salgado (salgado) wrote : | # |
(Re-sending because Launchpad somehow failed to parsed my last message
and only included the first quoted paragraph in the comment.)
On Wed, 2011-01-26 at 18:58 +0000, Andy Doan wrote:
> On 01/26/2011 07:13 AM, James Westby wrote:
> > The thing to be careful of here is reporting errors. If one fails and
> > then another is run it can fail with a knock-on error. We want both to
> > be reported, not just the last, as it can save hours of head scratching
Guilherme Salgado (salgado) wrote : | # |
(giving up on the email interface and now pasting my email here)
On Wed, 2011-01-26 at 18:58 +0000, Andy Doan wrote:
> On 01/26/2011 07:13 AM, James Westby wrote:
> > The thing to be careful of here is reporting errors. If one fails and
> > then another is run it can fail with a knock-on error. We want both to
> > be reported, not just the last, as it can save hours of head scratching.
This won't happen because on a try/finally the exception is re-raised
after the finally block is executed. I don't know what I had in mind
when I made my comment that the try/finally in run_local_
would make sure all cleanup functions executed.
>
> Good point. I'm not sure if this is poor form in Python, but in Java
> I've seen this type of approach (not elegant, but subtle bugs don't get
> ignored by accident):
>
> #for main loop logic:
> try:
> mount_chroot_
> for hwpack_file in hwpack_files:
> install_
> except Exception as e:
> print e
> finally:
> run_local_
We don't want this as it would swallow any exceptions raised by
install_hwpack() and let the script proceed as if the hwpack was
installed. I think the try/finally is the right thing here.
>
> #cleanup logic:
> def run_local_
> # Run the funcs in LIFO order, just like atexit does.
> while len(local_atexit) > 0:
> try:
> local_atexit.
> except Exception as e:
> print e
I think here we should do the same that the atexit module does:
while len(local_atexit) > 0:
func = local_atexit.pop()
try:
func()
except SystemExit:
except:
import traceback
print >> sys.stderr, "Error in local_atexit:"
if exc_info is not None:
raise exc_info[0], exc_info[1], exc_info[2]
>
> Another alternative: I know you guys are about to make some changes to
> l-m-c so maybe we should drop this for now?
We're always making changes in lmc, but this is a nice fix for an
annoying bug so I don't see why we'd drop it.
Andy Doan (doanac) wrote : | # |
Okay. I've added another commit to this branch that I think satisfies concerns from both Guilherme and James.
Guilherme Salgado (salgado) wrote : | # |
This looks great!
It'd be nice to have at least one test, just to show that run_local_
Andy Doan (doanac) wrote : | # |
> It'd be nice to have at least one test, just to show that
> run_local_
> exception. To do that you could easily use MockSomethingFi
> functions, having the former just change an instance variable so you know it
> was called and the latter just raise an exception. After that you'd just call
> install_hwpacks(), but you'll also need to mock sys.stdout and our custom
> Popen (the existing test_install_
> example)
I've put together a new patch to test this. I'm new to Python, but have done introspection in Java. Depending on how many beers you've had, this "setattr" feature of Python is either the most awesome thing ever or the most disgusting thing ever :)
salgado - I think this is what you were wanting me to add. Let me know what you think, and I can add this to the branch since it hasn't been merged yet.
=== modified file 'linaro_
--- linaro_
+++ linaro_
@@ -3,9 +3,7 @@
import subprocess
import tempfile
-from linaro_media_create import partitions
-from linaro_media_create import cmd_runner
-
+from linaro_media_create import (cmd_runner, hwpack, partitions)
class CreateTempDirFi
@@ -122,10 +120,12 @@
containing the positional arguments given to it to self.calls.
"""
calls = None
+ count = 0
return_value = None
def __call__(self, *args):
if self.calls is None:
+ self.count += 1
return self.return_value
@@ -137,3 +137,16 @@
+
+class MockHWPackAtExi
+
+ def __init__(self):
+ mock = MockCallableWit
+ mock.return_value = ('', '', '')
+ super(MockHWPac
+ hwpack, 'run_local_
+
+ def tearDown(self):
+ super(MockHWPac
+ hwpack.local_atexit = []
+
=== modified file 'linaro_
--- linaro_
+++ linaro_
@@ -72,6 +72,7 @@
MockCmdRun
MockSometh
MockRunSfd
+ MockHWPackAtExi
)
@@ -1015,3 +1016,19 @@
+
+ def test_hwpack_
+ def install_hwpack(p1, p2, p3):
+ raise Exception('hwpack mock exception')
+
+ self.useFixture
+ sys, 'stdout', open('/dev/null', 'w')))
+ self.useFixture
+ self.useFixture
Guilherme Salgado (salgado) wrote : | # |
On Fri, 2011-01-28 at 06:13 +0000, Andy Doan wrote:
> > It'd be nice to have at least one test, just to show that
> > run_local_
> > exception. To do that you could easily use MockSomethingFi
> > functions, having the former just change an instance variable so you know it
> > was called and the latter just raise an exception. After that you'd just call
> > install_hwpacks(), but you'll also need to mock sys.stdout and our custom
> > Popen (the existing test_install_
> > example)
>
> I've put together a new patch to test this. I'm new to Python, but
> have done introspection in Java. Depending on how many beers you've
> had, this "setattr" feature of Python is either the most awesome thing
> ever or the most disgusting thing ever :)
Thanks for the patch, Andy. I guess I've gotten used to setattr() but I
can imagine how it feels the first time you see it.
>
> salgado - I think this is what you were wanting me to add. Let me know
> what you think, and I can add this to the branch since it hasn't been
> merged yet.
Yep, this is what I was thinking; I've just one suggestion below.
>
> === modified file 'linaro_
> --- linaro_
> +++ linaro_
> @@ -3,9 +3,7 @@
> import subprocess
> import tempfile
>
> -from linaro_media_create import partitions
> -from linaro_media_create import cmd_runner
> -
> +from linaro_media_create import (cmd_runner, hwpack, partitions)
>
> class CreateTempDirFi
>
> @@ -122,10 +120,12 @@
> containing the positional arguments given to it to self.calls.
> """
> calls = None
> + count = 0
> return_value = None
> def __call__(self, *args):
> if self.calls is None:
> self.calls = []
> + self.count += 1
> self.calls.
> return self.return_value
>
> @@ -137,3 +137,16 @@
> mock.return_value = ('', '')
> super(MockRunSf
> partitions, 'run_sfdisk_
> +
> +class MockHWPackAtExi
> +
> + def __init__(self):
> + mock = MockCallableWit
> + mock.return_value = ('', '', '')
> + super(MockHWPac
> + hwpack, 'run_local_
> +
> + def tearDown(self):
> + super(MockHWPac
> + hwpack.local_atexit = []
> +
>
> === modified file 'linaro_
> --- linaro_
> +++ linaro_
> @@ -72,6 +72,7 @@
> MockCmdRunnerPo
> MockSomethingFi
> MockRunSfdiskCo
> + MockHWPackAtExi
> )
>
>
> @@ -1015,3 +1016,19 @@
> ['sudo', 'mv', '-f', '/tmp/dir/hosts', 'chroot/etc'],
> ['sudo', 'mv', '-f'...
Andy Doan (doanac) wrote : | # |
I believe the latest commit should include the testing that salgado last suggested.
Guilherme Salgado (salgado) wrote : | # |
This looks fine but the new run_local_
Guilherme Salgado (salgado) wrote : | # |
Andy, I'm landing your branch with http://
Preview Diff
1 | === modified file 'linaro_media_create/hwpack.py' |
2 | --- linaro_media_create/hwpack.py 2011-01-28 19:50:48 +0000 |
3 | +++ linaro_media_create/hwpack.py 2011-02-01 00:42:20 +0000 |
4 | @@ -49,12 +49,12 @@ |
5 | copy_file(linaro_hwpack_install_path, |
6 | os.path.join(chroot_dir, 'usr', 'bin')) |
7 | |
8 | - mount_chroot_proc(chroot_dir) |
9 | - |
10 | - for hwpack_file in hwpack_files: |
11 | - install_hwpack(chroot_dir, hwpack_file, hwpack_force_yes) |
12 | - |
13 | - run_local_atexit_funcs() |
14 | + try: |
15 | + mount_chroot_proc(chroot_dir) |
16 | + for hwpack_file in hwpack_files: |
17 | + install_hwpack(chroot_dir, hwpack_file, hwpack_force_yes) |
18 | + finally: |
19 | + run_local_atexit_funcs() |
20 | |
21 | |
22 | def install_hwpack(chroot_dir, hwpack_file, hwpack_force_yes): |
23 | @@ -82,12 +82,14 @@ |
24 | Also register a function in local_atexit to unmount that /proc filesystem. |
25 | """ |
26 | chroot_proc = os.path.join(chroot_dir, 'proc') |
27 | + |
28 | + def umount_chroot_proc(): |
29 | + cmd_runner.run(['umount', '-v', chroot_proc], as_root=True).wait() |
30 | + local_atexit.append(umount_chroot_proc) |
31 | + |
32 | proc = cmd_runner.run( |
33 | ['mount', 'proc', chroot_proc, '-t', 'proc'], as_root=True) |
34 | proc.wait() |
35 | - def umount_chroot_proc(): |
36 | - cmd_runner.run(['umount', '-v', chroot_proc], as_root=True).wait() |
37 | - local_atexit.append(umount_chroot_proc) |
38 | |
39 | |
40 | def copy_file(filepath, directory): |
41 | @@ -130,5 +132,19 @@ |
42 | |
43 | def run_local_atexit_funcs(): |
44 | # Run the funcs in LIFO order, just like atexit does. |
45 | + exc_info = None |
46 | while len(local_atexit) > 0: |
47 | - local_atexit.pop()() |
48 | + func = local_atexit.pop() |
49 | + try: |
50 | + func() |
51 | + except SystemExit: |
52 | + exc_info = sys.exc_info() |
53 | + except: |
54 | + import traceback |
55 | + print >> sys.stderr, "Error in local_atexit:" |
56 | + traceback.print_exc() |
57 | + exc_info = sys.exc_info() |
58 | + |
59 | + if exc_info is not None: |
60 | + raise exc_info[0], exc_info[1], exc_info[2] |
61 | + |
62 | |
63 | === modified file 'linaro_media_create/tests/test_media_create.py' |
64 | --- linaro_media_create/tests/test_media_create.py 2011-01-28 19:50:48 +0000 |
65 | +++ linaro_media_create/tests/test_media_create.py 2011-02-01 00:42:20 +0000 |
66 | @@ -1065,3 +1065,38 @@ |
67 | ['sudo', 'mv', '-f', '/tmp/dir/hosts', 'chroot/etc'], |
68 | ['sudo', 'mv', '-f', '/tmp/dir/resolv.conf', 'chroot/etc']], |
69 | fixture.mock.calls) |
70 | + |
71 | + def test_hwpack_atexit(self): |
72 | + self.run_local_atexit_functions_called = False |
73 | + |
74 | + # ensure this list gets cleared, or other test functions may |
75 | + # fail because the list isn't what they expected |
76 | + def clear_atexits(): |
77 | + linaro_media_create.hwpack.local_atexit = [] |
78 | + self.addCleanup(clear_atexits) |
79 | + |
80 | + def mock_run_local_atexit_functions(): |
81 | + self.run_local_atexit_functions_called = True |
82 | + |
83 | + def mock_install_hwpack(p1, p2, p3): |
84 | + raise Exception('hwpack mock exception') |
85 | + |
86 | + self.useFixture(MockSomethingFixture( |
87 | + sys, 'stdout', open('/dev/null', 'w'))) |
88 | + self.useFixture(MockCmdRunnerPopenFixture()) |
89 | + self.useFixture(MockSomethingFixture( |
90 | + linaro_media_create.hwpack, 'install_hwpack', mock_install_hwpack)) |
91 | + self.useFixture(MockSomethingFixture( |
92 | + linaro_media_create.hwpack, 'run_local_atexit_funcs', |
93 | + mock_run_local_atexit_functions)) |
94 | + |
95 | + |
96 | + force_yes = True |
97 | + exception_caught = False |
98 | + try: |
99 | + install_hwpacks('chroot', '/tmp/dir', force_yes, 'hwp.tgz', 'hwp2.tgz') |
100 | + except: |
101 | + exception_caught = True |
102 | + self.assertTrue(self.run_local_atexit_functions_called) |
103 | + self.assertTrue(exception_caught) |
104 | + |
Thanks for working on this, Andy. I like your solution but there's one
thing which doesn't seem right to me; see below.
On Tue, 2011-01-25 at 18:22 +0000, Andy Doan wrote: media_create/ hwpack. py' media_create/ hwpack. py 2011-01-17 17:51:13 +0000 media_create/ hwpack. py 2011-01-25 18:22:39 +0000 hwpack( chroot_ dir, hwpack_file, hwpack_force_yes): join(chroot_ dir, 'proc') chroot_ proc(): run(['umount' , '-v', chroot_proc], as_root= True).wait( ) append( umount_ chroot_ proc)
[...]
> === modified file 'linaro_
> --- linaro_
> +++ linaro_
> def install_
> @@ -63,12 +63,14 @@
> Also register a function in local_atexit to unmount that /proc filesystem.
> """
> chroot_proc = os.path.
> +
> + def umount_
> + cmd_runner.
> + local_atexit.
> +
I don't think we want to register this on local_atexit before mounting
the proc fs because if for some reason we fail to mount it, this will be
called and will also fail, maybe preventing other cleanup functions from
being called.
> proc = cmd_runner.run( chroot_ proc(): run(['umount' , '-v', chroot_proc], as_root= True).wait( ) append( umount_ chroot_ proc)
> ['mount', 'proc', chroot_proc, '-t', 'proc'], as_root=True)
> proc.wait()
> - def umount_
> - cmd_runner.
> - local_atexit.
>
>
> def copy_file(filepath, directory):
>
-- /launchpad. net/~salgado>
Guilherme Salgado <https:/