Merge ~ines-almeida/launchpad-buildd:close-session-for-fetch-service into launchpad-buildd:master

Proposed by Ines Almeida
Status: Rejected
Rejected by: Ines Almeida
Proposed branch: ~ines-almeida/launchpad-buildd:close-session-for-fetch-service
Merge into: launchpad-buildd:master
Prerequisite: ~ines-almeida/launchpad-buildd:update-close-session-for-fetch-service
Diff against target: 215 lines (+139/-1)
5 files modified
lpbuildd/proxy.py (+22/-1)
lpbuildd/snap.py (+7/-0)
lpbuildd/tests/test_snap.py (+34/-0)
lpbuildd/tests/test_util.py (+41/-0)
lpbuildd/util.py (+35/-0)
Reviewer Review Type Date Requested Status
Launchpad code reviewers Pending
Review via email: mp+463119@code.launchpad.net

Commit message

End proxy session and gather data when using the fetch service

We send an API request to the fetch service to end the proxy session, save the data from the response locally, and upload it as a build file

To post a comment you must log in.
Revision history for this message
Ines Almeida (ines-almeida) wrote :

I would still like to spend some more time going over where is the best place to call the method to save the fetch service data, but I think this is in a good enough state for a review.

Unmerged commits

fcebbce... by Ines Almeida

End proxy session and gather data when using the fetch service

We send an API request to the fetch service to end the proxy session, save the data from the resposne locally, and upload it as a build file

d5eb715... by Ines Almeida

Update doc string and remove (now-)unnecessary code

69cedf0... by Ines Almeida

Add missing 'return' from revoke_proxy_token function (and other minor test related changes)

3bed9f8... by Ines Almeida

Enable revoking a token for the fetch service

Handle calling the revocation_endpoint sent by Launchpad, both in the cases where we want to revoke the token after the snap pull phase, and at the end of the build

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lpbuildd/proxy.py b/lpbuildd/proxy.py
2index fdaf5ca..a5d9232 100644
3--- a/lpbuildd/proxy.py
4+++ b/lpbuildd/proxy.py
5@@ -12,7 +12,11 @@ from twisted.python.compat import intToBytes
6 from twisted.web import http, proxy
7 from zope.interface import implementer
8
9-from lpbuildd.util import RevokeProxyTokenError, revoke_proxy_token
10+from lpbuildd.util import (
11+ RevokeProxyTokenError,
12+ end_fetch_service_session,
13+ revoke_proxy_token,
14+)
15
16
17 class BuilderProxyClient(proxy.ProxyClient):
18@@ -262,3 +266,20 @@ class BuildManagerProxyMixin:
19 )
20 except RevokeProxyTokenError as e:
21 self._builder.log(f"{e}\n")
22+
23+ def endProxySession(self, output_file):
24+ """ When using the fetch service, we want to end the session at the end
25+ of a build and save the metadata from the session.
26+
27+ This file can be uploaded to the librarian as any other build file.
28+ """
29+ if not self._use_fetch_service:
30+ return
31+
32+ response = end_fetch_service_session(
33+ self.proxy_url,
34+ self.end_session_endpoint,
35+ )
36+
37+ with self.backend.open(output_file, "wb") as f:
38+ f.write(response.content)
39diff --git a/lpbuildd/snap.py b/lpbuildd/snap.py
40index c5b3205..3033ced 100644
41--- a/lpbuildd/snap.py
42+++ b/lpbuildd/snap.py
43@@ -41,6 +41,7 @@ class SnapBuildManager(BuildManagerProxyMixin, DebianBuildManager):
44 self.use_fetch_service = extra_args.get("use_fetch_service")
45 self.proxy_url = extra_args.get("proxy_url")
46 self.revocation_endpoint = extra_args.get("revocation_endpoint")
47+ self.end_session_endpoint = extra_args.get("end_session_endpoint")
48 self.build_source_tarball = extra_args.get(
49 "build_source_tarball", False
50 )
51@@ -153,3 +154,9 @@ class SnapBuildManager(BuildManagerProxyMixin, DebianBuildManager):
52 )
53 if self.backend.path_exists(source_tarball_path):
54 self.addWaitingFileFromBackend(source_tarball_path)
55+
56+ if self.use_fetch_service:
57+ session_file = os.path.join(output_path, "session-data.json")
58+ self.endProxySession(output_file=session_file)
59+ if self.backend.path_exists(session_file):
60+ self.addWaitingFileFromBackend(session_file)
61diff --git a/lpbuildd/tests/test_snap.py b/lpbuildd/tests/test_snap.py
62index a31934a..f00751b 100644
63--- a/lpbuildd/tests/test_snap.py
64+++ b/lpbuildd/tests/test_snap.py
65@@ -3,6 +3,7 @@
66
67 import base64
68 import os
69+from unittest import mock
70
71 import responses
72 from fixtures import EnvironmentVariable, TempDir
73@@ -906,3 +907,36 @@ class TestSnapBuildManagerIteration(TestCase):
74 f"http://fetch-service.example/{session_id}/token", request.url
75 )
76
77+ @responses.activate
78+ def test_endProxySession(self):
79+ session_id = "123"
80+ token = "test_token"
81+ metadata = {"test": "data"}
82+ output_file = "output_file.json"
83+
84+ responses.add(
85+ "DELETE",
86+ f"http://fetch-service.example/{session_id}",
87+ json=metadata,
88+ )
89+
90+ self.buildmanager.backend = mock.MagicMock()
91+ self.buildmanager.use_fetch_service = True
92+ self.buildmanager.end_session_endpoint = (
93+ f"http://fetch-service.example/{session_id}"
94+ )
95+ self.buildmanager.proxy_url = f"http://session_id:{token}@proxy.example"
96+
97+ self.buildmanager.endProxySession(output_file)
98+
99+ self.assertEqual(1, len(responses.calls))
100+ request = responses.calls[0].request
101+ self.assertEqual(f"Basic {token}", request.headers["Authorization"])
102+ self.assertEqual(
103+ f"http://fetch-service.example/{session_id}", request.url
104+ )
105+ self.assertEqual(1, self.buildmanager.backend.open.call_count)
106+ self.assertEqual(
107+ mock.call(output_file, 'wb'),
108+ self.buildmanager.backend.open.call_args
109+ )
110diff --git a/lpbuildd/tests/test_util.py b/lpbuildd/tests/test_util.py
111index 26e631a..2e9d8ac 100644
112--- a/lpbuildd/tests/test_util.py
113+++ b/lpbuildd/tests/test_util.py
114@@ -7,6 +7,7 @@ import responses
115 from testtools import TestCase
116
117 from lpbuildd.util import (
118+ end_fetch_service_session,
119 get_arch_bits,
120 revoke_proxy_token,
121 set_personality,
122@@ -105,3 +106,43 @@ class TestRevokeToken(TestCase):
123 self.assertEqual(
124 f"Basic {token}", response.request.headers["Authorization"]
125 )
126+
127+ @responses.activate
128+ def test_revoke_fetch_service_token(self):
129+ """Proxy token revocation for the fetch service, uses the right
130+ authentication and returns metadata """
131+
132+ token = "test-token"
133+ proxy_url = f"https://session_id:{token}@host:port"
134+ end_session_endpoint = "https://builder.api.endpoint/session_id"
135+
136+ responses.add(responses.DELETE, end_session_endpoint)
137+
138+ response = end_fetch_service_session(
139+ proxy_url,
140+ end_session_endpoint,
141+ )
142+ self.assertEqual(
143+ f"Basic {token}", response.request.headers["Authorization"]
144+ )
145+
146+ @responses.activate
147+ def test_end_fetch_service_session(self):
148+ """Proxy token revocation for the fetch service, uses the right
149+ authentication and returns metadata """
150+
151+ token = "test-token"
152+ proxy_url = f"https://session_id:{token}@host:port"
153+ end_session_endpoint = "https://builder.api.endpoint/session_id"
154+ metadata = {"test": "data"}
155+
156+ responses.add(responses.DELETE, end_session_endpoint, json=metadata)
157+
158+ response = end_fetch_service_session(
159+ proxy_url,
160+ end_session_endpoint,
161+ )
162+ self.assertEqual(
163+ f"Basic {token}", response.request.headers["Authorization"]
164+ )
165+ self.assertEqual(metadata, response.json())
166diff --git a/lpbuildd/util.py b/lpbuildd/util.py
167index e3188b1..4f2ff2a 100644
168--- a/lpbuildd/util.py
169+++ b/lpbuildd/util.py
170@@ -69,6 +69,16 @@ class RevokeProxyTokenError(Exception):
171 return f"Unable to revoke token for {self.username}: {self.exception}"
172
173
174+class EndProxySessionError(Exception):
175+ def __init__(self, session, exception):
176+ super().__init__(self)
177+ self.session = session
178+ self.exception = exception
179+
180+ def __str__(self):
181+ return f"Unable to end session for {self.session}: {self.exception}"
182+
183+
184 def revoke_proxy_token(proxy_url, revocation_endpoint, use_fetch_service=False):
185 """Revoke builder proxy token.
186
187@@ -103,3 +113,28 @@ def revoke_proxy_token(proxy_url, revocation_endpoint, use_fetch_service=False):
188 )
189 except requests.RequestException as e:
190 raise RevokeProxyTokenError(url.username, e)
191+
192+
193+def end_fetch_service_session(proxy_url, revocation_endpoint):
194+ """End fetch service session.
195+
196+ The proxy_url for the fetch service:
197+ http://{session_id}:{token}@{host}:{port}
198+
199+ We use the token for authentication.
200+
201+ :raises EndProxySessionError: if attempting to end the session failed.
202+ :returns metadata from the fetch service session
203+ """
204+ url = urlparse(proxy_url)
205+ token = url.password
206+
207+ try:
208+ return requests.delete(
209+ revocation_endpoint,
210+ headers={'Authorization': f'Basic {token}'},
211+ timeout=15,
212+ )
213+ except requests.RequestException as e:
214+ session = url.username
215+ raise EndProxySessionError(session, e)

Subscribers

People subscribed via source and target branches