Merge ~cjwatson/launchpad:code-import-scheduler-librarian into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: eaec2406b72f3acb41eca1ba0722d0f1c378d5e4
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:code-import-scheduler-librarian
Merge into: launchpad:master
Diff against target: 130 lines (+52/-8)
3 files modified
lib/lp/code/interfaces/codeimportscheduler.py (+7/-1)
lib/lp/code/xmlrpc/codeimportscheduler.py (+21/-6)
lib/lp/code/xmlrpc/tests/test_codeimportscheduler.py (+24/-1)
Reviewer Review Type Date Requested Status
Ioana Lasc (community) Approve
Review via email: mp+391793@code.launchpad.net

Commit message

Accept code import log file data over XML-RPC

Description of the change

The code import worker currently uploads its log file directly to the librarian. This is a bit of an obstacle to splitting out the code import worker from the main Launchpad codebase: it wouldn't necessarily be impossible to retain the remote librarian upload client in a future lp-codeimport, but it would be somewhat awkward as that client is currently designed with the expectation of having direct Launchpad database access.

Log files aren't typically all that large, though, so it seems workable to just send them as part of the XML-RPC `finishJobID` request and have the webapp side of that request do the librarian upload instead. This prepares for that by accepting either XML-RPC-encapsulated binary data or a librarian URL as the third parameter to `finishJobID`.

To post a comment you must log in.
Revision history for this message
Ioana Lasc (ilasc) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/code/interfaces/codeimportscheduler.py b/lib/lp/code/interfaces/codeimportscheduler.py
2index c15873e..e0d646e 100644
3--- a/lib/lp/code/interfaces/codeimportscheduler.py
4+++ b/lib/lp/code/interfaces/codeimportscheduler.py
5@@ -54,8 +54,14 @@ class ICodeImportScheduler(Interface):
6 :raise NoSuchCodeImportJob: if no job with id `job_id` exists.
7 """
8
9- def finishJobID(job_id, status_name, log_file_alias_url):
10+ def finishJobID(job_id, status_name, log_file):
11 """Call `ICodeImportJobWorkflow.finishJob` for job `job_id`.
12
13+ :param job_id: The ID of the code import job to finish.
14+ :param status_name: The outcome of the job as the name of a
15+ `CodeImportResultStatus` item.
16+ :param log_file: A log file to display for diagnostics, either as a
17+ `six.moves.xmlrpc_client.Binary` containing the log file data or
18+ as the URL of a file in the librarian.
19 :raise NoSuchCodeImportJob: if no job with id `job_id` exists.
20 """
21diff --git a/lib/lp/code/xmlrpc/codeimportscheduler.py b/lib/lp/code/xmlrpc/codeimportscheduler.py
22index 35ba346..6e58bd8 100644
23--- a/lib/lp/code/xmlrpc/codeimportscheduler.py
24+++ b/lib/lp/code/xmlrpc/codeimportscheduler.py
25@@ -8,7 +8,10 @@ __all__ = [
26 'CodeImportSchedulerAPI',
27 ]
28
29+import io
30+
31 import six
32+from six.moves import xmlrpc_client
33 from zope.component import getUtility
34 from zope.interface import implementer
35 from zope.security.proxy import removeSecurityProxy
36@@ -61,11 +64,10 @@ class CodeImportSchedulerAPI(LaunchpadXMLRPCView):
37 """See `ICodeImportScheduler`."""
38 return self._updateHeartbeat(job_id, six.ensure_text(log_tail))
39
40- def finishJobID(self, job_id, status_name, log_file_alias_url):
41+ def finishJobID(self, job_id, status_name, log_file):
42 """See `ICodeImportScheduler`."""
43 return self._finishJobID(
44- job_id, six.ensure_text(status_name),
45- six.ensure_text(log_file_alias_url))
46+ job_id, six.ensure_text(status_name), log_file)
47
48 @return_fault
49 def _getImportDataForJobID(self, job_id):
50@@ -87,14 +89,27 @@ class CodeImportSchedulerAPI(LaunchpadXMLRPCView):
51 return 0
52
53 @return_fault
54- def _finishJobID(self, job_id, status_name, log_file_alias_url):
55+ def _finishJobID(self, job_id, status_name, log_file):
56 job = self._getJob(job_id)
57 status = CodeImportResultStatus.items[status_name]
58 workflow = removeSecurityProxy(getUtility(ICodeImportJobWorkflow))
59- if log_file_alias_url:
60+ if isinstance(log_file, xmlrpc_client.Binary):
61+ if log_file.data:
62+ log_file_name = '%s.log' % (
63+ job.code_import.target.unique_name[1:].replace('/', '-'))
64+ log_file_alias = getUtility(ILibraryFileAliasSet).create(
65+ log_file_name, len(log_file.data),
66+ io.BytesIO(log_file.data), 'text/plain')
67+ else:
68+ log_file_alias = None
69+ elif log_file:
70+ # XXX cjwatson 2020-10-05: Backward compatibility for previous
71+ # versions that uploaded the log file to the librarian from the
72+ # scheduler; remove this once deployed code import machines no
73+ # longer need this.
74 library_file_alias_set = getUtility(ILibraryFileAliasSet)
75 # XXX This is so so so terrible:
76- log_file_alias_id = int(log_file_alias_url.split('/')[-2])
77+ log_file_alias_id = int(six.ensure_text(log_file).split('/')[-2])
78 log_file_alias = library_file_alias_set[log_file_alias_id]
79 else:
80 log_file_alias = None
81diff --git a/lib/lp/code/xmlrpc/tests/test_codeimportscheduler.py b/lib/lp/code/xmlrpc/tests/test_codeimportscheduler.py
82index c79f741..49f2e44 100644
83--- a/lib/lp/code/xmlrpc/tests/test_codeimportscheduler.py
84+++ b/lib/lp/code/xmlrpc/tests/test_codeimportscheduler.py
85@@ -6,6 +6,7 @@
86 __metaclass__ = type
87
88 from six.moves import xmlrpc_client
89+import transaction
90 from zope.component import getUtility
91 from zope.security.proxy import removeSecurityProxy
92
93@@ -113,7 +114,7 @@ class TestCodeImportSchedulerAPI(TestCaseWithFactory):
94 self.assertSqlAttributeEqualsDate(
95 code_import, 'date_last_successful', UTC_NOW)
96
97- def test_finishJobID_with_log_file(self):
98+ def test_finishJobID_with_log_file_alias_url(self):
99 # finishJobID calls the finishJobID job workflow method and can parse
100 # a librarian file's http url to figure out its ID.
101 code_import_job = self.makeCodeImportJob(running=True)
102@@ -125,6 +126,28 @@ class TestCodeImportSchedulerAPI(TestCaseWithFactory):
103 self.assertEqual(
104 log_file_alias, code_import.results.last().log_file)
105
106+ def test_finishJobID_with_log_file_data(self):
107+ # finishJobID calls the finishJobID job workflow method and uploads
108+ # log file data to the librarian.
109+ code_import_job = self.makeCodeImportJob(running=True)
110+ code_import = code_import_job.code_import
111+ self.api.finishJobID(
112+ code_import_job.id, CodeImportResultStatus.SUCCESS.name,
113+ xmlrpc_client.Binary(b'log file data\n'))
114+ transaction.commit()
115+ self.assertEqual(
116+ b'log file data\n', code_import.results.last().log_file.read())
117+
118+ def test_finishJobID_with_empty_log_file_data(self):
119+ # finishJobID calls the finishJobID job workflow method, but does
120+ # not upload zero-byte log files to the librarian.
121+ code_import_job = self.makeCodeImportJob(running=True)
122+ code_import = code_import_job.code_import
123+ self.api.finishJobID(
124+ code_import_job.id, CodeImportResultStatus.SUCCESS.name,
125+ xmlrpc_client.Binary(b''))
126+ self.assertIsNone(code_import.results.last().log_file)
127+
128 def test_finishJobID_not_found(self):
129 # getImportDataForJobID returns a NoSuchCodeImportJob fault when there
130 # is no code import job with the given ID.

Subscribers

People subscribed via source and target branches

to status/vote changes: