Merge lp:~milo/linaro-patchmetrics/bug1012037 into lp:linaro-patchmetrics

Proposed by Milo Casagrande
Status: Merged
Merged at revision: 366
Proposed branch: lp:~milo/linaro-patchmetrics/bug1012037
Merge into: lp:linaro-patchmetrics
Diff against target: 243 lines (+129/-57)
2 files modified
apps/patchwork/bin/update-committed-patches.py (+7/-3)
apps/patchwork/utils.py (+122/-54)
To merge this branch: bzr merge lp:~milo/linaro-patchmetrics/bug1012037
Reviewer Review Type Date Requested Status
Deepti B. Kalakeri (community) Approve
Review via email: mp+112337@code.launchpad.net

Description of the change

Hello,

requesting a new merge: with this changes I refactored a little bit the functions used to clone and pull the git repositories, I added a new one for the kill action, and for the new checkout action.

The new functions would be helpful too in the process of fixing bug 1017933.
The addition of a dictionary at the top of the file with the name of the project and the branch name is a temporary solution.

To post a comment you must log in.
370. By Milo Casagrande

Added 'master' branch for 'gcc-patches' project.

Revision history for this message
Milo Casagrande (milo) wrote :

For the moment I added two repositories in the temporary dictionary: cpufreq and gcc. For cpu-freq we had a confirmation to use the 'next' branch, for gcc we still do not have it, but if we have to change branch later, it will not be a problem, the 'git checkout' operation will work and switch to the new branch.

Revision history for this message
Deepti B. Kalakeri (deeptik) wrote :

+++ apps/patchwork/bin/update-committed-patches.py 2012-06-28 08:43:05 +0000
@@ -21,8 +21,12 @@

 from patchwork.models import Project, State
 from patchwork.utils import (
- ensure_source_checkout_for_project, FailedToFetchGitRepo,
- get_diff_for_commit, get_commits_to_parse, get_patches_matching_commit)
+ ensure_source_checkout_for_project,
+ get_commits_to_parse,
+ get_diff_for_commit,
+ get_patches_matching_commit,
+ GitException,
+ )

The comma at the end can be removed.

=== modified file 'apps/patchwork/utils.py'
--- apps/patchwork/utils.py 2012-06-25 15:29:36 +0000
+++ apps/patchwork/utils.py 2012-06-28 08:43:05 +0000
@@ -33,6 +33,25 @@

 # Timeout for git operations.
 GIT_TIMEOUT = 120 * 60
+# XXX This is a temporary solution, a workardound for bug 1017933.
+# Once that bug is fixed or in the process, this should be removed.

FIXME would be more appropriate than XXX

+ # XXX this is a temporary solution, we need to fix bug
+ # https://bugs.launchpad.net/linaro-patchmetrics/+bug/1017933
+ if project.name in REPO_BRANCHES:
+ ensure_on_valid_branch(project, REPO_BRANCHES.get(project.name))
+
Here as well, FIXME would be more appropriate than XXX

The following piece of code repeats in 2 places, it can be made as a function call ?
    wait = 0
    wait_increment = 5

    while wait < timeout:
        proc.poll()
        if proc.returncode is not None:
            break
        wait += wait_increment
        time.sleep(wait_increment)

    if proc.returncode is None:
        kill_git_processes(proc, project)

    stdout, stderr = proc.communicate()

review: Needs Fixing
371. By Milo Casagrande

Removed duplicated code, PEP8 fixes.

Revision history for this message
Milo Casagrande (milo) wrote :

> +++ apps/patchwork/bin/update-committed-patches.py 2012-06-28 08:43:05
>
> The comma at the end can be removed.

Fixed.

> === modified file 'apps/patchwork/utils.py'
> # Timeout for git operations.
> GIT_TIMEOUT = 120 * 60
> +# XXX This is a temporary solution, a workardound for bug 1017933.
> +# Once that bug is fixed or in the process, this should be removed.
>
> FIXME would be more appropriate than XXX

Fixed for both.

> The following piece of code repeats in 2 places, it can be made as a function
> call ?
> wait = 0
> wait_increment = 5
>
> while wait < timeout:
> proc.poll()
> if proc.returncode is not None:
> break
> wait += wait_increment
> time.sleep(wait_increment)
>
> if proc.returncode is None:
> kill_git_processes(proc, project)

I was not completely sure to add a new function for that too, but now that you point it out.
I added a new one, including up to the kill_git_processes call. I preferred to keep the other one out of this new function.

Thanks.

Revision history for this message
Deepti B. Kalakeri (deeptik) wrote :

+1 Looks good. I hope we get our Errors fixed now.

Thanks!!!
Deepti.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'apps/patchwork/bin/update-committed-patches.py'
2--- apps/patchwork/bin/update-committed-patches.py 2012-01-13 12:48:45 +0000
3+++ apps/patchwork/bin/update-committed-patches.py 2012-06-28 11:55:22 +0000
4@@ -21,8 +21,12 @@
5
6 from patchwork.models import Project, State
7 from patchwork.utils import (
8- ensure_source_checkout_for_project, FailedToFetchGitRepo,
9- get_diff_for_commit, get_commits_to_parse, get_patches_matching_commit)
10+ ensure_source_checkout_for_project,
11+ get_commits_to_parse,
12+ get_diff_for_commit,
13+ get_patches_matching_commit,
14+ GitException
15+ )
16
17
18 def main():
19@@ -31,7 +35,7 @@
20 for project in projects:
21 try:
22 root = ensure_source_checkout_for_project(project)
23- except FailedToFetchGitRepo, e:
24+ except GitException, e:
25 sys.stderr.write(str(e) + "\n")
26 continue
27
28
29=== modified file 'apps/patchwork/utils.py'
30--- apps/patchwork/utils.py 2012-06-25 15:29:36 +0000
31+++ apps/patchwork/utils.py 2012-06-28 11:55:22 +0000
32@@ -33,6 +33,25 @@
33
34 # Timeout for git operations.
35 GIT_TIMEOUT = 120 * 60
36+# FIXME This is a temporary solution, a workardound for bug 1017933.
37+# Once that bug is fixed or in the process, this should be removed.
38+REPO_BRANCHES = {'cpufreq': 'next', 'gcc-patches': 'master'}
39+
40+
41+class GitException(Exception):
42+ """General class for git exceptions."""
43+
44+
45+class FailedToFetchGitRepo(GitException):
46+ """Failed to fetch the git repo of a project."""
47+
48+
49+class FailedToCheckoutGitRepo(GitException):
50+ """Failed to perform a checkout operation on a git repository."""
51+
52+
53+class GitTimeout(GitException):
54+ """Timeout when fetching the git repo of a project."""
55
56
57 def get_patch_ids(d, prefix='patch_id'):
58@@ -153,14 +172,6 @@
59 return []
60
61
62-class FailedToFetchGitRepo(Exception):
63- """Failed to fetch the git repo of a project."""
64-
65-
66-class GitFetchTimeout(FailedToFetchGitRepo):
67- """Timeout when fetching the git repo of a project."""
68-
69-
70 def get_child_pids(pid):
71 """Recursively get the PIDs of the given process' children."""
72 pids = []
73@@ -185,6 +196,14 @@
74 # not suitable, whereas its latest version has a bunch of non-packaged
75 # dependencies.
76 def ensure_source_checkout_for_project(project, timeout=GIT_TIMEOUT):
77+ """Ensures that we have the code for the project we are working on.
78+
79+ If there is no code in the PATCHWORK_GIT_REPOS_DIR, it performs a 'clone'
80+ operation, otherwise we try to 'pull' the latest changes.
81+
82+ :param project: the project we are working on
83+ :param timeout: the timeout for the git operation, defaults to GIT_TIMEOUT
84+ """
85 root = os.path.join(settings.PATCHWORK_GIT_REPOS_DIR, project.linkname)
86
87 if os.path.exists(root):
88@@ -194,60 +213,109 @@
89 proc = subprocess.Popen(['git', 'clone', project.source_tree, root],
90 stdout=subprocess.PIPE)
91
92+ git_process_poll(proc, project, timeout)
93+
94+ stdout, stderr = proc.communicate()
95+ if proc.returncode != 0:
96+ raise FailedToFetchGitRepo(
97+ "Failed to fetch %s (%s - %s).\n stdout: %s\n stderr: %s" % (
98+ project.source_tree, project.name, project.linkname, stdout,
99+ stderr))
100+
101+ # FIXME this is a temporary solution, we need to fix bug
102+ # https://bugs.launchpad.net/linaro-patchmetrics/+bug/1017933
103+ if project.name in REPO_BRANCHES:
104+ ensure_on_valid_branch(project, REPO_BRANCHES.get(project.name))
105+
106+ return root
107+
108+
109+def ensure_on_valid_branch(project, branch, timeout=GIT_TIMEOUT):
110+ """Ensures that we are on a valid git branch.
111+
112+ Performs a git checkout on the provided branch name, no check is done on
113+ the name, it has to be a valid git branch. If we are already on that branch
114+ nothing happens, git will return 0 as normal.
115+
116+ :param project: the project we are working on
117+ :param branch: the name of the branch to checkout
118+ :param timeout: the max timeout for git operation, defaults to GIT_TIMEOUT
119+ """
120+ cwd = os.path.join(settings.PATCHWORK_GIT_REPOS_DIR, project.linkname)
121+ proc = subprocess.Popen(['git', 'checkout', branch], cwd=cwd,
122+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
123+
124+ git_process_poll(proc, project, timeout)
125+
126+ stdout, stderr = proc.communicate()
127+ if proc.returncode != 0:
128+ raise FailedToCheckoutGitRepo(
129+ "Failed to checkout branch '%s' of %s.\n stdout: %s"
130+ "\n stderr: %s" % (branch, project.name, stdout, stderr))
131+
132+
133+def git_process_poll(process, project, timeout):
134+ """Check the status of the process.
135+
136+ :param process: the process to watch
137+ :param project: the project we are working on
138+ :param timeout: the max timeout for the process
139+ """
140 wait = 0
141 wait_increment = 5
142
143 while wait < timeout:
144- proc.poll()
145- if proc.returncode is not None:
146+ process.poll()
147+ if process.returncode is not None:
148 break
149 wait += wait_increment
150 time.sleep(wait_increment)
151
152- if proc.returncode is None:
153- msg = "Timeout (%d/%d seconds) when fetching '%s' (%s).\n" % \
154- (wait, timeout, project.name, project.linkname)
155- msg += " Project path: %s\n Project tree: %s\n" % \
156- (root, project.source_tree)
157-
158- # Kill the subprocess we started and all its children. This is
159- # necessary because 'git pull' will actually run a shell script
160- # as a subprocess, with the script firing a 'git fetch' subprocess,
161- # and if we don't kill them all individually they will be left around
162- # and some times they will never finish.
163- pids = [str(proc.pid)]
164- # We traverse the child processes recursively here instead of querying
165- # all processes under the same process group because on production
166- # this will be run as a cronjob and thus the process group will
167- # include the shell process started by cron, which is the parent of
168- # this very process and thus we don't want to kill -- we only want to
169- # kill the subprocess we started above and all its children.
170- pids.extend(get_child_pids(proc.pid))
171- msg += " Killing processes %s and moving on." % pids
172- proc = subprocess.Popen(
173- ['kill', '-KILL'] + pids,
174- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
175- kill_stdout, kill_stderr = proc.communicate()
176- msg += " Output of the kill command: (%s, %s)." % (
177- kill_stdout, kill_stderr)
178-
179- # Poll the subprocess we started to ensure it's not left behind as a
180- # zombie, but first sleep a couple seconds to make sure it's finished
181- # when we poll.
182- time.sleep(2)
183- if proc.poll() is None:
184- msg += (" Oops, we failed to kill the git process (pid=%d) that "
185- "timed out." % proc.pid)
186- raise GitFetchTimeout(msg)
187-
188- stdout, stderr = proc.communicate()
189- if proc.returncode != 0:
190- raise FailedToFetchGitRepo(
191- "Failed to fetch %s (%s - %s).\n stdout: %s\n stderr: %s" % (
192- project.source_tree, project.name, project.linkname, stdout,
193- stderr))
194-
195- return root
196+ if process.returncode is None:
197+ kill_git_processes(process, project)
198+
199+
200+def kill_git_processes(process, project):
201+ """Kills the process of the git operation, and its subprocesses.
202+
203+ :param process: the parent process to kill
204+ :param project: the project we are working on
205+ """
206+ cwd = os.path.join(settings.PATCHWORK_GIT_REPOS_DIR, project.linkname)
207+ msg = ("Timeout when performing git operation on '%s' (%s).\n" %
208+ (project.name, project.linkname))
209+ msg += " Project path: %s\n Project tree: %s\n" % \
210+ (cwd, project.source_tree)
211+
212+ # Kill the subprocess we started and all its children. This is
213+ # necessary because 'git pull' will actually run a shell script
214+ # as a subprocess, with the script firing a 'git fetch' subprocess,
215+ # and if we don't kill them all individually they will be left around
216+ # and some times they will never finish.
217+ pids = [str(process.pid)]
218+ # We traverse the child processes recursively here instead of querying
219+ # all processes under the same process group because on production
220+ # this will be run as a cronjob and thus the process group will
221+ # include the shell process started by cron, which is the parent of
222+ # this very process and thus we don't want to kill -- we only want to
223+ # kill the subprocess we started above and all its children.
224+ pids.extend(get_child_pids(process.pid))
225+ msg += " Killing processes %s and moving on." % pids
226+ proc = subprocess.Popen(
227+ ['kill', '-KILL'] + pids,
228+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
229+ kill_stdout, kill_stderr = proc.communicate()
230+ msg += " Output of the kill command: (%s, %s)." % (
231+ kill_stdout, kill_stderr)
232+
233+ # Poll the subprocess we started to ensure it's not left behind as a
234+ # zombie, but first sleep a couple seconds to make sure it's finished
235+ # when we poll.
236+ time.sleep(2)
237+ if proc.poll() is None:
238+ msg += (" Oops, we failed to kill the git process (pid=%d) that "
239+ "timed out." % proc.pid)
240+ raise GitTimeout(msg)
241
242
243 def get_commits_to_parse(root, start_at):

Subscribers

People subscribed via source and target branches