Merge lp:~zyga/checkbox/fix-1299210 into lp:checkbox

Proposed by Zygmunt Krynicki
Status: Merged
Approved by: Daniel Manrique
Approved revision: 3001
Merged at revision: 2995
Proposed branch: lp:~zyga/checkbox/fix-1299210
Merge into: lp:checkbox
Diff against target: 393 lines (+135/-91)
6 files modified
checkbox-ng/checkbox_ng/commands/cli.py (+15/-33)
plainbox/plainbox/abc.py (+33/-0)
plainbox/plainbox/impl/commands/run.py (+4/-21)
plainbox/plainbox/impl/commands/test_run.py (+9/-21)
plainbox/plainbox/impl/ctrl.py (+33/-0)
plainbox/plainbox/impl/runner.py (+41/-16)
To merge this branch: bzr merge lp:~zyga/checkbox/fix-1299210
Reviewer Review Type Date Requested Status
Daniel Manrique (community) Approve
Review via email: mp+219316@code.launchpad.net

Description of the change

3e1382d plainbox:ctrl: add CheckBoxExecutionController.get_warm_up_for_job
ac93d40 plainbox:abc: add IExecutionController.get_warm_up_for_job()
b8b9380 plainbox:ctrl: implement RootViaPTL1ExecutionController.get_warm_up_for_job
0f9b67b plainbox:runner: fix typo in comment
d0dad89 plainbox:runner: split off _get_ctrl_for_job() from _run_extcmd()
1437bb0 plainbox:runner: add JobRunner.get_warm_up_sequence()
536eceb plainbox:commands:run: use runner.get_warm_up_sequence()
ae60a9f plainbox:abc: add IJobRunner.get_warm_up_sequence()
bb587dc plainbox:commands:run: simplify tests related to auth warmup
87dde25 checkbox-ng:commands:cli: refactor CliInvocation
cae29ce checkbox-ng:commands:cli: use get_warm_up_sequence
02b129a checkbox-ng:commands:cli: remove CliInvocation._auth_warmup_needed()
3628855 plainbox:commands:run: remove RunInvocation._auth_warmup_needed()
4d02c62 plainbox:runner: drop authenticate_warmup()

To post a comment you must log in.
Revision history for this message
Daniel Manrique (roadmr) wrote :

Looks good, I like the part where it will just continue asking for the password, we had a lot of trouble with checkbox in the past on that scenario :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'checkbox-ng/checkbox_ng/commands/cli.py'
2--- checkbox-ng/checkbox_ng/commands/cli.py 2014-05-12 13:13:13 +0000
3+++ checkbox-ng/checkbox_ng/commands/cli.py 2014-05-13 09:24:14 +0000
4@@ -47,7 +47,6 @@
5 from plainbox.impl.job import JobTreeNode
6 from plainbox.impl.result import DiskJobResult, MemoryJobResult
7 from plainbox.impl.runner import JobRunner
8-from plainbox.impl.runner import authenticate_warmup
9 from plainbox.impl.runner import slugify
10 from plainbox.impl.secure.config import Unset
11 from plainbox.impl.secure.qualifiers import CompositeQualifier
12@@ -303,7 +302,10 @@
13 self.provider_list,
14 self.settings['default_whitelist']))
15 manager.checkpoint()
16-
17+ runner = JobRunner(
18+ manager.storage.location, self.provider_list,
19+ os.path.join(manager.storage.location, 'io-logs'),
20+ command_io_delegate=self)
21 if self.is_interactive and not resume_in_progress:
22 # Pre-run all local jobs
23 desired_job_list = select_jobs(
24@@ -315,13 +317,13 @@
25 self._update_desired_job_list(manager, desired_job_list)
26 # Ask the password before anything else in order to run local jobs
27 # requiring privileges
28- if self._auth_warmup_needed(manager):
29+ warm_up_list = runner.get_warm_up_sequence(manager.state.run_list)
30+ if warm_up_list:
31 print("[ {} ]".format(_("Authentication")).center(80, '='))
32- return_code = authenticate_warmup()
33- if return_code:
34- raise SystemExit(return_code)
35+ for warm_up_func in warm_up_list:
36+ warm_up_func()
37 self._local_only = True
38- self._run_jobs(ns, manager)
39+ self._run_jobs(runner, ns, manager)
40 self._local_only = False
41
42 if not resume_in_progress:
43@@ -332,11 +334,11 @@
44 if self.is_interactive:
45 # Ask the password before anything else in order to run jobs
46 # requiring privileges
47- if self._auth_warmup_needed(manager):
48+ warm_up_list = runner.get_warm_up_sequence(manager.state.run_list)
49+ if warm_up_list:
50 print("[ {} ]".format(_("Authentication")).center(80, '='))
51- return_code = authenticate_warmup()
52- if return_code:
53- raise SystemExit(return_code)
54+ for warm_up_func in warm_up_list:
55+ warm_up_func()
56 tree = SelectableJobTreeNode.create_tree(
57 manager.state.run_list,
58 legacy_mode=True)
59@@ -365,7 +367,7 @@
60 else:
61 print(_("Estimated duration cannot be determined for"
62 " manual jobs."))
63- self._run_jobs(ns, manager)
64+ self._run_jobs(runner, ns, manager)
65 manager.destroy()
66
67 # FIXME: sensible return value
68@@ -411,31 +413,11 @@
69 manager.state.metadata.running_job_name = None
70 manager.checkpoint()
71
72- def _run_jobs(self, ns, manager):
73- runner = JobRunner(
74- manager.storage.location, self.provider_list,
75- os.path.join(manager.storage.location, 'io-logs'),
76- command_io_delegate=self)
77+ def _run_jobs(self, runner, ns, manager):
78 self._run_jobs_with_session(ns, manager, runner)
79 if not self._local_only:
80 self.save_results(manager)
81
82- def _auth_warmup_needed(self, manager):
83- # Don't warm up plainbox-trusted-launcher-1 if none of the providers
84- # use it. We assume that the mere presence of a provider makes it
85- # possible for a root job to be preset but it could be improved to
86- # actually know when this step is absolutely not required (no local
87- # jobs, no jobs
88- # need root)
89- if all(not provider.secure for provider in self.provider_list):
90- return False
91- # Don't use authentication warm-up if none of the jobs on the run list
92- # requires it.
93- if all(job.user is None for job in manager.state.run_list):
94- return False
95- # Otherwise, do pre-authentication
96- return True
97-
98 def save_results(self, manager):
99 if self.is_interactive:
100 print("[ {} ]".format(_('Results')).center(80, '='))
101
102=== modified file 'plainbox/plainbox/abc.py'
103--- plainbox/plainbox/abc.py 2014-04-30 18:34:21 +0000
104+++ plainbox/plainbox/abc.py 2014-05-13 09:24:14 +0000
105@@ -356,6 +356,23 @@
106 # executing. We could expose the underlying process mechanics so that
107 # QT/GTK applications could tie that directly into their event loop.
108
109+ @abstractmethod
110+ def get_warm_up_sequence(self, job_list):
111+ """
112+ Determine if authentication warm-up may be needed.
113+
114+ :param job_lits:
115+ A list of jobs that may be executed
116+ :returns:
117+ A list of methods to call to complete the warm-up step.
118+
119+ Authentication warm-up is related to the plainbox-secure-launcher-1
120+ program that can be 'warmed-up' to perhaps cache the security
121+ credentials. This is usually done early in the testing process so that
122+ we can prompt for passwords before doing anything that takes an
123+ extended amount of time.
124+ """
125+
126
127 class IUserInterfaceIO(metaclass=ABCMeta):
128 """
129@@ -652,6 +669,22 @@
130 The higher the value, the more applicable this controller is.
131 """
132
133+ @abstractmethod
134+ def get_warm_up_for_job(self, job):
135+ """
136+ Get a warm-up function that should be called before running this job.
137+
138+ :returns:
139+ A callable (without arguments) or None, depending on needs of a
140+ particular job.
141+
142+ The warm-up function is an optional advisory interface to improve the
143+ testing experience for the user. A job may not require any warm-up. In
144+ such case the return value is None. Note that even if this function is
145+ not called the testing process should perform the same way (correctly)
146+ but the user may be prompted for additional steps mid-way.
147+ """
148+
149
150 class IBuildSystem(metaclass=ABCMeta):
151 """
152
153=== modified file 'plainbox/plainbox/impl/commands/run.py'
154--- plainbox/plainbox/impl/commands/run.py 2014-05-09 12:48:33 +0000
155+++ plainbox/plainbox/impl/commands/run.py 2014-05-13 09:24:14 +0000
156@@ -45,7 +45,6 @@
157 from plainbox.impl.exporter import get_all_exporters
158 from plainbox.impl.result import DiskJobResult, MemoryJobResult
159 from plainbox.impl.runner import JobRunner
160-from plainbox.impl.runner import authenticate_warmup
161 from plainbox.impl.runner import slugify
162 from plainbox.impl.session import SessionManager
163 from plainbox.impl.session import SessionMetaData
164@@ -424,11 +423,11 @@
165 Ask the password before anything else in order to run jobs requiring
166 privileges
167 """
168- if self._auth_warmup_needed():
169+ warm_up_list = self.runner.get_warm_up_sequence(self.state.run_list)
170+ if warm_up_list:
171 print(_("[ Authentication ]").center(80, '='))
172- return_code = authenticate_warmup()
173- if return_code:
174- raise SystemExit(return_code)
175+ for warm_up_func in warm_up_list:
176+ warm_up_func()
177
178 def run_all_selected_jobs(self):
179 """
180@@ -510,22 +509,6 @@
181 answer = input("{} [{}] ".format(prompt, ", ".join(allowed)))
182 return answer
183
184- def _auth_warmup_needed(self):
185- # Don't warm up plainbox-trusted-launcher-1 if none of the providers
186- # use it. We assume that the mere presence of a provider makes it
187- # possible for a root job to be preset but it could be improved to
188- # acutally know when this step is absolutely not required (no local
189- # jobs, no jobs
190- # need root)
191- if all(not provider.secure for provider in self.provider_list):
192- return False
193- # Don't use authentication warm-up if none of the jobs on the run list
194- # requires it.
195- if all(job.user is None for job in self.state.run_list):
196- return False
197- # Otherwise, do pre-authentication
198- return True
199-
200 def _save_results(self, output_file, input_stream):
201 if output_file is sys.stdout:
202 print(_("[ Results ]").center(80, '='))
203
204=== modified file 'plainbox/plainbox/impl/commands/test_run.py'
205--- plainbox/plainbox/impl/commands/test_run.py 2014-04-01 23:35:57 +0000
206+++ plainbox/plainbox/impl/commands/test_run.py 2014-05-13 09:24:14 +0000
207@@ -111,28 +111,16 @@
208 def test_run_without_args(self, mock_check_output):
209 with TestIO(combined=True) as io:
210 with self.assertRaises(SystemExit) as call:
211- with patch('plainbox.impl.commands.run.authenticate_warmup') as mock_warmup:
212- mock_warmup.return_value = 0
213- main(['run'])
214+ main(['run'])
215 self.assertEqual(call.exception.args, (0,))
216- expected1 = """
217- ===============================[ Analyzing Jobs ]===============================
218- Estimated duration cannot be determined for automated jobs.
219- Estimated duration cannot be determined for manual jobs.
220- ==============================[ Running All Jobs ]==============================
221- ==================================[ Results ]===================================
222- """
223- expected2 = """
224- ===============================[ Authentication ]===============================
225- ===============================[ Analyzing Jobs ]===============================
226- Estimated duration cannot be determined for automated jobs.
227- Estimated duration cannot be determined for manual jobs.
228- ==============================[ Running All Jobs ]==============================
229- ==================================[ Results ]===================================
230- """
231- self.assertIn(io.combined, [
232- cleandoc(expected1) + "\n",
233- cleandoc(expected2) + "\n"])
234+ expected = """
235+ ===============================[ Analyzing Jobs ]===============================
236+ Estimated duration cannot be determined for automated jobs.
237+ Estimated duration cannot be determined for manual jobs.
238+ ==============================[ Running All Jobs ]==============================
239+ ==================================[ Results ]===================================
240+ """
241+ self.assertEqual(io.combined, cleandoc(expected) + "\n")
242
243 def test_output_format_list(self):
244 with TestIO(combined=True) as io:
245
246=== modified file 'plainbox/plainbox/impl/ctrl.py'
247--- plainbox/plainbox/impl/ctrl.py 2014-03-26 20:01:03 +0000
248+++ plainbox/plainbox/impl/ctrl.py 2014-05-13 09:24:14 +0000
249@@ -59,6 +59,7 @@
250 from plainbox.impl.secure.rfc822 import RFC822SyntaxError
251 from plainbox.impl.secure.rfc822 import gen_rfc822_records
252 from plainbox.impl.session.jobs import JobReadinessInhibitor
253+from plainbox.vendor import extcmd
254
255 __all__ = [
256 'CheckBoxSessionStateController',
257@@ -559,6 +560,14 @@
258 """
259 return os.path.join(self._session_dir, "CHECKBOX_DATA")
260
261+ def get_warm_up_for_job(self, job):
262+ """
263+ Get a warm-up function that should be called before running this job.
264+
265+ :returns:
266+ None
267+ """
268+
269
270 class UserJobExecutionController(CheckBoxExecutionController):
271 """
272@@ -753,6 +762,30 @@
273 else:
274 return 0
275
276+ def get_warm_up_for_job(self, job):
277+ """
278+ Get a warm-up function that should be called before running this job.
279+
280+ :returns:
281+ a warm-up function for jobs that need to run as another
282+ user or None if the job can run as the current user.
283+ """
284+ if job.user is None:
285+ return
286+ else:
287+ return plainbox_trusted_launcher_warm_up
288+
289+
290+def plainbox_trusted_launcher_warm_up():
291+ """
292+ Warm-up function for plainbox-trusted-laucher-1.
293+
294+ returned by :meth:`RootViaPTL1ExecutionController.get_warm_up_for_job()`
295+ """
296+ warmup_popen = extcmd.ExternalCommand()
297+ return warmup_popen.call(
298+ ['pkexec', 'plainbox-trusted-launcher-1', '--warmup'])
299+
300
301 class RootViaPkexecExecutionController(
302 CheckBoxDifferentialExecutionController):
303
304=== modified file 'plainbox/plainbox/impl/runner.py'
305--- plainbox/plainbox/impl/runner.py 2014-02-20 21:43:56 +0000
306+++ plainbox/plainbox/impl/runner.py 2014-05-13 09:24:14 +0000
307@@ -63,18 +63,6 @@
308 return ''.join(c if c in valid_chars else '_' for c in _string)
309
310
311-def authenticate_warmup():
312- """
313- Call the checkbox trusted launcher in warmup mode.
314-
315- This will use the corresponding PolicyKit action and start the
316- authentication agent (depending on the installed policy file)
317- """
318- warmup_popen = extcmd.ExternalCommand()
319- return warmup_popen.call(
320- ['pkexec', 'plainbox-trusted-launcher-1', '--warmup'])
321-
322-
323 class IOLogRecordGenerator(extcmd.DelegateBase):
324 """
325 Delegate for extcmd that generates io_log entries.
326@@ -233,6 +221,29 @@
327 UserJobExecutionController(session_dir, provider_list),
328 ]
329
330+ def get_warm_up_sequence(self, job_list):
331+ """
332+ Determine if authentication warm-up may be needed.
333+
334+ :param job_lits:
335+ A list of jobs that may be executed
336+ :returns:
337+ A list of methods to call to complete the warm-up step.
338+
339+ Authentication warm-up is related to the plainbox-secure-launcher-1
340+ program that can be 'warmed-up' to perhaps cache the security
341+ credentials. This is usually done early in the testing process so that
342+ we can prompt for passwords before doing anything that takes an
343+ extended amount of time.
344+ """
345+ warm_up_list = []
346+ for job in job_list:
347+ ctrl = self._get_ctrl_for_job(job)
348+ warm_up_func = ctrl.get_warm_up_for_job(job)
349+ if warm_up_func is not None and warm_up_func not in warm_up_list:
350+ warm_up_list.add(warm_up_func)
351+ return warm_up_list
352+
353 def run_job(self, job, config=None):
354 """
355 Run the specified job an return the result
356@@ -657,6 +668,21 @@
357 return return_code, record_path
358
359 def _run_extcmd(self, job, config, extcmd_popen):
360+ ctrl = self._get_ctrl_for_job(job)
361+ return ctrl.execute_job(job, config, extcmd_popen)
362+
363+ def _get_ctrl_for_job(self, job):
364+ """
365+ Get the execution controller most applicable to run this job
366+
367+ :param job:
368+ A job definition to run
369+ :returns:
370+ An execution controller instance
371+ :raises LookupError:
372+ if no execution controller capable of running the specified job can
373+ be found
374+ """
375 # Compute the score of each controller
376 ctrl_score = [
377 (ctrl, ctrl.get_score(job))
378@@ -665,12 +691,11 @@
379 ctrl_score.sort(key=lambda pair: pair[1])
380 # Get the best score
381 ctrl, score = ctrl_score[-1]
382- # Ensure that the controler is viable
383+ # Ensure that the controller is viable
384 if score < 0:
385- raise RuntimeError(
386+ raise LookupError(
387 _("No exec controller supports job {}").format(job))
388 logger.debug(
389 _("Selected execution controller %s (score %d) for job %r"),
390 ctrl.__class__.__name__, score, job.id)
391- # Delegate and execute
392- return ctrl.execute_job(job, config, extcmd_popen)
393+ return ctrl

Subscribers

People subscribed via source and target branches