Merge lp:~psivaa/jenkins-launchpad-plugin/stop-tmp-leak into lp:jenkins-launchpad-plugin

Proposed by Para Siva
Status: Merged
Approved by: Para Siva
Approved revision: 130
Merged at revision: 126
Proposed branch: lp:~psivaa/jenkins-launchpad-plugin/stop-tmp-leak
Merge into: lp:jenkins-launchpad-plugin
Diff against target: 89 lines (+21/-3)
3 files modified
jlp/commands/autoland.py (+7/-1)
jlp/commands/getMergeProposals.py (+7/-1)
jlp/commands/voteOnMergeProposal.py (+7/-1)
To merge this branch: bzr merge lp:~psivaa/jenkins-launchpad-plugin/stop-tmp-leak
Reviewer Review Type Date Requested Status
Francis Ginther Approve
PS Jenkins bot (community) continuous-integration Approve
Joe Talbott (community) Approve
Review via email: mp+272144@code.launchpad.net

Commit message

Fix to wrap the launchpadlib leaking launchpadlib_cache directory.

Description of the change

To fix launchpadlib leaking launchpadlib_cachedir on failures and exceptions.
The proposed one afaik appears to be the less intrusive fix to overcome the leak.
These have been patched and tested in [1]

Note:
These directories are actually created by launchpadlib [2](in the absence of the calling functions not creating directories and passing the reference). So removing them inside jlp is not so intuitive but I guess that's all we could do to fix this issue with less changes. We may actually create the directories and pass the reference to launchpadlib to use, but I think this would be outside the scope for this story.

[1]: https://ci-jenkaas.staging.ubuntu.com/
[2]: http://bazaar.launchpad.net/~lazr-developers/launchpadlib/trunk/view/head:/src/launchpadlib/launchpad.py#L603
--

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Francis Ginther (fginther) wrote :

You can extend the try block up within autoland.py all the way up to the line:

    mp = launchpadutils.get_mp_handle_from_url(lp_handle,
                                               args['merge_proposal'])

The finally clause will still kick in even if the code execution leaves through the return statement [1], so you just need the single rmtree() inside the 'finally'. The jenkins ci runs are failing due to pep8 issues (line too long). Otherwise this looks good. Thanks for identifying and solving the issue.

An alternative to the try/finally approach is to use an atexit handler [2] to register the rmtree as a cleanup function.

[1] - https://docs.python.org/2/tutorial/errors.html#defining-clean-up-actions
[2] - https://docs.python.org/2/library/atexit.html

review: Needs Fixing
128. By Para Siva

Use atexit to handle cleanups

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Para Siva (psivaa) wrote :

Thanks for the atexit suggestion Francis. That proved a lot easier and lesser intrusive.

Would appreciate a re-review. Thanks

129. By Para Siva

Add comments to why cleanup function

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Joe Talbott (joetalbott) wrote :

LGTM. I wonder if we can avoid duplicating the block of code in all three scripts by adding the atexit method in __init__.py, but we don't need to block on that unless it's super easy to take care of.

review: Approve
Revision history for this message
Francis Ginther (fginther) wrote :

I agree that the atexit approach saves a lot or indentation changes and looks cleaner.

Just a suggestion:
I prefer to see the atexit handler right next to the code that adds it. For example:

    launchpad_cachedir = os.path.join('/tmp',
                                      str(os.getpid()),
                                      '.launchpadlib')
    atexit.register(rmtree, os.path.join('/tmp', str(os.getpid())), ignore_errors=True)

The advantage of this is that if the code is modified in the future to change the location of the launchpad_cachedir, it's a lot easier to see that there is an exit handler that needs to be updated as well.

Another approach would be to refactor launchpad_cachedir a bit and use a global variable that both the atexit handler and the original code could share. Something like:

cachedir_base = os.path.join('/tmp', str(os.getpid()))
@atexit.register
def clean_launchpadlib_cachedir():
    rmtree(cachedir_base, ignore_errors=True)

...

    launchpad_cachedir = os.path.join(cachedir_base, '.launchpadlib')

These are just style suggestoins, you may choose to use them or not.

review: Approve
130. By Para Siva

Move atexit registeration closer to where it requires attention to

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Francis Ginther (fginther) wrote :

Looks great, thanks for all the work on this one.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'jlp/commands/autoland.py'
2--- jlp/commands/autoland.py 2013-06-14 19:02:11 +0000
3+++ jlp/commands/autoland.py 2015-09-24 16:09:34 +0000
4@@ -1,6 +1,7 @@
5 import re
6 from argparse import ArgumentParser
7 import argparse
8+import atexit
9 from jlp.launchpadutils import build_state, LaunchpadVote, get_vote_subject
10 from jlp import (launchpadutils, Branch,
11 DputRunner, jenkinsutils, get_launchpad,
12@@ -222,6 +223,12 @@
13 launchpad_cachedir = os.path.join('/tmp',
14 str(os.getpid()),
15 '.launchpadlib')
16+
17+ # `launchpad_cachedir` is leaked upon unexpected exits
18+ # adding this cleanup to stop directories filling up `/tmp/`
19+ atexit.register(rmtree, os.path.join('/tmp',
20+ str(os.getpid())),
21+ ignore_errors=True)
22 lp_handle = get_launchpad(launchpadlib_dir=launchpad_cachedir)
23 mp = launchpadutils.get_mp_handle_from_url(lp_handle,
24 args['merge_proposal'])
25@@ -253,5 +260,4 @@
26
27 # merging starts here
28 ret = merge_and_commit(mp, args)
29- rmtree(launchpad_cachedir, ignore_errors=True)
30 return ret
31
32=== modified file 'jlp/commands/getMergeProposals.py'
33--- jlp/commands/getMergeProposals.py 2013-05-10 14:10:52 +0000
34+++ jlp/commands/getMergeProposals.py 2015-09-24 16:09:34 +0000
35@@ -1,4 +1,5 @@
36 from argparse import ArgumentParser
37+import atexit
38 from jlp import get_launchpad, launchpadutils, logger
39 import os
40 from shutil import rmtree
41@@ -14,13 +15,18 @@
42 launchpad_cachedir = os.path.join('/tmp',
43 str(os.getpid()),
44 '.launchpadlib')
45+
46+ # `launchpad_cachedir` is leaked upon unexpected exits
47+ # adding this cleanup to stop directories filling up `/tmp/`
48+ atexit.register(rmtree, os.path.join('/tmp',
49+ str(os.getpid())),
50+ ignore_errors=True)
51 lp_handle = get_launchpad(launchpadlib_dir=launchpad_cachedir)
52 for b in branch:
53 mps = launchpadutils.get_merge_proposals(lp_handle, b, [merge_type])
54 logger.debug("[%s] merge proposals: %s" % (b, mps))
55 logger.debug("count: %d" % (len(mps)))
56 count += len(mps)
57- rmtree(launchpad_cachedir, ignore_errors=True)
58 return count
59
60
61
62=== modified file 'jlp/commands/voteOnMergeProposal.py'
63--- jlp/commands/voteOnMergeProposal.py 2013-05-10 14:10:52 +0000
64+++ jlp/commands/voteOnMergeProposal.py 2015-09-24 16:09:34 +0000
65@@ -1,4 +1,5 @@
66 from argparse import ArgumentParser
67+import atexit
68 from jlp import launchpadutils, get_launchpad, logger
69 import re
70 import os
71@@ -24,6 +25,12 @@
72 launchpad_cachedir = os.path.join('/tmp',
73 str(os.getpid()),
74 '.launchpadlib')
75+
76+ # `launchpad_cachedir` is leaked upon unexpected exits
77+ # adding this cleanup to stop directories filling up `/tmp/`
78+ atexit.register(rmtree, os.path.join('/tmp',
79+ str(os.getpid())),
80+ ignore_errors=True)
81 lp_handle = get_launchpad(launchpadlib_dir=launchpad_cachedir)
82 mp = launchpadutils.get_mp_handle_from_url(lp_handle,
83 args['merge_proposal'])
84@@ -61,5 +68,4 @@
85 args['build_url'],
86 reason)
87
88- rmtree(launchpad_cachedir, ignore_errors=True)
89 return 0

Subscribers

People subscribed via source and target branches

to all changes: