Merge lp:~psivaa/britney/announce-generic-testclient into lp:~canonical-ci-engineering/britney/queued-announce-and-collect

Proposed by Para Siva
Status: Needs review
Proposed branch: lp:~psivaa/britney/announce-generic-testclient
Merge into: lp:~canonical-ci-engineering/britney/queued-announce-and-collect
Diff against target: 211 lines (+174/-0)
3 files modified
britney.conf (+4/-0)
britney.py (+52/-0)
testclient.py (+118/-0)
To merge this branch: bzr merge lp:~psivaa/britney/announce-generic-testclient
Reviewer Review Type Date Requested Status
Celso Providelo (community) Needs Fixing
Review via email: mp+259666@code.launchpad.net

Commit message

Functionality to post a package details to pm.candidates queue

Description of the change

Functionality to post a package details to pm.candidates queue. Still to do is to update the excuses file with the entries. This will be part of another MP.

To post a comment you must log in.
439. By Para Siva

Move caching decision to testclient

Revision history for this message
Celso Providelo (cprov) wrote :

Psivaa,

We discussed this change in a hangout ...

Basically we should cleanup the configuration setup (remove DEBUG, ARCHES and FETCH and replace your 'products' by 'required_test', everything prefixed with TESTCLIENT_) and the TestClient logic (cache per series, plain dictionary keyed by (sourcename, version), json.{dump, load}, replace needs_announce with 'if (name, version) in cache.keys()', re-use connection & SimpleQueue to announce everything in that batch).

Let's give it another round of review before staging the changes.

review: Needs Fixing
440. By Para Siva

After review meeting first pass

441. By Para Siva

Remove an entry from the cache if new version comes up

442. By Para Siva

Remove entry from excuses

443. By Para Siva

Replace source with candidate name

Revision history for this message
Para Siva (psivaa) wrote :

Thanks cprov for the comments and the hangout. Greatly helped.

As requested in irc, i'd like a re-review please.
Thanks

Revision history for this message
Celso Providelo (cprov) wrote :

Psivaa,

When you arrive this morning, please take a look at https://code.launchpad.net/~cprov/britney/testclient-api/+merge/259668 and check if my refactoring of the announcer (based on the work I've done for the collector) makes sense.

My MP still pending integration tests and better error handling (logging, at least).

Unmerged revisions

443. By Para Siva

Replace source with candidate name

442. By Para Siva

Remove entry from excuses

441. By Para Siva

Remove an entry from the cache if new version comes up

440. By Para Siva

After review meeting first pass

439. By Para Siva

Move caching decision to testclient

438. By Para Siva

pep8 and pyflakes

437. By Para Siva

A couple of tests

436. By Para Siva

Second pass with dup package entries

435. By Para Siva

Announce source package initial commit

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'britney.conf'
2--- britney.conf 2015-03-05 14:57:03 +0000
3+++ britney.conf 2015-05-21 18:43:46 +0000
4@@ -70,3 +70,7 @@
5 BOOTTEST_DEBUG = yes
6 BOOTTEST_ARCHES = armhf amd64
7 BOOTTEST_FETCH = yes
8+
9+TESTCLIENT_ENABLE = yes
10+TESTCLIENT_REQUIRED_TESTS = selftest
11+TESTCLIENT_AMQP_URIS = amqp://guest:guest@162.213.32.181:5672//
12
13=== modified file 'britney.py'
14--- britney.py 2015-02-20 19:02:00 +0000
15+++ britney.py 2015-05-21 18:43:46 +0000
16@@ -227,6 +227,7 @@
17 PROVIDES, RDEPENDS, RCONFLICTS, MULTIARCH, ESSENTIAL)
18 from autopkgtest import AutoPackageTest, ADT_PASS, ADT_EXCUSES_LABELS
19 from boottest import BootTest
20+from testclient import TestClient
21
22
23 __author__ = 'Fabio Tranchitella and the Debian Release Team'
24@@ -1677,6 +1678,7 @@
25 excuse.addhtml("Not considered")
26 excuse.run_autopkgtest = run_autopkgtest
27 excuse.run_boottest = run_boottest
28+ #excuse.run_producttest = run_producttest
29
30 self.excuses.append(excuse)
31 return update_candidate
32@@ -1984,6 +1986,56 @@
33 upgrade_me.remove(excuse.name)
34 unconsidered.append(excuse.name)
35
36+ if (getattr(self.options, "testclient_enable", "no") == "yes" and
37+ self.options.series):
38+ testing_excuses = []
39+ for excuse in self.excuses:
40+ # Skip removals, binary-only candidates, proposed-updates
41+ # and unknown versions.
42+ if (excuse.name.startswith("-") or
43+ "/" in excuse.name or
44+ "_" in excuse.name or
45+ excuse.ver[1] == "-"):
46+ continue
47+ testing_excuses.append(excuse)
48+
49+ testclient = TestClient(
50+ self, self.options.distribution, self.options.series)
51+
52+ if not self.options.dry_run:
53+ testclient.announce_src(testing_excuses,
54+ self.options.testclient_amqp_uris.strip())
55+
56+ required_tests = getattr(
57+ self.options, "testclient_required_tests", "").split()
58+
59+ for excuse in testing_excuses:
60+ hints = self.hints.search('force', package=excuse.name)
61+ hints.extend(
62+ self.hints.search('force-badtest', package=excuse.name))
63+ forces = [x for x in hints
64+ if same_source(excuse.ver[1], x.version)]
65+ for test in testclient.getTests(excuse.name):
66+ excuse.addhtml(
67+ "%s result: %s (<a href=\"%s\">results</a>)" % (
68+ test.name, test.status, test.result_url))
69+ if forces:
70+ excuse.addhtml(
71+ "Should wait for %s %s %s, but forced by "
72+ "%s" % (excuse.name, excuse.ver[1],
73+ test.name, forces[0].user))
74+ continue
75+ if test.name not in required_tests:
76+ continue
77+ if test.status not in TestClient.VALID_STATUSES:
78+ excuse.addreason(test.name)
79+ if excuse.is_valid:
80+ excuse.is_valid = False
81+ excuse.addhtml("Not considered")
82+ upgrade_me.remove(excuse.name)
83+ unconsidered.append(excuse.name)
84+
85+
86 # invalidate impossible excuses
87 for e in self.excuses:
88 # parts[0] == package name
89
90=== added file 'testclient.py'
91--- testclient.py 1970-01-01 00:00:00 +0000
92+++ testclient.py 2015-05-21 18:43:46 +0000
93@@ -0,0 +1,118 @@
94+# -*- coding: utf-8 -*-
95+
96+# Copyright (C) 2015 Canonical Ltd.
97+
98+# This program is free software; you can redistribute it and/or modify
99+# it under the terms of the GNU General Public License as published by
100+# the Free Software Foundation; either version 2 of the License, or
101+# (at your option) any later version.
102+
103+# This program is distributed in the hope that it will be useful,
104+# but WITHOUT ANY WARRANTY; without even the implied warranty of
105+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
106+# GNU General Public License for more details.
107+
108+import json
109+from kombu import Connection
110+import os
111+import time
112+
113+
114+class TestClient(object):
115+
116+ VALID_STATUSES = ('PASS',)
117+
118+ EXCUSE_LABELS = {
119+ "PASS": '<span style="background:#87d96c">Pass</span>',
120+ "FAIL": '<span style="background:#ff6666">Regression</span>',
121+ "RUNNING": '<span style="background:#99ddff">Test in progress</span>',
122+ }
123+
124+ def __init__(self, britney, distribution, series):
125+ self.britney = britney
126+ self.distribution = distribution
127+ self.series = series
128+
129+ def _cache_file_name(self):
130+ """Returns the name of the cache file
131+
132+ Provides the file name for write_cache and read_cache
133+ """
134+ home = os.path.expanduser("~")
135+ cache_file = os.path.abspath(os.path.join(home, 'testclient',
136+ '{}.json'.format(self.series)))
137+ return cache_file
138+
139+ def announce_src(self, testing_excuses, amqp_uris):
140+ """Puts new src details in `pm.candidates` queue"""
141+
142+ cached_entries = self._read_cache()
143+
144+ to_be_announced = []
145+ for excuse in testing_excuses:
146+ if excuse in cached_entries:
147+ continue
148+ else:
149+ to_be_announced.append(excuse)
150+
151+ with Connection(amqp_uris) as connection:
152+ try:
153+ queue = connection.SimpleQueue(
154+ 'pm.candidates')
155+ for candidate in to_be_announced:
156+ payload = {
157+ 'source': candidate.name,
158+ 'version': candidate.version,
159+ 'distribution': self.distribution,
160+ 'series': self.series,
161+ }
162+ queue.put(payload)
163+ self._write_cache({candidate.name, candidate.version})
164+ except Exception as exc:
165+ print("E: [%s] - Queueing package: %s erred with %s" %
166+ (time.asctime(), candidate.name, exc))
167+ finally:
168+ queue.close()
169+
170+ def _write_cache(self, candidate):
171+ """Writes new src details in to the cache file
172+
173+ This helps decide if a package, version needs
174+ to be announced
175+ """
176+ cache_file = self._cache_file_name()
177+
178+ if not os.path.exists(os.path.dirname(cache_file)):
179+ os.makedirs(os.path.dirname(cache_file))
180+
181+ data = self._read_cache()
182+ to_remove = []
183+
184+ with open(cache_file, 'w') as outfile:
185+ for cached_items in data:
186+ for key, value in cached_items.items():
187+ # Remove the entries from the cache if
188+ # another version of an entry is coming new
189+ if candidate.name == key and candidate.version != value:
190+ to_remove.append(cached_items)
191+ if {candidate.name: candidate.version} not in data:
192+ data.append({candidate.name: candidate.version})
193+ for removable in to_remove:
194+ data.remove(removable)
195+ json.dump(data, outfile)
196+
197+ def _read_cache(self):
198+ """Reads the cache file contents
199+
200+ This helps decide if a package, version needs
201+ to be announced
202+ """
203+ cache_file = self._cache_file_name()
204+ entries = []
205+ if not os.path.exists(cache_file):
206+ return entries
207+ with open(cache_file) as infile:
208+ data = json.load(infile)
209+ for k in data:
210+ entries.append(k)
211+ return entries

Subscribers

People subscribed via source and target branches