Merge ppa-dev-tools:command-tests into ppa-dev-tools:main

Proposed by Bryce Harrington
Status: Merged
Merge reported by: Bryce Harrington
Merged at revision: 8abb18213477cb686676621a204a83a436235c57
Proposed branch: ppa-dev-tools:command-tests
Merge into: ppa-dev-tools:main
Diff against target: 268 lines (+164/-8)
7 files modified
AUTHORS.md (+1/-0)
ppa/constants.py (+20/-0)
ppa/io.py (+33/-0)
ppa/job.py (+31/-5)
ppa/ppa.py (+3/-3)
scripts/ppa (+44/-0)
tests/test_io.py (+32/-0)
Reviewer Review Type Date Requested Status
Athos Ribeiro (community) Approve
Canonical Server Reporter Pending
Review via email: mp+428951@code.launchpad.net

Description of the change

Adds the command 'ppa tests' which for now just reports the running and waiting tests. (Later, Results will be added as well.)

Since running and waiting info is only available while a PPA's tests are in process, to check the functionality you'll need to queue up some fresh tests for your favorite PPA; e.g.:

    $ lp-test-ppa ppa:bryce/dovecot-merge-v1e2.3.19.1adfsg1-2 -r kinetic --showurl

Trigger several, then wait a few minutes and they should start appearing in output:

    $ ./scripts/ppa tests ppa:bryce/dovecot-merge-v1e2.3.19.1adfsg1-2
Running:
    time pkg release arch ppa trigger
    10 dovecot kinetic armhf bryce/dovecot-merge-v1e2.3.19.1adfsg1-2 dovecot/1:2.3.19.1+dfsg1-2ubuntu2~kinetic1
    120 dovecot kinetic armhf bryce/dovecot-merge-v1e2.3.19.1adfsg1-2 dovecot/1:2.3.19.1+dfsg1-2ubuntu2~kinetic1
    270 dovecot kinetic amd64 bryce/dovecot-merge-v1e2.3.19.1adfsg1-2 dovecot/1:2.3.19.1+dfsg1-2ubuntu2~kinetic1
    30 dovecot kinetic arm64 bryce/dovecot-merge-v1e2.3.19.1adfsg1-2 dovecot/1:2.3.19.1+dfsg1-2ubuntu2~kinetic1
    30 dovecot kinetic ppc64el bryce/dovecot-merge-v1e2.3.19.1adfsg1-2 dovecot/1:2.3.19.1+dfsg1-2ubuntu2~kinetic1
Waiting:
    Q-num pkg release arch ppa trigger
    1 dovecot kinetic s390x bryce/dovecot-merge-v1e2.3.19.1adfsg1-2 dovecot/1:2.3.19.1+dfsg1-2ubuntu2~kinetic1
    1 dovecot kinetic armhf bryce/dovecot-merge-v1e2.3.19.1adfsg1-2 dovecot/1:2.3.19.1+dfsg1-2ubuntu2~kinetic1
    1 dovecot kinetic amd64 bryce/dovecot-merge-v1e2.3.19.1adfsg1-2 dovecot/1:2.3.19.1+dfsg1-2ubuntu2~kinetic1

To post a comment you must log in.
Revision history for this message
Athos Ribeiro (athos-ribeiro) wrote :

LGTM! Thanks, Bryce.

I included a couple inline nitpicks. As usual, feel free to ignore some or all of them. This looks good as is :)

review: Approve
Revision history for this message
Bryce Harrington (bryce) wrote :

Thanks for the review. Updated, merged and landed:

$ git push origin main
Total 0 (delta 0), reused 0 (delta 0)
To git+ssh://git.launchpad.net/ppa-dev-tools
   8fcdc4f..84609b3 main -> main

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/AUTHORS.md b/AUTHORS.md
2index 9712ae3..6138e91 100644
3--- a/AUTHORS.md
4+++ b/AUTHORS.md
5@@ -1 +1,2 @@
6 Bryce Harrington <bryce@canonical.com>
7+Athos Ribeiro <athos.ribeiro@canonical.com>
8diff --git a/ppa/constants.py b/ppa/constants.py
9new file mode 100644
10index 0000000..f6e7b01
11--- /dev/null
12+++ b/ppa/constants.py
13@@ -0,0 +1,20 @@
14+#!/usr/bin/env python3
15+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
16+
17+# Copyright (C) 2022 Authors
18+#
19+# Released under GNU GPLv2 or later, read the file 'LICENSE.GPLv2+' for
20+# more information.
21+#
22+# Authors:
23+# Bryce Harrington <bryce@canonical.com>
24+
25+"""Global constants"""
26+
27+ARCHES_ALL = ["amd64", "arm64", "armhf", "armel", "i386", "powerpc", "ppc64el", "s390x", "riscv64"]
28+ARCHES_PPA = ["amd64", "arm64", "armhf", "i386", "powerpc", "ppc64el", "s390x"]
29+ARCHES_PPA_EXTRA = ["riscv64"]
30+ARCHES_AUTOPKGTEST = ["amd64", "arm64", "armhf", "i386", "ppc64el", "s390x"]
31+
32+URL_LPAPI = "https://api.launchpad.net/devel"
33+URL_AUTOPKGTEST = "https://autopkgtest.ubuntu.com"
34diff --git a/ppa/io.py b/ppa/io.py
35new file mode 100644
36index 0000000..bef1fa0
37--- /dev/null
38+++ b/ppa/io.py
39@@ -0,0 +1,33 @@
40+#!/usr/bin/env python3
41+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
42+
43+# Copyright (C) 2022 Authors
44+#
45+# Released under GNU GPLv2 or later, read the file 'LICENSE.GPLv2+' for
46+# more information.
47+#
48+# Authors:
49+# Bryce Harrington <bryce@canonical.com>
50+
51+"""Utilities for reading input and writing output to external locations."""
52+
53+import sys
54+import urllib.request
55+
56+
57+def open_url(url, desc="data"):
58+ """Opens a remote URL for reading.
59+
60+ :rtype: urllib.request.Request
61+ :returns: A request object for the stream to read from, or None on error.
62+ """
63+ request = urllib.request.Request(url)
64+ request.add_header('Cache-Control', 'max-age=0')
65+ try:
66+ return urllib.request.urlopen(request)
67+ except urllib.error.HTTPError as e:
68+ # 401 here means nothing is published or ran yet.
69+ # This is a rather common case, so skip mention of it
70+ if e.code != 401:
71+ sys.stderr.write(f"Error: Could not retrieve {desc} from {url}: {e}")
72+ return None
73diff --git a/ppa/job.py b/ppa/job.py
74index 61d6fb2..2ae06ba 100755
75--- a/ppa/job.py
76+++ b/ppa/job.py
77@@ -14,10 +14,7 @@
78 from typing import Iterator
79 import json
80
81-# Global constants
82-ARCHES = ["amd64", "s390x", "ppc64el", "arm64", "armhf", "riscv64"]
83-URL_LPAPI = "https://api.launchpad.net/devel"
84-URL_AUTOPKGTEST = "https://autopkgtest.ubuntu.com"
85+from .constants import URL_AUTOPKGTEST
86
87
88 class Job:
89@@ -142,6 +139,36 @@ def get_waiting(response, series=None, ppa=None) -> Iterator[Job]:
90 yield job
91
92
93+def show_running(jobs):
94+ """Prints the active (running and waiting) tests"""
95+ rformat = " %-8s %-40s %-8s %-8s %-40s %s"
96+
97+ n = 0
98+ for n, e in enumerate(jobs, start=1):
99+ if n == 1:
100+ print("Running:")
101+ ppa_str = ','.join(e.ppas)
102+ trigger_str = ','.join(e.triggers)
103+ print(rformat % ("time", "pkg", "release", "arch", "ppa", "trigger"))
104+ print(rformat % (str(e.submit_time), e.source_package, e.series, e.arch, ppa_str, trigger_str))
105+ if n == 0:
106+ print("Running: (none)")
107+
108+
109+def show_waiting(jobs):
110+ """Prints the active (running and waiting) tests"""
111+ rformat = " %-8s %-40s %-8s %-8s %-40s %s"
112+
113+ n = 0
114+ for n, e in enumerate(jobs, start=1):
115+ if n == 1:
116+ print("Waiting:")
117+ print(rformat % ("Q-num", "pkg", "release", "arch", "ppa", "trigger"))
118+ print(rformat % (e.number, e.source_package, e.series, e.arch, ','.join(e.ppas), ','.join(e.triggers)))
119+ if n == 0:
120+ print("Waiting: (none)")
121+
122+
123 if __name__ == "__main__":
124 import os
125 from urllib.request import urlopen
126@@ -180,4 +207,3 @@ if __name__ == "__main__":
127 response = urlopen(f"file://{root_dir}/tests/data/queues-20220822.json")
128 for job in get_waiting(response, 'kinetic', ppa):
129 print(job)
130-
131diff --git a/ppa/ppa.py b/ppa/ppa.py
132index 62e0d58..0b90fd6 100755
133--- a/ppa/ppa.py
134+++ b/ppa/ppa.py
135@@ -12,6 +12,8 @@ from textwrap import indent
136 from functools import lru_cache
137 from lazr.restfulclient.errors import BadRequest, NotFound
138
139+from .constants import ARCHES_PPA
140+
141 def ppa_address_split(ppa_address, default_team='me'):
142 """Parse an address for a ppa into its team and name components
143 """
144@@ -52,8 +54,6 @@ class Ppa:
145 This object proxies a PPA, allowing lazy initialization and caching
146 of data from the remote.
147 """
148- # TODO: May need to load this from a (cached?) query
149- ALL_ARCHITECTURES = [ 'amd64', 'arm64', 'armel', 'armhf', 'i386', 'powerpc', 'ppc64el', 's390x']
150 def __init__(self, ppa_name, team_name, ppa_description=None, service=None):
151 """Initializes a new Ppa object for a given PPA
152
153@@ -145,7 +145,7 @@ class Ppa:
154 def architectures(self):
155 return [ proc.name for proc in self.archive.processors ]
156
157- def set_architectures(self, architectures=ALL_ARCHITECTURES):
158+ def set_architectures(self, architectures=ARCHES_PPA):
159 assert self._service
160 uri_base = "https://api.launchpad.net/devel/+processors/{}"
161 procs = [ uri_base.format(arch) for arch in architectures ]
162diff --git a/scripts/ppa b/scripts/ppa
163index 1697999..56ff827 100755
164--- a/scripts/ppa
165+++ b/scripts/ppa
166@@ -62,6 +62,15 @@ if '__file__' in globals():
167 os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")))
168
169 from ppa._version import __version__
170+from ppa.constants import URL_AUTOPKGTEST
171+from ppa.io import open_url
172+from ppa.job import (
173+ Job,
174+ get_waiting,
175+ show_waiting,
176+ get_running,
177+ show_running
178+)
179 from ppa.lp import Lp
180 from ppa.ppa import Ppa
181 from ppa.ppa_group import PpaGroup, PpaAlreadyExists
182@@ -440,6 +449,40 @@ def command_wait(lp, config):
183 return 1
184
185
186+def command_tests(lp, config):
187+ """Displays testing status for the PPA.
188+
189+ :param Lp lp: The Launchpad wrapper object.
190+ :param dict config: Configuration param:value map.
191+ :rtype: Int
192+ :returns: Status code 0 on success, non-zero on error.
193+ """
194+ if not lp:
195+ return 1
196+
197+ try:
198+ ppa = get_ppa(lp, config)
199+ target = "%s/%s" % (ppa.team_name, ppa.name)
200+ release = 'kinetic'
201+
202+ # Running Queue
203+ response = open_url(f"{URL_AUTOPKGTEST}/static/running.json", "running autopkgtests")
204+ if response:
205+ show_running(sorted(get_running(response, series=release, ppa=target),
206+ key=lambda k: str(k.submit_time)))
207+
208+ # Waiting Queue
209+ response = open_url(f"{URL_AUTOPKGTEST}/queues.json", "waiting autopkgtests")
210+ if response:
211+ show_waiting(get_waiting(response, series=release, ppa=target))
212+
213+ return 0
214+ except KeyboardInterrupt:
215+ return 2
216+ print("Unhandled error")
217+ return 1
218+
219+
220 COMMANDS = {
221 'create': (command_create, None),
222 'desc': (command_desc, None),
223@@ -447,6 +490,7 @@ COMMANDS = {
224 'list': (command_list, None),
225 'show': (command_show, None),
226 'status': (command_status, None),
227+ 'tests': (command_tests, None),
228 'wait': (command_wait, None),
229 }
230
231diff --git a/tests/test_io.py b/tests/test_io.py
232new file mode 100644
233index 0000000..fce6611
234--- /dev/null
235+++ b/tests/test_io.py
236@@ -0,0 +1,32 @@
237+#!/usr/bin/env python3
238+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
239+
240+# Author: Bryce Harrington <bryce@canonical.com>
241+#
242+# Copyright (C) 2022 Bryce W. Harrington
243+#
244+# Released under GNU GPLv2 or later, read the file 'LICENSE.GPLv2+' for
245+# more information.
246+
247+"""Tests for utilities used to read and write data externally."""
248+
249+import os
250+import sys
251+import urllib
252+import pytest
253+
254+sys.path.insert(0, os.path.realpath(
255+ os.path.join(os.path.dirname(__file__), "..")))
256+
257+from ppa.io import open_url
258+
259+
260+def test_open_url(tmp_path):
261+ """Checks that the open_url() object reads from a valid URL."""
262+ f = tmp_path / "open_url.txt"
263+ f.write_text("abcde")
264+
265+ request = open_url(f"file://{f}")
266+ assert request
267+ assert type(request) == urllib.response.addinfourl
268+ assert request.read().decode() == 'abcde'

Subscribers

People subscribed via source and target branches

to all changes: