Merge lp:~sylvain-pineau/checkbox/fix-1361071 into lp:checkbox

Proposed by Sylvain Pineau
Status: Merged
Approved by: Sylvain Pineau
Approved revision: 3531
Merged at revision: 3527
Proposed branch: lp:~sylvain-pineau/checkbox/fix-1361071
Merge into: lp:checkbox
Diff against target: 263 lines (+53/-27)
7 files modified
checkbox-support/checkbox_support/parsers/udevadm.py (+15/-4)
plainbox/plainbox/impl/ctrl.py (+1/-1)
plainbox/plainbox/impl/secure/launcher1.py (+25/-8)
plainbox/plainbox/impl/secure/test_launcher1.py (+3/-3)
plainbox/plainbox/impl/test_ctrl.py (+2/-2)
providers/plainbox-provider-checkbox/jobs/disk.txt.in (+6/-8)
providers/plainbox-provider-resource-generic/bin/udev_resource (+1/-1)
To merge this branch: bzr merge lp:~sylvain-pineau/checkbox/fix-1361071
Reviewer Review Type Date Requested Status
Sylvain Pineau (community) Approve
Zygmunt Krynicki (community) Approve
Review via email: mp+245551@code.launchpad.net

Description of the change

Fixes https://bugs.launchpad.net/plainbox/+bug/1361071 by adding support for template jobs requiring root privileges.

The trusted launcher now understands jobs generated from local AND resources jobs.

To post a comment you must log in.
Revision history for this message
Zygmunt Krynicki (zyga) wrote :

Not sure if needs fixing or not. Have a look at the comments below please

Revision history for this message
Zygmunt Krynicki (zyga) wrote :

Let's work on landing via-changes and rebase this one on top

review: Needs Fixing
Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote :

rebased on trunk with via changes. also fixed plainbox/plainbox/impl/ctrl.py to get the source job not from the origin object but the new job_state.via_job

review: Needs Resubmitting
Revision history for this message
Zygmunt Krynicki (zyga) wrote :

Looks good apart from the _name that you are already fixing thing :)

The comment below about how jobs are instantiated. Do you think we should copy the checks from the session controller?

Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote :

fixed udevadm parser to create a dedicated property for "name"

review: Needs Resubmitting
Revision history for this message
Zygmunt Krynicki (zyga) wrote :

+1

16:05 <@spineau> zyga: when you're referring to the check in session controller code (https://code.launchpad.net/~sylvain-pineau/checkbox/fix-1361071/+merge/245551/comments/607377)
16:05 <@zyga> yes>
16:05 <@zyga> ?
16:06 <@spineau> zyga: are you suggesting to call new_unit.validate() ?
16:06 <@zyga> IIRC not just that
16:06 <@zyga> let me see
16:07 <@zyga> http://bazaar.launchpad.net/~checkbox-dev/checkbox/trunk/view/head:/plainbox/plainbox/impl/ctrl.py#L245
16:07 <@zyga> if you trace how that call unfolds
16:07 <@zyga> you'll see validity checks and duplicate checks
16:07 <@zyga> I think it's okay as is (as it it will work in the good case)
16:08 <@zyga> my worry is just about the bad case and what happens then
16:10 <@spineau> zyga: but the check should have been done by the session controller already, why duplicating this logic a second time in the secure launcher?*
16:10 <@zyga> the outcome may be different
16:10 <@zyga> and the original may have discarded something (separate concern)
16:10 <@zyga> that we're not discarding
16:11 <@spineau> zyga: we had this problem before then
16:13 <@spineau> zyga: as the check for duplicated jobs is only performed by the session controller
16:17 <@zyga> spineau: yes, though that doesn't mean the problem goes away
16:18 <@spineau> zyga: almost EOD for me, could you please explain in the merge request your concern about validity checks and duplicate checks missing from the trusted launcher?
16:18 <@spineau> zyga: are you seeing that as a blocker?
16:18 <@zyga> spineau: well, I think this can be merged
16:18 <@zyga> spineau: but I would still love to explore how the trusted launcher can be broken today
16:18 <@spineau> zyga: in that case we should follow up your remarks in a bug report I think

review: Approve
Revision history for this message
Daniel Manrique (roadmr) wrote :
Download full text (11.4 KiB)

The attempt to merge lp:~sylvain-pineau/checkbox/fix-1361071 into lp:checkbox failed. Below is the output from the failed tests.

[precise] starting container
[precise] (timing) 0.04user 0.03system 0:04.19elapsed 1%CPU (0avgtext+0avgdata 7864maxresident)k
[precise] (timing) 0inputs+32outputs (0major+5757minor)pagefaults 0swaps
[precise] provisioning container
[precise] (timing) 39.36user 11.37system 1:19.11elapsed 64%CPU (0avgtext+0avgdata 51604maxresident)k
[precise] (timing) 0inputs+16424outputs (0major+4483301minor)pagefaults 0swaps
[precise-testing] Starting tests...
Found a test script: ./checkbox-gui/requirements/container-tests-checkbox-gui-build
[precise-testing] container-tests-checkbox-gui-build: PASS
[precise-testing] (timing) 32.33user 2.33system 0:35.15elapsed 98%CPU (0avgtext+0avgdata 116152maxresident)k
[precise-testing] (timing) 0inputs+4216outputs (0major+477937minor)pagefaults 0swaps
Found a test script: ./checkbox-ng/requirements/container-tests-checkbox-ng-unit
[precise-testing] container-tests-checkbox-ng-unit: PASS
[precise-testing] (timing) 0.63user 0.12system 0:00.77elapsed 97%CPU (0avgtext+0avgdata 39872maxresident)k
[precise-testing] (timing) 0inputs+3456outputs (0major+20631minor)pagefaults 0swaps
Found a test script: ./checkbox-support/requirements/container-tests-checkbox-support
[precise-testing] container-tests-checkbox-support: PASS
[precise-testing] (timing) 16.67user 0.12system 0:16.88elapsed 99%CPU (0avgtext+0avgdata 83084maxresident)k
[precise-testing] (timing) 0inputs+1032outputs (0major+31508minor)pagefaults 0swaps
Found a test script: ./checkbox-touch/requirements/container-tests-touch-unit-tests
[precise-testing] container-tests-touch-unit-tests: PASS
[precise-testing] (timing) 0.00user 0.00system 0:00.02elapsed 40%CPU (0avgtext+0avgdata 2020maxresident)k
[precise-testing] (timing) 0inputs+8outputs (0major+2345minor)pagefaults 0swaps
Found a test script: ./plainbox/plainbox/impl/providers/categories/requirements/container-tests-provider-categories
[precise-testing] container-tests-provider-categories: PASS
[precise-testing] (timing) 0.16user 0.04system 0:00.21elapsed 94%CPU (0avgtext+0avgdata 13496maxresident)k
[precise-testing] (timing) 0inputs+176outputs (0major+5874minor)pagefaults 0swaps
Found a test script: ./plainbox/requirements/001-container-tests-plainbox-egg-info
[precise-testing] 001-container-tests-plainbox-egg-info: PASS
[precise-testing] (timing) 0.13user 0.02system 0:00.17elapsed 92%CPU (0avgtext+0avgdata 10500maxresident)k
[precise-testing] (timing) 0inputs+88outputs (0major+4985minor)pagefaults 0swaps
Found a test script: ./plainbox/requirements/container-tests-plainbox
[precise-testing] container-tests-plainbox: FAIL
[precise-testing] stdout: http://paste.ubuntu.com/9699339/
[precise-testing] stderr: http://paste.ubuntu.com/9699340/
[precise-testing] (timing) Command exited with non-zero status 1
[precise-testing] (timing) 5.12user 0.36system 0:05.53elapsed 99%CPU (0avgtext+0avgdata 63804maxresident)k
[precise-testing] (timing) 0inputs+2624outputs (0major+52524minor)pagefaults 0swaps
Found...

3527. By Sylvain Pineau

checkbox-support:parsers:udevadm.py: Keep the device name in the slots

Most of local/template jobs are using the device name /dev/<XXXX> to create
child jobs.
To avoid recreating the full device path in the job definition, the udev parser
now returns - if present - the device name, the one stating with "N: <XXXX>" in
the "udevadm info --export-db" output.

3528. By Sylvain Pineau

providers:resource-generic:udev_resource: return the device name attribute

The device name attribute is now part of the output and can be used by local or
template jobs.

3529. By Sylvain Pineau

plainbox:ctrl: Change how we get the source job to create parent_env

For template jobs, job.origin.source.job fails withthe following error:

AttributeError: 'FileTextSource' object has no attribute 'job'

job.origin.source.job cannot be used for all types of generated jobs.
A generic way to get the source job is to use now job_state.via_job.

3530. By Sylvain Pineau

plainbox:impl:secure:launcher1.py: Add support for template jobs

When the generator job is a resource job, the trusted launcher will now recreate
all the possible template jobs that use it.
That way generated jobs requiring root privileges can be created from templates
and still use the plainbox trusted launcher.
The command line options are untouched.

3531. By Sylvain Pineau

providers:checkbox:disks.txt.in: staging/disk now use the device resource

This new template job now use the udevadm parser to solve two problems:
- all the device parsing logic is delegated to the resource, not the template
  filter (i.e. removal of ram and loop devices)
- the udev resource is not the default udevadm parser and thus not part of
  default whitelists, causing https://bugs.launchpad.net/plainbox/+bug/1361071

Fixes https://bugs.launchpad.net/plainbox/+bug/1361071

Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote :

Fixed ctrl and launcher unit tests

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'checkbox-support/checkbox_support/parsers/udevadm.py'
2--- checkbox-support/checkbox_support/parsers/udevadm.py 2014-11-19 12:19:27 +0000
3+++ checkbox-support/checkbox_support/parsers/udevadm.py 2015-01-09 21:49:01 +0000
4@@ -90,6 +90,7 @@
5 class UdevadmDevice(object):
6 __slots__ = (
7 "_environment",
8+ "_name",
9 "_bits",
10 "_stack",
11 "_bus",
12@@ -99,8 +100,9 @@
13 "_vendor",
14 "_vendor_id",)
15
16- def __init__(self, environment, bits=None, stack=[]):
17+ def __init__(self, environment, name, bits=None, stack=[]):
18 self._environment = environment
19+ self._name = name
20 self._bits = bits
21 self._stack = stack
22 self._bus = None
23@@ -118,6 +120,11 @@
24 self.product))
25
26 @property
27+ def name(self):
28+ if self._name is not None:
29+ return self._name
30+
31+ @property
32 def bus(self):
33 if self._bus is not None:
34 return self._bus
35@@ -639,7 +646,7 @@
36 def as_json(self):
37 attributes = ("path", "bus", "category", "driver", "product_id",
38 "vendor_id", "subproduct_id", "subvendor_id", "product",
39- "vendor", "interface",)
40+ "vendor", "interface", "name")
41
42 return {a: getattr(self, a) for a in attributes if getattr(self, a)}
43
44@@ -715,8 +722,9 @@
45 if not record:
46 continue
47
48- # Determine path and environment
49+ # Determine path, name and environment
50 path = None
51+ name = None
52 element = None
53 environment = {}
54 for line in record.splitlines():
55@@ -732,6 +740,8 @@
56
57 if key == "P":
58 path = value
59+ elif key == "N":
60+ name = value
61 elif key == "E":
62 key_match = multi_pattern.match(value)
63 if not key_match:
64@@ -749,7 +759,8 @@
65 # Set default DEVPATH
66 environment.setdefault("DEVPATH", path)
67
68- device = self.device_factory(environment, self.bits, list(stack))
69+ device = self.device_factory(
70+ environment, name, self.bits, list(stack))
71 if not self._ignoreDevice(device):
72 if device._raw_path in self.devices:
73 if self.devices[device._raw_path].category == 'CARDREADER':
74
75=== modified file 'plainbox/plainbox/impl/ctrl.py'
76--- plainbox/plainbox/impl/ctrl.py 2015-01-07 14:55:36 +0000
77+++ plainbox/plainbox/impl/ctrl.py 2015-01-09 21:49:01 +0000
78@@ -954,7 +954,7 @@
79 cmd += ['--generator', job_state.via_job.checksum]
80 parent_env = self.get_differential_execution_environment(
81 # FIXME: job_state is from an unrelated job :/
82- job.origin.source.job, job_state, config, session_dir,
83+ job_state.via_job, job_state, config, session_dir,
84 nest_dir)
85 for key, value in sorted(parent_env.items()):
86 cmd += ['-G', '{}={}'.format(key, value)]
87
88=== modified file 'plainbox/plainbox/impl/secure/launcher1.py'
89--- plainbox/plainbox/impl/secure/launcher1.py 2014-07-22 13:58:51 +0000
90+++ plainbox/plainbox/impl/secure/launcher1.py 2015-01-09 21:49:01 +0000
91@@ -30,6 +30,8 @@
92
93 from plainbox.i18n import gettext as _
94 from plainbox.impl.job import JobDefinition
95+from plainbox.impl.resource import Resource
96+from plainbox.impl.unit.template import TemplateUnit
97 from plainbox.impl.secure.origin import JobOutputTextSource
98 from plainbox.impl.secure.providers.v1 import all_providers
99 from plainbox.impl.secure.rfc822 import load_rfc822_records, RFC822SyntaxError
100@@ -92,16 +94,16 @@
101 cmd = [job.shell, '-c', job.command]
102 return subprocess.call(cmd, env=self.modify_execution_environment(env))
103
104- def run_local_job(self, checksum, env):
105+ def run_generator_job(self, checksum, env):
106 """
107- Run a job with and interpret the stdout as a job definition.
108+ Run a job with and process the stdout to get a job definition.
109
110 :param checksum:
111 The checksum of the job to execute
112 :param env:
113 Environment to execute the job in.
114 :returns:
115- A list of job definitions that were parsed out of the output.
116+ A list of job definitions that were processed from the output.
117 :raises LookupError:
118 If the checksum does not match any known job
119 """
120@@ -116,11 +118,26 @@
121 record_list = load_rfc822_records(output, source=source)
122 except RFC822SyntaxError as exc:
123 logging.error(
124- _("Syntax error in job generated from %s: %s"), job, exc)
125+ _("Syntax error in record generated from %s: %s"), job, exc)
126 else:
127- for record in record_list:
128- job = JobDefinition.from_rfc822_record(record)
129- job_list.append(job)
130+ if job.plugin == 'local':
131+ for record in record_list:
132+ job = JobDefinition.from_rfc822_record(record)
133+ job_list.append(job)
134+ elif job.plugin == 'resource':
135+ resource_list = []
136+ for record in record_list:
137+ resource = Resource(record.data)
138+ resource_list.append(resource)
139+ for plugin in all_providers.get_all_plugins():
140+ for u in plugin.plugin_object.get_units()[0]:
141+ if (
142+ isinstance(u, TemplateUnit) and
143+ u.resource_id == job.id
144+ ):
145+ logging.info(_("Instantiating unit: %s"), u)
146+ for new_unit in u.instantiate_all(resource_list):
147+ job_list.append(new_unit)
148 return job_list
149
150
151@@ -252,7 +269,7 @@
152 # Run the local job and feed the result back to the launcher
153 if ns.generator:
154 try:
155- generated_job_list = launcher.run_local_job(
156+ generated_job_list = launcher.run_generator_job(
157 ns.generator, ns.generator_env)
158 launcher.add_job_list(generated_job_list)
159 except LookupError as exc:
160
161=== modified file 'plainbox/plainbox/impl/secure/test_launcher1.py'
162--- plainbox/plainbox/impl/secure/test_launcher1.py 2014-09-13 11:37:17 +0000
163+++ plainbox/plainbox/impl/secure/test_launcher1.py 2015-01-09 21:49:01 +0000
164@@ -117,7 +117,7 @@
165 def test_run_local_job(self, mock_check_output, mock_load_rfc822_records,
166 mock_from_rfc822_record):
167 # Create a mock job and add it to the launcher
168- job = mock.Mock(spec=JobDefinition, name='job')
169+ job = mock.Mock(spec=JobDefinition, name='job', plugin='local')
170 self.launcher.add_job_list([job])
171 # Create two mock rfc822 records
172 record1 = mock.Mock(spec=RFC822Record, name='record')
173@@ -125,7 +125,7 @@
174 # Ensure that load_rfc822_records() returns some mocked records
175 mock_load_rfc822_records.return_value = [record1, record2]
176 # Run the tested method
177- job_list = self.launcher.run_local_job(job.checksum, None)
178+ job_list = self.launcher.run_generator_job(job.checksum, None)
179 # Ensure that we run the job command via job.shell
180 mock_check_output.assert_called_with(
181 [job.shell, '-c', job.command], env={}, universal_newlines=True)
182@@ -271,7 +271,7 @@
183 with TestIO(combined=True) as io:
184 retval = main(['--target=1234', '--generator=5678'])
185 # Ensure that the local job command was invoked
186- mock_launcher().run_local_job.assert_called_with(local_job.checksum, None)
187+ mock_launcher().run_generator_job.assert_called_with(local_job.checksum, None)
188 # Ensure that the target job command was invoked
189 mock_launcher().run_shell_from_job.assert_called_with(
190 target_job.checksum, None)
191
192=== modified file 'plainbox/plainbox/impl/test_ctrl.py'
193--- plainbox/plainbox/impl/test_ctrl.py 2015-01-07 14:55:36 +0000
194+++ plainbox/plainbox/impl/test_ctrl.py 2015-01-09 21:49:01 +0000
195@@ -792,7 +792,7 @@
196 verify that we run plainbox-trusted-launcher-1 as the desired user
197 """
198 self.job.get_environ_settings.return_value = []
199- self.job.origin.source.job = mock.Mock(
200+ self.job_state.via_job = mock.Mock(
201 name='generator_job',
202 spec=JobDefinition,
203 provider=mock.Mock(
204@@ -803,7 +803,7 @@
205 units_dir="units_dir-generator",
206 CHECKBOX_SHARE='CHECKBOX_SHARE-generator'))
207 # Mock the default flags (empty set)
208- self.job.origin.source.job.get_flag_set.return_value = frozenset()
209+ self.job_state.via_job.get_flag_set.return_value = frozenset()
210 PATH = os.pathsep.join([self.NEST_DIR, 'vanilla-path'])
211 expected = [
212 'pkexec', '--user', self.job.user,
213
214=== modified file 'providers/plainbox-provider-checkbox/jobs/disk.txt.in'
215--- providers/plainbox-provider-checkbox/jobs/disk.txt.in 2014-09-09 16:02:53 +0000
216+++ providers/plainbox-provider-checkbox/jobs/disk.txt.in 2015-01-09 21:49:01 +0000
217@@ -6,16 +6,16 @@
218
219 unit: template
220 template-unit: job
221-template-imports: from 2013.com.canonical.certification import staging/udev as udev
222-template-resource: udev
223-template-filter: udev.attr_DEVTYPE == 'disk' and 'ram' not in udev.name and 'loop' not in udev.name
224+template-imports: from 2013.com.canonical.certification import device
225+template-resource: device
226+template-filter: device.category == 'DISK'
227 plugin: shell
228 id: staging/disk/stats_{name}
229 requires:
230 block_device.{name}_state != 'removable'
231 user: root
232 command: disk_stats_test {name}
233-_summary: Disk statistics for {attr_DEVNAME}
234+_summary: Disk statistics for /dev/{name}
235 _description:
236 This test checks disk stats, generates some activity and rechecks stats to
237 verify they've changed. It also verifies that disks appear in the various
238@@ -23,11 +23,9 @@
239 .
240 This test will inspect the following disk:
241 .
242- product vendor: {attr_ID_VENDOR}
243- product name: {attr_ID_MODEL}
244- serial number: {attr_ID_SERIAL_SHORT}
245+ product name: {product}
246 sysfs path: {path}
247- device node path: {attr_DEVNAME}
248+ device node path: /dev/{name}
249
250 plugin: local
251 _summary: Check stats changes for each disk
252
253=== modified file 'providers/plainbox-provider-resource-generic/bin/udev_resource'
254--- providers/plainbox-provider-resource-generic/bin/udev_resource 2014-08-08 16:35:49 +0000
255+++ providers/plainbox-provider-resource-generic/bin/udev_resource 2015-01-09 21:49:01 +0000
256@@ -27,7 +27,7 @@
257
258 class UdevResult:
259
260- attributes = ("path", "bus", "category", "driver", "product_id",
261+ attributes = ("path", "name", "bus", "category", "driver", "product_id",
262 "vendor_id", "subproduct_id", "subvendor_id", "product",
263 "vendor", "interface",)
264

Subscribers

People subscribed via source and target branches