Merge ~sbaldassin/bileto:ust-bileto into bileto:master
- Git
- lp:~sbaldassin/bileto
- ust-bileto
- Merge into master
Status: | Rejected |
---|---|
Rejected by: | Robert Bruce Park |
Proposed branch: | ~sbaldassin/bileto:ust-bileto |
Merge into: | bileto:master |
Diff against target: |
856 lines (+742/-2) 7 files modified
.bzr-builddeb/default.conf (+2/-0) bileto/models.py (+14/-1) bileto/static/index.html (+285/-0) bileto/static/style.css (+384/-0) britney/iterate.py (+30/-1) db_migrations.sh (+1/-0) scripts/import.sh (+26/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Robert Bruce Park (community) | Needs Information | ||
Review via email: mp+306251@code.launchpad.net |
Commit message
Adding ubuntu system tests to bileto
Description of the change
This change adds ubuntu system tests to bileto.
In order to deploy this, you'll need the qakit module available in the canonical-
Robert Bruce Park (robru) wrote : | # |
Also, where is the code for qakit.ust.worker?
Thanks
Santiago Baldassin (sbaldassin) wrote : | # |
Thanks for your feedback Robert. I'll be working on your comments. In the meantime, here's the code for qakit: https:/
About triggering this from Britney during proposed migration, we would just need to re-use the code that I've added in bileto or we could just publish an amqp message (containing the ppa) to /ubuntu-
Last but not least, I'd like to keep the reporting in bileto if you don't mind since it will help us to trace the results easily to our jenkins builds. Polling for results will generate a single http request which response is a 100 bytes message so the performance degradation in bileto should be minimum and I also make sure that if no response is received then an empty response is returned so that angularjs can parse it in any case
Robert Bruce Park (robru) wrote : | # |
Ok, so I've had a chance to review the qakit source and I need to raise some concerns here. One thing is that the compute_ust_signoff as you've written it will be triggered every time the ticket is modified, for any reason. So it's true that the status job will update the ticket every 20 minutes, which will result in ust polling every 20 minutes, but it will also do a ust poll, eg, if a user edits a field on the ticket, and this is a really unacceptable slowdown for user experience, to do a syncronous poll and block the thread while the user is waiting for their change to save.
**IF** we go ahead with the idea of bileto polling ust results, we'll need to model it after britney/iterate.py, eg, there should be an independent script that runs from cron and then feeds the results into bileto. That way it would run much more consistently and performantly, without interfering with user edits of tickets.
Santiago Baldassin (sbaldassin) wrote : | # |
Good point Robert! There's no need to poll for results when the user is editing the form. Instead of using an independent script to get the results, I'd like to suggest doing it with an ajax call in the app.js which will be triggered every 2 minutes. That way users will have to wait only 2 minutes in the worst case scenario and we'll be using a non blocking asynchronous method. As long as we update the scope in the callback, angular will take care of reflecting that in the ui
Robert Bruce Park (robru) wrote : | # |
Well the problem with doing it in app.js is that it then runs client side:
if two people are viewing the same ticket you'll get redundant polling of
results, and if nobody is viewing the ticket then the results never appear!
if app.js then attempts to save the result to the ticket, the audit log
will appear as if users are entering information manually, eg, the log
would show "sbalda: ust_signoff: approved" which would be quite strange. Is
this really the behavior you want?
I think really the only sensible approach is to model britney/iterate.py:
run it from cron periodically, query bileto for what tickets to submit for
testing, get results and save them to the ticket. Except for the first
phase we just want to submit requests and not poll or display results just
yet.
On Sep 21, 2016 6:59 AM, "Santiago Baldassin" <
<email address hidden>> wrote:
> Good point Robert! There's no need to poll for results when the user is
> editing the form. Instead of using an independent script to get the
> results, I'd like to suggest doing it with an ajax call in the app.js which
> will be triggered every 2 minutes. That way users will have to wait only 2
> minutes in the worst case scenario and we'll be using a non blocking
> asynchronous method. As long as we update the scope in the callback,
> angular will take care of reflecting that in the ui
> --
> https:/
> You are reviewing the proposed merge of ~sbaldassin/
> bileto:master.
>
Robert Bruce Park (robru) wrote : | # |
Sorry, another question: Are you saying it's possible to trigger UST with an ajax request? Or is the ajax request only for querying the status after it's been triggered?
If the former, I'd strongly prefer that over importing this qakit module, which imports jenkins module. Even when we used jenkins extensively, bileto only ever spoke JSON to it, I never had to import jenkins module, so I'd prefer not to start using that now if it can be helped.
Santiago Baldassin (sbaldassin) wrote : | # |
Thanks Robert. I've addressed all your comments and replied to a couple of them inline. I've remove the polling results part for now and I'll be submitting a new mp for that
Santiago Baldassin (sbaldassin) wrote : | # |
> Sorry, another question: Are you saying it's possible to trigger UST with an
> ajax request? Or is the ajax request only for querying the status after it's
> been triggered?
>
> If the former, I'd strongly prefer that over importing this qakit module,
> which imports jenkins module. Even when we used jenkins extensively, bileto
> only ever spoke JSON to it, I never had to import jenkins module, so I'd
> prefer not to start using that now if it can be helped.
I was thinking about an ajax request to get the results. About importing qakit, I'd like to keep it if you guys don't mind. Please take into account that qakit is not only talking to Jenkins but also to practitest and to launchpad. The beauty of using the jenkins lib is that you forget about dealing with http requests, catching errors, packing/unpacking requests and results, etc. By the way, the jenkins module is a well known and widely use module, so I'm not sure why we should not use it, that said, If there's any technical roadblock related to it, I'm open to change it
Robert Bruce Park (robru) wrote : | # |
Hey Santiago, apologies I've been meaning to dedicate more time to this but the bileto.ubuntu.com rollout was not particularly smooth so I've had a few fires to put out.
The single greatest challenge we will face is the fact that britney runs in precise, which limits us to python 3.2, which is really showing its age. It's not clear to me that qakit or jenkins modules are going to work in precise, and backporting them may well be harder than just writing a few http requests.
In terms of the bileto side, I like to keep things as simple as possible, so loading the big jenkins module in my server process is really unappealing to me (even if you resolve the issue of polling jenkins every time a user edits a ticket, I won't accept any code that imports jenkins in bileto/*.py) keep in mind that our flask server has 15 worker processes so anything imported in bileto/*.py is going to be imported in 15 different processes and stick around forever.
The significantly better approach is to just have one script called periodically from cron. It can load jenkins, iterate over tickets, submit some tests, and then exit without being a huge burden on the system for a prolonged period of time.
Santiago Baldassin (sbaldassin) wrote : | # |
Hi Robert, no need to apologize. I'll be working on getting rid of Jenkins in qakit and let you know when it's ready.
- 0c5bc46... by Santiago Baldassin
-
Moving the trigger for ust to iterate.py
- 3560ed2... by Santiago Baldassin
-
Merge branch 'master' of git+ssh:
//git.launchpad .net/bileto
Unmerged commits
- 0c5bc46... by Santiago Baldassin
-
Moving the trigger for ust to iterate.py
- cbf2617... by Santiago Baldassin
-
Adding ubuntu system tests signoff
This includes an additional signoff for the requests in bileto.
Once a silo is assigned to the requests and the lander approved
the changes, ubuntu system tests will be triggered - 3560ed2... by Santiago Baldassin
-
Merge branch 'master' of git+ssh:
//git.launchpad .net/bileto - 5f56daa... by Santiago Baldassin
-
Merge branch 'master' of https:/
/git.launchpad. net/bileto - 3f02943... by Robert Bruce Park
-
Retry git fetch a few times before giving up.
- 83f2a02... by Robert Bruce Park
-
PPAs are no longer Assigned, they are Active.
- e1975b4... by Robert Bruce Park
-
Add multiverse dependency as per cjwatson.
- 51950d6... by Robert Bruce Park
-
Create dir before copying into it.
- 7096cc8... by Robert Bruce Park
-
Increase authorized_size.
- 0ba81f4... by Robert Bruce Park
-
Ephemeral PPAs phase two: Use them for new tickets.
Preview Diff
1 | diff --git a/.bzr-builddeb/default.conf b/.bzr-builddeb/default.conf |
2 | new file mode 100644 |
3 | index 0000000..6c96a98 |
4 | --- /dev/null |
5 | +++ b/.bzr-builddeb/default.conf |
6 | @@ -0,0 +1,2 @@ |
7 | +[BUILDDEB] |
8 | +split = True |
9 | diff --git a/bileto/models.py b/bileto/models.py |
10 | index 5ea297f..a3a5bd2 100644 |
11 | --- a/bileto/models.py |
12 | +++ b/bileto/models.py |
13 | @@ -3,7 +3,6 @@ |
14 | This file defines the two DB schemas, Request and Comment, and various |
15 | metadata related to those. |
16 | """ |
17 | - |
18 | import re |
19 | |
20 | from os import environ |
21 | @@ -12,6 +11,7 @@ from datetime import datetime |
22 | from functools import lru_cache |
23 | from fcntl import LOCK_EX, LOCK_NB, flock |
24 | |
25 | +from qakit.ust.worker import Worker, UstResult |
26 | from sqlalchemy import BigInteger, desc, text |
27 | from sqlalchemy.inspection import inspect |
28 | from sqlalchemy.orm import relationship |
29 | @@ -53,6 +53,11 @@ BRITNEY_SIGNOFF = """Automated Signoff |
30 | Indicates if britney has approved the ticket based on autopkgtests & other \ |
31 | factors. Check the excuses page for details.""" |
32 | |
33 | +UST_SIGNOFF = """Ubuntu System Tests Signoff |
34 | + |
35 | +Indicates if ubuntu system tests has approved the silo""" |
36 | + |
37 | + |
38 | CREATOR = """Request Creator |
39 | |
40 | Launchpad nickname of the person who created this request.""" |
41 | @@ -103,6 +108,10 @@ QA = """QA Signoff |
42 | |
43 | Indicates whether or not the ticket has been approved by QA team.""" |
44 | |
45 | +UST = """UST Signoff |
46 | + |
47 | +Indicates whether or not the silo has been approved by Ubuntu system tests.""" |
48 | + |
49 | SERIES = """Target Series |
50 | |
51 | What series to build for, eg, wily, vivid, or yakkety+xenial+vivid.""" |
52 | @@ -239,6 +248,8 @@ class Request(db.Model, BiletoModel): |
53 | dest = db.Column(db.Text, default=EMPTY, doc=PPA) |
54 | lander_signoff = db.Column(db.Text, default=EMPTY, doc=LANDER_SIGNOFF) |
55 | britney_signoff = db.Column(db.Text, default=EMPTY, doc=BRITNEY_SIGNOFF) |
56 | + ust_signoff = db.Column(db.Text, default=EMPTY, doc=UST_SIGNOFF) |
57 | + ust_build_url = db.Column(db.Text, default=EMPTY, doc=UST_SIGNOFF) |
58 | qa_signoff = db.Column(db.Text, default=EMPTY, doc=QA) |
59 | |
60 | search = db.Column(db.Text, default=EMPTY, doc='Searchable Field') |
61 | @@ -329,6 +340,8 @@ class Request(db.Model, BiletoModel): |
62 | if lander_fail or any(clear in status for clear in CLEAR): |
63 | self.lander_signoff = EMPTY |
64 | self.britney_signoff = EMPTY |
65 | + self.ust_signoff = EMPTY |
66 | + self.ust_build_url = EMPTY |
67 | self.qa_signoff = 'Required' |
68 | self.autopkgtest = EMPTY |
69 | |
70 | diff --git a/bileto/static/index.html b/bileto/static/index.html |
71 | new file mode 100644 |
72 | index 0000000..9422e18 |
73 | --- /dev/null |
74 | +++ b/bileto/static/index.html |
75 | @@ -0,0 +1,285 @@ |
76 | +<!DOCTYPE html> |
77 | +<html ng-app="Bileto" ng-controller="appController"> |
78 | +<head> |
79 | +<meta charset="utf-8" /> |
80 | +<title>Bileto Landing Tickets</title> |
81 | +<link rel="stylesheet" type="text/css" href="/static/style.css"> |
82 | +<link rel="icon" type="image/gif" href="/static/goldenticket.png"> |
83 | +<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script> |
84 | +<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-sanitize.min.js"></script> |
85 | +<script src="/static/app.js" charset="utf-8"></script> |
86 | +</head> |
87 | +<body> |
88 | + |
89 | +<datalist ng-repeat="(list, items) in meta.datalists" id="{{list}}"> |
90 | +<option ng-repeat="item in items" value="{{item}}" /> |
91 | +</datalist> |
92 | + |
93 | +<div id="nav"> |
94 | +<div id="login"> |
95 | +<a href="/logout" ng-if="data.nickname">Log out {{data.nickname}}</a> |
96 | +<form name="sso_login" action="/login" method=post ng-if="!data.nickname"> |
97 | +<input type=submit value="Log in with Ubuntu SSO"> |
98 | +<input type=hidden name=next value="/#{{here}}"> |
99 | +</form> |
100 | +</div> |
101 | +<a href="#/create" ng-if="data.nickname">Create New Request</a> |
102 | +<a href="#" ng-click="editing = null">All Requests</a> |
103 | +<a href="#/silo/landing" ng-click="editing = null">{{data.assigned}} Active</a> |
104 | +<a href="#/user/{{data.ircnick}}" ng-click="editing = null" ng-if="data.ircnick">Mine</a> |
105 | +<a href="#/failures" ng-click="editing = null">Failures</a> |
106 | +<a href="#/tickets?active_only&lander_signoff=Approved" ng-click="editing = null">Lander Approved</a> |
107 | +<a href="#/tickets?active_only&qa_signoff=Ready" ng-click="editing = null">Ready for QA</a> |
108 | +<a href="#/publishable" ng-click="editing = null">Publishable</a> |
109 | +<a href="#/tickets?status=queue" ng-click="editing = null">Queued</a> |
110 | +<a href="#/tickets?status=pocket" ng-click="editing = null">Migrating</a> |
111 | +<a href="#/tickets?status=Landed" ng-click="editing = null">Landed</a> |
112 | +<a href="#/tickets?status=Abandoned" ng-click="editing = null">Abandoned</a> |
113 | +<form name="live_search"> |
114 | +<input type="search" class="search" placeholder="Search..." ng-model="searchstring" /> |
115 | +<input type="submit" ng-click="search(searchstring)" value="Search All Requests" /> |
116 | +<input type="submit" ng-click="search(searchstring, true)" value="Search Active Requests" /> |
117 | +<input type="submit" ng-click="siloname(searchstring)" value="Show PPA #" /> |
118 | +<input type="submit" ng-click="request(searchstring)" value="Show Ticket #" /> |
119 | +</form> |
120 | +</div> |
121 | + |
122 | +<div id="body"> |
123 | + |
124 | +<div id="error" ng-if="error"> |
125 | +<h2>{{error.header}}</h2> |
126 | +<img src="/static/error.gif"> |
127 | +<p>{{error.message}}</p> |
128 | +</div> |
129 | + |
130 | +<div ng-if="data.prev" class="center"> |
131 | +<a ng-click="prev()">... newer ...</a> |
132 | +</div> |
133 | + |
134 | +<div id="requests"> |
135 | +<div class="req" ng-repeat="req in data.requests" ng-class="{card: !expand}"> |
136 | + |
137 | +<div class="summary" ng-if="!expand" ng-click="request(req.request_id)"> |
138 | +<p><span class="{{req.status_color}}" title="{{interpret_color(req.status_color)}}"> </span> |
139 | +<span class="signoff_{{req.lander_signoff}}" title="Lander {{req.lander_signoff}}"> </span> |
140 | +<span class="signoff_{{req.britney_signoff}}" title="Britney {{req.britney_signoff}}"> </span> |
141 | +<span class="signoff_{{req.ust_signoff}}" title="UST {{req.britney_signoff}}"> </span> |
142 | +<span class="signoff_{{req.qa_signoff}}" title="QA {{req.qa_signoff}}"> </span></p> |
143 | +<p><a href="#/ticket/{{req.request_id}}">{{req.request_id}}. {{req.date | date:'yyyy-MM-dd'}} {{req.siloname}}</a></p> |
144 | +<p>{{req.sources}}</p> |
145 | +<p ng-bind-html="linkify(req.description)"></p> |
146 | +<p>{{req.landers}}</p> |
147 | +</div> |
148 | + |
149 | +<div class="expanded" ng-if="expand"> |
150 | +<div class="display" ng-if="!editing"> |
151 | +<form name="live_edit"> |
152 | +<table class="details"> |
153 | + |
154 | +<tr> |
155 | +<th class="signoff"> |
156 | +<a target="_blank" href="{{req.job_log}}" ng-if="req.job_log" class="status_details" title="Click for Details">» {{title(meta.docs.status)}}</a> |
157 | +<span ng-if="!req.job_log">{{title(meta.docs.status)}}</span> |
158 | +</th> |
159 | +<td ng-bind-html="display_status(req.status)" class="{{req.status_color}}"></td> |
160 | +</tr> |
161 | + |
162 | +<tr> |
163 | +<th class="signoff">{{title(meta.docs.lander_signoff)}}</th> |
164 | +<td class="signoff signoff_{{req.lander_signoff}}"> |
165 | +<select class="signoff signoff_{{req.lander_signoff}}" name="lander_signoff" title="{{meta.docs.lander_signoff}}" ng-change="submit(live_edit, req)" ng-model="req.lander_signoff" ng-options="value for value in meta.datalists.lander_signoff" ng-disabled="!data.teams.length"></select> |
166 | +</td> |
167 | +</tr> |
168 | + |
169 | +<tr> |
170 | +<th class="signoff">{{title(meta.docs.britney_signoff)}}</th> |
171 | +<td class="signoff_{{req.britney_signoff}}">{{req.britney_signoff}}</td> |
172 | +</tr> |
173 | + |
174 | +<tr> |
175 | +<th class="signoff">{{title(meta.docs.ust_signoff)}}</th> |
176 | +<td> |
177 | +<a href="{{req.ust_build_url}}"> |
178 | +<div class="signoff_{{req.ust_signoff}}"> |
179 | +{{req.ust_signoff}} |
180 | +</div> |
181 | +</a> |
182 | +</td> |
183 | +</tr> |
184 | + |
185 | +<tr> |
186 | +<th class="signoff">{{title(meta.docs.qa_signoff)}}</th> |
187 | +<td class="signoff signoff_{{req.qa_signoff}}"> |
188 | +<select class="signoff signoff_{{req.qa_signoff}}" name="qa_signoff" title="{{meta.docs.qa_signoff}}" ng-change="submit(live_edit, req)" ng-model="req.qa_signoff" ng-options="value for value in meta.datalists.qa_signoff" ng-disabled="!data.admin.length && !req.download_links"></select> |
189 | +</td> |
190 | +</tr> |
191 | + |
192 | +<tr> |
193 | +<th>{{title(meta.docs.artifacts)}}</th> |
194 | +<td ng-bind-html="linkify(req.artifacts)"></td> |
195 | +</tr> |
196 | + |
197 | +<tr ng-if="req.autopkgtest"> |
198 | +<th>{{title(meta.docs.autopkgtest)}}</th> |
199 | +<td ng-bind-html="linkify(req.autopkgtest)"></td> |
200 | +</tr> |
201 | + |
202 | +<tr ng-repeat="field in long_fields"> |
203 | +<th>{{title(meta.docs[field])}}</th> |
204 | +<td ng-bind-html="linkify(req[field])"></td> |
205 | +</tr> |
206 | + |
207 | +<tr> |
208 | +<th>{{title(meta.docs.siloname)}}</th> |
209 | +<td ng-bind-html="display_siloname(meta.ppa_team, req.siloname)"></td> |
210 | +</tr> |
211 | + |
212 | +<!-- FIXME: sync_request is bit-rotty and doesn't work. disabled for now. |
213 | +<tr> |
214 | +<th>{{title(meta.docs.sync_request)}}</th> |
215 | +<td>{{req.sync_request}}</td> |
216 | +</tr> |
217 | +--> |
218 | + |
219 | +<tr> |
220 | +<th>{{title(meta.docs.sources)}}</th> |
221 | +<td class="sources" ng-bind-html="display_sources(req.sources)"></td> |
222 | +</tr> |
223 | + |
224 | +<tr ng-repeat="field in dropdowns"> |
225 | +<th>{{title(meta.docs[field])}}</th> |
226 | +<td>{{req[field]}}</td> |
227 | +</tr> |
228 | + |
229 | +<tr ng-if="req.series.indexOf('+') === -1"> |
230 | +<th>{{title(meta.docs.dest)}}</th> |
231 | +<td ng-bind-html="display_dest(req.dest)"></td> |
232 | +</tr> |
233 | + |
234 | +<tr> |
235 | +<th>{{title(meta.docs.landers)}}</th> |
236 | +<td ng-bind-html="display_landers(req.landers)"></td> |
237 | +</tr> |
238 | + |
239 | +<tr> |
240 | +<th><a href="#/ticket/{{req.request_id}}">{{title(meta.docs.request_id)}}</a></th> |
241 | +<td><a href="#/ticket/{{req.request_id}}"> |
242 | +<span ng-bind-html="easter_egg(req.request_id)"></span> |
243 | +{{req.request_id}}. {{req.date | date:'yyyy-MM-dd'}} |
244 | +</a></td> |
245 | +</tr> |
246 | + |
247 | +</table> |
248 | +</form> |
249 | +</div> |
250 | +</div> |
251 | + |
252 | +<div class="edit" ng-if="editing"> |
253 | +<form name="edit_request"> |
254 | +<p> |
255 | +<textarea rows="6" name="description" |
256 | + placeholder="{{title(meta.docs.description)}}" title="{{meta.docs.description}}" |
257 | + ng-model="req.description" required>{{req.description}}</textarea> |
258 | +</p> |
259 | +<p> |
260 | +<textarea rows="6" name="test_plan" |
261 | + placeholder="{{title(meta.docs.test_plan)}}" title="{{meta.docs.test_plan}}" |
262 | + ng-model="req.test_plan" required>{{req.test_plan}}</textarea> |
263 | +</p> |
264 | +<p> |
265 | +<textarea rows="6" wrap="off" name="merge_proposals" |
266 | + placeholder="{{title(meta.docs.merge_proposals)}}" title="{{meta.docs.merge_proposals}}" |
267 | + ng-model="req.merge_proposals" ng-pattern="/^((https?:\/\/code.launchpad.net\/~\S+\/\+merge\/\d+|#.*)[\s\n]*)*$/">{{req.merge_proposals}}</textarea> |
268 | +</p> |
269 | +<p> |
270 | +<textarea rows="6" wrap="off" name="download_links" |
271 | + placeholder="{{title(meta.docs.download_links)}}" title="{{meta.docs.download_links}}" |
272 | + ng-model="req.download_links" ng-pattern="/^(https?:\/\/\S+[\s\n]*)*$/">{{req.download_links}}</textarea> |
273 | +</p> |
274 | +<p> |
275 | +<input type="text" name="landers" list="landers" size="10" |
276 | + placeholder="{{title(meta.docs.landers)}}" title="{{meta.docs.landers}}" |
277 | + value="{{req.landers}}" ng-model="req.landers" /> |
278 | +</p> |
279 | +<!-- FIXME: sync_request is bit-rotty and doesn't work. disabled for now. |
280 | +<p> |
281 | +<input type="text" name="sync_request" list="sync_request" size="10" |
282 | + placeholder="{{title(meta.docs.sync_request)}}" title="{{meta.docs.sync_request}}" |
283 | + value="{{req.sync_request}}" ng-model="req.sync_request" |
284 | + ng-pattern="/^(\d+|\S+,[a-z0-9.]+)$/"/> |
285 | +</p> |
286 | +--> |
287 | +<p> |
288 | +<input type="text" name="sources" list="sources" size="10" |
289 | + placeholder="{{title(meta.docs.sources)}}" title="{{meta.docs.sources}}" |
290 | + value="{{req.sources}}" ng-model="req.sources" /> |
291 | +</p> |
292 | +<p ng-repeat="field in dropdowns"> |
293 | +<select name="{{field}}" title="{{meta.docs[field]}}" ng-model="req[field]" ng-options="value for value in meta.datalists[field]"></select> |
294 | +</p> |
295 | +<p ng-if="req.series.indexOf('+') === -1"> |
296 | +<select name="dest" title="{{meta.docs.dest}}" ng-model="req.dest"> |
297 | +<option value="">Ubuntu Archive</option> |
298 | +<option value="ci-train-ppa-service/ubuntu/stable-phone-overlay">Stable Overlay PPA</option> |
299 | +</select> |
300 | +</p> |
301 | +<p> |
302 | +<input type="hidden" name="distribution" value="ubuntu" ng-model="req.distribution" /> |
303 | +<button ng-click="submit(edit_request, req)" ng-disabled="busy || !edit_request.$valid"> |
304 | +<span ng-if="req.request_id">Save #{{req.request_id}}</span> |
305 | +<span ng-if="!req.request_id">Create New Request</span> |
306 | +<span ng-if="!edit_request.$valid">(mouse over pink fields for help)</span> |
307 | +</button> |
308 | +<div class="red" ng-if="display_sru_warning(req)">Warning: Are you sure you want to |
309 | +publish an SRU?</div> |
310 | +</p> |
311 | +</form> |
312 | +</div> |
313 | + |
314 | +<div class="actionbox" ng-if="req.request_id && expand"> |
315 | +<table class="actions"><tr> |
316 | +<td ng-if="!editing"><a href="/log/{{req.request_id}}/build" target="_blank">Build</a></td> |
317 | +<td ng-if="req.siloname && !editing"><a href="/log/{{req.request_id}}/diff" target="_blank">Diff</a></td> |
318 | +<td ng-if="!editing"><a href="/log/{{req.request_id}}/revert" target="_blank">Revert</a></td> |
319 | +<td ng-if="req.siloname && !editing"><a href="/log/{{req.request_id}}/publish" target="_blank">Publish</a></td> |
320 | +<td ng-if="!editing"><a href="/log/{{req.request_id}}/finalize" target="_blank">Finalize</a></td> |
321 | +<td ng-if="!editing"><a href="/log/{{req.request_id}}/abandon" target="_blank">Abandon</a></td> |
322 | +<td ng-if="editing"><a ng-click="cancel()">Cancel Edit</a></td> |
323 | +<td ng-if="data.nickname && !editing"><a ng-click="edit(req)">Edit</a></td> |
324 | +</tr></table> |
325 | +</div> |
326 | + |
327 | +<div class="comments" ng-if="expand"> |
328 | +<a ng-click="toggle_log()" ng-if="expand && data.requests[0].comments.length"><span ng-if="!full_log">▸ Show</span><span ng-if="full_log">▾ Hide</span> Audit Logs</a> |
329 | +<div class="comment" ng-repeat="comment in req.comments" ng-if="full_log || !comment.log_url"> |
330 | +<strong> |
331 | +<a target="_blank" href="{{comment.log_url}}" ng-if="comment.log_url">{{comment.date | date:'yyyy-MM-dd HH:mm:ss Z'}} ({{comment.author}})</a> |
332 | +<span ng-if="!comment.log_url">{{comment.date | date:'yyyy-MM-dd HH:mm:ss Z'}} ({{comment.author}})</span> |
333 | +</strong> |
334 | +<span ng-bind-html="display_status(comment.text)"></span> |
335 | +</div> |
336 | +</div> |
337 | + |
338 | +<div class="new_comment" ng-if="req.request_id && data.nickname && expand && !editing"> |
339 | +<form name="new_comment_form"> |
340 | +<textarea class="commentbox" rows="2" name="comment_text" placeholder="Add a comment..." ng-model="new_comment.text"></textarea> |
341 | +<button ng-click="post_comment(req.request_id, new_comment.text)" ng-disabled="busy">Save Comment</button> |
342 | +</form> |
343 | +</div> |
344 | + |
345 | +</div> |
346 | + |
347 | +<div ng-if="data.next" class="center"> |
348 | +<a ng-click="next()">... older ...</a> |
349 | +</div> |
350 | + |
351 | +<div id="admin"> |
352 | +<a href="/static/logs.html">All Logs</a> |
353 | +<a href="/static/britney/index.html">Britney Files</a> |
354 | +<a href="/static/report.txt">Diagnostic Report</a> |
355 | +</div> |
356 | + |
357 | +<div id="busy" ng-if="busy"></div> |
358 | + |
359 | +</body> |
360 | +</html> |
361 | diff --git a/bileto/static/style.css b/bileto/static/style.css |
362 | new file mode 100644 |
363 | index 0000000..985e85f |
364 | --- /dev/null |
365 | +++ b/bileto/static/style.css |
366 | @@ -0,0 +1,384 @@ |
367 | +html, body { |
368 | + background-color: white; |
369 | + color: black; |
370 | + font-family: Ubuntu, Arial, sans-serif; |
371 | + margin: 0; |
372 | + padding: 0; |
373 | +} |
374 | + |
375 | +body { |
376 | + padding-bottom: 1em; |
377 | +} |
378 | + |
379 | +input, select, button, textarea { |
380 | + width: 100%; |
381 | + font-size: medium; |
382 | +} |
383 | + |
384 | +a { |
385 | + color: #dd4814; |
386 | + background-color: white; |
387 | +} |
388 | + |
389 | +a, input[type=submit], select, button, .summary { |
390 | + cursor: pointer; |
391 | + text-decoration: none; |
392 | + outline: 0; |
393 | + margin: 0; |
394 | + border: 0; |
395 | + border-radius: 0; |
396 | + font-size: medium; |
397 | +} |
398 | + |
399 | +#nav { |
400 | + text-align: center; |
401 | + width: auto; |
402 | + float: right; |
403 | +} |
404 | + |
405 | +#body { |
406 | + width: auto; |
407 | + overflow: hidden; |
408 | +} |
409 | + |
410 | +form { |
411 | + display: block; |
412 | + margin: 0; |
413 | + padding: 0; |
414 | + width: 100%; |
415 | +} |
416 | + |
417 | +h1, #nav a, #nav input, .new_comment button, .actions a { |
418 | + background-color: #dd4814; |
419 | + border: 0; |
420 | + color: white; |
421 | + display: block; |
422 | + margin: 0; |
423 | + padding: 1em; |
424 | + text-decoration: none; |
425 | + text-align: center; |
426 | +} |
427 | + |
428 | +.status_details { |
429 | + background-color: #dd4814; |
430 | + color: white; |
431 | + padding: 0.3em; |
432 | +} |
433 | + |
434 | +h1 { |
435 | + font-size: large; |
436 | + text-align: center; |
437 | + width: auto; |
438 | +} |
439 | + |
440 | +th { |
441 | + text-align: right; |
442 | + vertical-align: top; |
443 | + white-space: nowrap; |
444 | + padding-right: 0.5em; |
445 | +} |
446 | + |
447 | +td { |
448 | + vertical-align: top; |
449 | + text-align: left; |
450 | +} |
451 | + |
452 | +#nav input, .new_comment input { |
453 | + width: 100%; |
454 | +} |
455 | + |
456 | +#nav input[type=search] { |
457 | + border-top: 1px solid white; |
458 | +} |
459 | + |
460 | +#nav a:hover, #nav input:hover, h1:hover, |
461 | +.edit input[type=submit]:hover, .edit button:hover, |
462 | +.new_comment input:hover, .new_comment button:hover, |
463 | +.actions a:hover { |
464 | + text-decoration: none; |
465 | + background-color: white; |
466 | + color: #dd4814; |
467 | +} |
468 | + |
469 | +#requests, .display, .edit { |
470 | + display: block; |
471 | +} |
472 | + |
473 | +#requests { |
474 | + padding-top: 2em; |
475 | +} |
476 | + |
477 | +#requests > * { |
478 | + vertical-align: top; |
479 | +} |
480 | + |
481 | +.req { |
482 | + text-align: center; |
483 | +} |
484 | + |
485 | +.edit { |
486 | + margin-top: -2em; |
487 | +} |
488 | + |
489 | +.summary { |
490 | + padding: 0.5em 1em 1em 1em; |
491 | + max-height: 20em; |
492 | +} |
493 | + |
494 | +.summary p { |
495 | + margin: 0.5em; |
496 | +} |
497 | + |
498 | +.display { |
499 | + padding: 1em; |
500 | +} |
501 | + |
502 | +.card { |
503 | + width: 450px; |
504 | + display: inline-block; |
505 | + text-align: left; |
506 | + border-top: 1px solid #ccc; |
507 | + margin: 1em; |
508 | + overflow: auto; |
509 | +} |
510 | + |
511 | +.card, .display { |
512 | + margin-top: 0; |
513 | + padding-top: 0; |
514 | +} |
515 | + |
516 | +.details { |
517 | + width: auto; |
518 | + margin: 0 auto; |
519 | +} |
520 | + |
521 | +.details td, .details th { |
522 | + padding: 0.2em; |
523 | +} |
524 | + |
525 | +td.sources a { |
526 | + display: block; |
527 | +} |
528 | + |
529 | +.center { |
530 | + text-align: center; |
531 | +} |
532 | + |
533 | +input, textarea, button, select { |
534 | + width: 100%; |
535 | + display: block; |
536 | + color: black; |
537 | + background-color: #eee; |
538 | + padding: 0.5em; |
539 | + border: 0; |
540 | + box-sizing: border-box; |
541 | + -webkid-box-sizing: border-box; |
542 | + -moz-box-sizing: border-box; |
543 | +} |
544 | + |
545 | +.edit input[type=submit], .edit button { |
546 | + background-color: #dd4814; |
547 | + color: white; |
548 | + padding: 1em; |
549 | + border: 0; |
550 | +} |
551 | + |
552 | +p { |
553 | + margin: 1em 1em 0 1em; |
554 | +} |
555 | + |
556 | +.comments { |
557 | + display: inline-block; |
558 | + width: auto; |
559 | + padding: 0 2em; |
560 | + margin: 0 auto; |
561 | + text-align: left; |
562 | +} |
563 | + |
564 | +.new_comment { |
565 | + padding: 0; |
566 | + margin: 1em; |
567 | +} |
568 | + |
569 | +table, tr, td { |
570 | + border-collapse: collapse; |
571 | + margin: 0; |
572 | + padding: 0; |
573 | +} |
574 | + |
575 | +table { |
576 | + width: 100%; |
577 | +} |
578 | + |
579 | +.commentbox { |
580 | + width: 100%; |
581 | +} |
582 | + |
583 | +.actionbox { |
584 | + margin: 1em; |
585 | +} |
586 | + |
587 | +table.actions { |
588 | + table-layout: fixed; |
589 | +} |
590 | + |
591 | +table.actions tr, table.actions td { |
592 | + height: 100%; |
593 | + vertical-align: middle; |
594 | +} |
595 | + |
596 | +table.actions a { |
597 | + display: block; |
598 | + width: 100%; |
599 | + height: 100%; |
600 | + padding: 1em 0; |
601 | + vertical-align: middle; |
602 | + box-sizing: border-box; |
603 | + -webkid-box-sizing: border-box; |
604 | + -moz-box-sizing: border-box; |
605 | +} |
606 | + |
607 | +select.signoff { |
608 | + width: auto; |
609 | + display: inline-block; |
610 | +} |
611 | + |
612 | +/* These colors must be kept in sync with $scope.interpret_colors in app.js */ |
613 | +.black::before, .signoff_::before, .signoff_Required::before, |
614 | +.red::before, .signoff_Failed::before, |
615 | +.orange::before, .signoff_Queued::before, .signoff_Running::before, .signoff_Ready::before, |
616 | +.purple::before, |
617 | +.green::before, .signoff_N\/A::before, .signoff_Approved::before, |
618 | +.olive::before, |
619 | +.blue::before, |
620 | +.grey::before { |
621 | + font-size: xx-large; |
622 | + line-height: 0; |
623 | + display: inline-block; |
624 | + text-align: center; |
625 | + width: 1em; |
626 | +} |
627 | + |
628 | +.black, .signoff_, .signoff_Required { |
629 | + color: black; |
630 | +} |
631 | + |
632 | +.black::before, .signoff_::before, .signoff_Required::before { |
633 | + color: black; |
634 | + content: "— "; |
635 | +} |
636 | + |
637 | +.red, .signoff_Failed { |
638 | + color: red; |
639 | + font-weight: bold; |
640 | +} |
641 | + |
642 | +.red::before, .signoff_Failed::before { |
643 | + color: red; |
644 | + content: "✘ "; |
645 | +} |
646 | + |
647 | +.orange, .signoff_Queued, .signoff_Running, .signoff_Ready { |
648 | + color: orange; |
649 | + font-weight: bold; |
650 | +} |
651 | + |
652 | +.orange::before, .signoff_Queued::before, .signoff_Running::before, .signoff_Ready::before { |
653 | + color: orange; |
654 | + content: "⚡ "; |
655 | +} |
656 | + |
657 | +.purple { |
658 | + color: purple; |
659 | +} |
660 | + |
661 | +.purple::before { |
662 | + color: purple; |
663 | + content: "♨ "; |
664 | +} |
665 | + |
666 | +.green, .signoff_N\/A, .signoff_Approved { |
667 | + color: green; |
668 | +} |
669 | + |
670 | +.green::before, .signoff_N\/A::before, .signoff_Approved::before { |
671 | + color: green; |
672 | + content: "✔ "; |
673 | +} |
674 | + |
675 | +.olive { |
676 | + color: olive; |
677 | + font-weight: bold; |
678 | +} |
679 | + |
680 | +.olive::before { |
681 | + color: olive; |
682 | + content: "✱ "; |
683 | +} |
684 | + |
685 | +.blue { |
686 | + color: blue; |
687 | +} |
688 | + |
689 | +.blue::before { |
690 | + color: blue; |
691 | + content: "✔ "; |
692 | +} |
693 | + |
694 | +.grey { |
695 | + color: grey; |
696 | +} |
697 | + |
698 | +.grey::before { |
699 | + color: #888; |
700 | + content: "◌ "; |
701 | +} |
702 | + |
703 | +#busy { |
704 | + background-color: black; |
705 | + color: black; |
706 | + height: 100%; |
707 | + left: 0; |
708 | + margin: 0; |
709 | + opacity: 0.3; |
710 | + padding: 0; |
711 | + position: absolute; |
712 | + top: 0; |
713 | + width: 100%; |
714 | +} |
715 | + |
716 | +.fifty { |
717 | + width: 50%; |
718 | + display: inline-block; |
719 | +} |
720 | + |
721 | +#error { |
722 | + width: 100%; |
723 | + text-align: center; |
724 | +} |
725 | + |
726 | +textarea.ng-invalid, input.ng-invalid { |
727 | + background-color: #ffe5e5; |
728 | +} |
729 | + |
730 | +button[disabled], button[disabled]:hover, button[disabled]:focus, button[disabled]:active, |
731 | +select[disabled], select[disabled]:hover, select[disabled]:focus, select[disabled]:active { |
732 | + color: #666; |
733 | + background-color: #ccc; |
734 | + cursor: default; |
735 | +} |
736 | + |
737 | +.signoff { |
738 | + vertical-align: middle; |
739 | +} |
740 | + |
741 | +#admin { |
742 | + margin: 3em 0 0 0; |
743 | + text-align: center; |
744 | + font-size: x-small; |
745 | +} |
746 | + |
747 | +#admin a { |
748 | + margin: 1em; |
749 | + font-size: x-small; |
750 | +} |
751 | diff --git a/britney/iterate.py b/britney/iterate.py |
752 | index edd9b35..08d3bae 100755 |
753 | --- a/britney/iterate.py |
754 | +++ b/britney/iterate.py |
755 | @@ -20,6 +20,7 @@ from fcntl import LOCK_EX, LOCK_NB, flock |
756 | from os import unlink, symlink |
757 | from os.path import abspath, dirname, exists |
758 | |
759 | +from qakit.ust.worker import UstResult, Worker |
760 | from requests import get, post |
761 | |
762 | HOME = dirname(abspath(__file__)) |
763 | @@ -179,6 +180,34 @@ def parallelize_fetching(all_series, runs): |
764 | update_request(ticket) |
765 | |
766 | |
767 | +def compute_ust_signoff(requests): |
768 | + """Notify the ticket status in ubuntu system tests. |
769 | + Once a silo is assigned to the ticket, ust is triggered only if |
770 | + the lander has approved the ticket. After triggering the tests, |
771 | + the worker is asked for results |
772 | + :return UstResult which has two attributes: status and build url |
773 | + which are both use in the UI""" |
774 | + ust_signoff = None |
775 | + for request in requests: |
776 | + if not request['siloname']: |
777 | + ust_worker = Worker() |
778 | + if request['ust_signoff'] == "": |
779 | + if request['lander_signoff'] == 'Approved': |
780 | + try: |
781 | + ust_worker.run_ust(request['siloname']) |
782 | + ust_signoff = UstResult("Queued", "") |
783 | + except Exception: |
784 | + pass |
785 | + |
786 | + if ust_signoff: |
787 | + req = dict( |
788 | + author='ust-bot', |
789 | + request_id=request['request_id'], |
790 | + ust_signoff=ust_signoff.status) |
791 | + post(WRITE_API + Config.api_token, dumps(req, sort_keys=True), |
792 | + headers={'content-type': 'application/json'}) |
793 | + |
794 | + |
795 | def print_proc(proc): |
796 | """Print the output from a subprocess.""" |
797 | sys.stdout.buffer.write(BNEWLINE.join( |
798 | @@ -194,6 +223,7 @@ def main(): |
799 | sys.stderr = sys.stdout = open(LOG_NAME, 'x') |
800 | reqs = get_requests() |
801 | parallelize_fetching(*build_siloname_dict(reqs)) |
802 | + compute_ust_signoff(reqs) |
803 | except BlockingIOError: |
804 | return 0 |
805 | except (IOError, ConnectionRefusedError): |
806 | @@ -205,6 +235,5 @@ def main(): |
807 | cleanup(lock) |
808 | return 0 |
809 | |
810 | - |
811 | if __name__ == '__main__': # pragma: no cover |
812 | sys.exit(main()) |
813 | diff --git a/db_migrations.sh b/db_migrations.sh |
814 | index e346c14..f6a8b3f 100755 |
815 | --- a/db_migrations.sh |
816 | +++ b/db_migrations.sh |
817 | @@ -32,6 +32,7 @@ exit 0 |
818 | # These ones have succeeded in production and only kept for reference |
819 | $PG "ALTER TABLE request ADD COLUMN lander_signoff text DEFAULT '';" || true |
820 | $PG "ALTER TABLE request ADD COLUMN britney_signoff text DEFAULT '';" || true |
821 | +$PG "ALTER TABLE request ADD COLUMN ust_signoff text DEFAULT '';" || true |
822 | $PG "ALTER TABLE request ADD COLUMN status_color text DEFAULT '';" || true |
823 | $PG "ALTER TABLE request ADD COLUMN artifacts text DEFAULT '';" || true |
824 | |
825 | diff --git a/scripts/import.sh b/scripts/import.sh |
826 | new file mode 100755 |
827 | index 0000000..60b8173 |
828 | --- /dev/null |
829 | +++ b/scripts/import.sh |
830 | @@ -0,0 +1,26 @@ |
831 | +#!/bin/sh |
832 | + |
833 | +# This is a history-destroying git-to-bzr converter, |
834 | +# for when you just want the code. |
835 | + |
836 | +repos="https://github.com/emacs-mirror/emacs.git" |
837 | + |
838 | +sleep "$(shuf --input-range 0-60 --head-count 1)" |
839 | + |
840 | +set -eux |
841 | + |
842 | + |
843 | +for repo in $repos; do |
844 | + base="$(basename "$repo" .git)" |
845 | + tmpdir="/tmp/$base" |
846 | + |
847 | + [ -d "$tmpdir" ] || git clone --depth 1 "$repo" "$tmpdir" |
848 | + cd "$tmpdir" |
849 | + git pull |
850 | + |
851 | + [ -d ".bzr" ] || bzr init . |
852 | + echo '.git*' > .bzrignore |
853 | + bzr add . |
854 | + bzr commit --message "Auto commit $(date)" |
855 | + bzr push "lp:~/$base/imported" --overwrite |
856 | +done |
Thanks for rebasing your works Santiago, apologies again for the git history situation. Some comments inline.
Before you fix any of the issues I raise in my review, though, we need to consider that Steve and Martin both want this triggered from Britney, so that it can work again during proposed-migration.
Also, during the meeting, we agreed that we would transition this in slowly, such that at first we just trigger the tests without reporting them anywhere, so that results can be manually inspected. Today I'll be digging into the britney code to figure out the best way to trigger this.