launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #30996
[Merge] ~ines-almeida/launchpad-buildd:close-session-for-fetch-service into launchpad-buildd:master
Ines Almeida has proposed merging ~ines-almeida/launchpad-buildd:close-session-for-fetch-service into launchpad-buildd:master with ~ines-almeida/launchpad-buildd:update-close-session-for-fetch-service as a prerequisite.
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
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~ines-almeida/launchpad-buildd/+git/launchpad-buildd/+merge/463119
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~ines-almeida/launchpad-buildd:close-session-for-fetch-service into launchpad-buildd:master.
diff --git a/lpbuildd/proxy.py b/lpbuildd/proxy.py
index fdaf5ca..a5d9232 100644
--- a/lpbuildd/proxy.py
+++ b/lpbuildd/proxy.py
@@ -12,7 +12,11 @@ from twisted.python.compat import intToBytes
from twisted.web import http, proxy
from zope.interface import implementer
-from lpbuildd.util import RevokeProxyTokenError, revoke_proxy_token
+from lpbuildd.util import (
+ RevokeProxyTokenError,
+ end_fetch_service_session,
+ revoke_proxy_token,
+)
class BuilderProxyClient(proxy.ProxyClient):
@@ -262,3 +266,20 @@ class BuildManagerProxyMixin:
)
except RevokeProxyTokenError as e:
self._builder.log(f"{e}\n")
+
+ def endProxySession(self, output_file):
+ """ When using the fetch service, we want to end the session at the end
+ of a build and save the metadata from the session.
+
+ This file can be uploaded to the librarian as any other build file.
+ """
+ if not self._use_fetch_service:
+ return
+
+ response = end_fetch_service_session(
+ self.proxy_url,
+ self.end_session_endpoint,
+ )
+
+ with self.backend.open(output_file, "wb") as f:
+ f.write(response.content)
diff --git a/lpbuildd/snap.py b/lpbuildd/snap.py
index c5b3205..3033ced 100644
--- a/lpbuildd/snap.py
+++ b/lpbuildd/snap.py
@@ -41,6 +41,7 @@ class SnapBuildManager(BuildManagerProxyMixin, DebianBuildManager):
self.use_fetch_service = extra_args.get("use_fetch_service")
self.proxy_url = extra_args.get("proxy_url")
self.revocation_endpoint = extra_args.get("revocation_endpoint")
+ self.end_session_endpoint = extra_args.get("end_session_endpoint")
self.build_source_tarball = extra_args.get(
"build_source_tarball", False
)
@@ -153,3 +154,9 @@ class SnapBuildManager(BuildManagerProxyMixin, DebianBuildManager):
)
if self.backend.path_exists(source_tarball_path):
self.addWaitingFileFromBackend(source_tarball_path)
+
+ if self.use_fetch_service:
+ session_file = os.path.join(output_path, "session-data.json")
+ self.endProxySession(output_file=session_file)
+ if self.backend.path_exists(session_file):
+ self.addWaitingFileFromBackend(session_file)
diff --git a/lpbuildd/tests/test_snap.py b/lpbuildd/tests/test_snap.py
index a31934a..dfd4b1c 100644
--- a/lpbuildd/tests/test_snap.py
+++ b/lpbuildd/tests/test_snap.py
@@ -3,6 +3,7 @@
import base64
import os
+from unittest import mock
import responses
from fixtures import EnvironmentVariable, TempDir
@@ -18,6 +19,7 @@ from lpbuildd.tests.fakebuilder import FakeBuilder
from lpbuildd.tests.matchers import HasWaitingFiles
+
class MockBuildManager(SnapBuildManager):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -906,3 +908,36 @@ class TestSnapBuildManagerIteration(TestCase):
f"http://fetch-service.example/{session_id}/token", request.url
)
+ @responses.activate
+ def test_endProxySession(self):
+ session_id = "123"
+ token = "test_token"
+ metadata = {"test": "data"}
+ output_file = "output_file.json"
+
+ responses.add(
+ "DELETE",
+ f"http://fetch-service.example/{session_id}",
+ json=metadata,
+ )
+
+ self.buildmanager.backend = mock.MagicMock()
+ self.buildmanager.use_fetch_service = True
+ self.buildmanager.end_session_endpoint = (
+ f"http://fetch-service.example/{session_id}"
+ )
+ self.buildmanager.proxy_url = f"http://session_id:{token}@proxy.example"
+
+ self.buildmanager.endProxySession(output_file)
+
+ self.assertEqual(1, len(responses.calls))
+ request = responses.calls[0].request
+ self.assertEqual(f"Basic {token}", request.headers["Authorization"])
+ self.assertEqual(
+ f"http://fetch-service.example/{session_id}", request.url
+ )
+ self.assertEqual(1, self.buildmanager.backend.open.call_count)
+ self.assertEqual(
+ mock.call(output_file, 'wb'),
+ self.buildmanager.backend.open.call_args
+ )
diff --git a/lpbuildd/tests/test_util.py b/lpbuildd/tests/test_util.py
index 26e631a..2e9d8ac 100644
--- a/lpbuildd/tests/test_util.py
+++ b/lpbuildd/tests/test_util.py
@@ -7,6 +7,7 @@ import responses
from testtools import TestCase
from lpbuildd.util import (
+ end_fetch_service_session,
get_arch_bits,
revoke_proxy_token,
set_personality,
@@ -105,3 +106,43 @@ class TestRevokeToken(TestCase):
self.assertEqual(
f"Basic {token}", response.request.headers["Authorization"]
)
+
+ @responses.activate
+ def test_revoke_fetch_service_token(self):
+ """Proxy token revocation for the fetch service, uses the right
+ authentication and returns metadata """
+
+ token = "test-token"
+ proxy_url = f"https://session_id:{token}@host:port"
+ end_session_endpoint = "https://builder.api.endpoint/session_id"
+
+ responses.add(responses.DELETE, end_session_endpoint)
+
+ response = end_fetch_service_session(
+ proxy_url,
+ end_session_endpoint,
+ )
+ self.assertEqual(
+ f"Basic {token}", response.request.headers["Authorization"]
+ )
+
+ @responses.activate
+ def test_end_fetch_service_session(self):
+ """Proxy token revocation for the fetch service, uses the right
+ authentication and returns metadata """
+
+ token = "test-token"
+ proxy_url = f"https://session_id:{token}@host:port"
+ end_session_endpoint = "https://builder.api.endpoint/session_id"
+ metadata = {"test": "data"}
+
+ responses.add(responses.DELETE, end_session_endpoint, json=metadata)
+
+ response = end_fetch_service_session(
+ proxy_url,
+ end_session_endpoint,
+ )
+ self.assertEqual(
+ f"Basic {token}", response.request.headers["Authorization"]
+ )
+ self.assertEqual(metadata, response.json())
diff --git a/lpbuildd/util.py b/lpbuildd/util.py
index e3188b1..4f2ff2a 100644
--- a/lpbuildd/util.py
+++ b/lpbuildd/util.py
@@ -69,6 +69,16 @@ class RevokeProxyTokenError(Exception):
return f"Unable to revoke token for {self.username}: {self.exception}"
+class EndProxySessionError(Exception):
+ def __init__(self, session, exception):
+ super().__init__(self)
+ self.session = session
+ self.exception = exception
+
+ def __str__(self):
+ return f"Unable to end session for {self.session}: {self.exception}"
+
+
def revoke_proxy_token(proxy_url, revocation_endpoint, use_fetch_service=False):
"""Revoke builder proxy token.
@@ -103,3 +113,28 @@ def revoke_proxy_token(proxy_url, revocation_endpoint, use_fetch_service=False):
)
except requests.RequestException as e:
raise RevokeProxyTokenError(url.username, e)
+
+
+def end_fetch_service_session(proxy_url, revocation_endpoint):
+ """End fetch service session.
+
+ The proxy_url for the fetch service:
+ http://{session_id}:{token}@{host}:{port}
+
+ We use the token for authentication.
+
+ :raises EndProxySessionError: if attempting to end the session failed.
+ :returns metadata from the fetch service session
+ """
+ url = urlparse(proxy_url)
+ token = url.password
+
+ try:
+ return requests.delete(
+ revocation_endpoint,
+ headers={'Authorization': f'Basic {token}'},
+ timeout=15,
+ )
+ except requests.RequestException as e:
+ session = url.username
+ raise EndProxySessionError(session, e)
References