Merge lp:~ted/snapcraft/aws-iot into lp:~snappy-dev/snapcraft/core
- aws-iot
- Merge into core
Status: | Work in progress | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~ted/snapcraft/aws-iot | ||||
Merge into: | lp:~snappy-dev/snapcraft/core | ||||
Diff against target: |
545 lines (+373/-21) 8 files modified
snapcraft/cmds.py (+1/-4) snapcraft/lifecycle.py (+5/-5) snapcraft/plugins/awscli.py (+105/-0) snapcraft/plugins/awsiot.py (+227/-0) snapcraft/plugins/python2.py (+14/-1) snapcraft/plugins/python3.py (+14/-1) snapcraft/tests/test_cmds.py (+6/-9) snapcraft/tests/test_lifecycle.py (+1/-1) |
||||
To merge this branch: | bzr merge lp:~ted/snapcraft/aws-iot | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Sergio Schvezov | Needs Fixing | ||
Review via email: mp+276064@code.launchpad.net |
Commit message
Add plugins for working with AWS' IoT features
Description of the change
Sergio Schvezov (sergiusens) : | # |
Sergio Schvezov (sergiusens) wrote : | # |
This looks really good but the need for AWSCLIPlugin I want to avoid.
I still strongly believe that the AWSCLIPlugin should not be needed and a build-package for awscli should be used (we can update the deb in our ppa so we have the IOT option if need be).
Or is there a reason for the aws cli to live in the snap?
There are also some inline comments.
ftr,
$ rmadison awscli
awscli | 1.2.9-2 | trusty/universe | source, all
awscli | 1.7.0-1 | vivid/universe | source, all
awscli | 1.7.0-1build1 | wily/universe | source, all
awscli | 1.7.0-1build1 | xenial/universe | source, all
Sergio Schvezov (sergiusens) : | # |
Sergio Schvezov (sergiusens) : | # |
Ted Gould (ted) wrote : | # |
On Thu, 2015-10-29 at 11:48 +0000, Sergio Schvezov wrote:
> > + def _keys_from_
> > + certsjson = None
>
> this shouldn't be needed.
Python is weird. r256
> > === modified file 'snapcraft/
> > --- snapcraft/
> > +++ snapcraft/
> > @@ -48,6 +48,15 @@
> > schema[
> > 'type': 'string',
> > }
> > + schema[
> >
>
>
> doesn't this fix the other bug about list of requirements?
>
No, because there are apparently some projects (plainbox) that use
multiple files of requirements. So there's another feature there.
Unmerged revisions
- 256. By Ted Gould
-
Removing unneeded None
- 255. By Ted Gould
-
Making sure we get more files
- 254. By Ted Gould
-
Adding docstrings
- 253. By Ted Gould
-
Testing fixes from Sergio
- 252. By Ted Gould
-
Fix function call
- 251. By Ted Gould
-
Adding the plugins to the command tests
- 250. By Ted Gould
-
Switching to the 'new run'
- 249. By Ted Gould
-
Merge trunk
- 248. By Ted Gould
-
Reduce complexity of cert gen
- 247. By Ted Gould
-
Include cleanups
Preview Diff
1 | === modified file 'snapcraft/cmds.py' |
2 | --- snapcraft/cmds.py 2015-10-26 16:35:16 +0000 |
3 | +++ snapcraft/cmds.py 2015-10-29 14:24:50 +0000 |
4 | @@ -273,10 +273,7 @@ |
5 | parts_files = {} |
6 | for part in parts: |
7 | # Gather our own files up |
8 | - fileset = getattr(part.code.options, 'stage', ['*']) or ['*'] |
9 | - part_files, _ = lifecycle.migratable_filesets( |
10 | - fileset, |
11 | - part.installdir) |
12 | + part_files, _ = part.migratable_fileset_for('stage') |
13 | |
14 | # Scan previous parts for collisions |
15 | for other_part_name in parts_files: |
16 | |
17 | === modified file 'snapcraft/lifecycle.py' |
18 | --- snapcraft/lifecycle.py 2015-10-26 14:04:47 +0000 |
19 | +++ snapcraft/lifecycle.py 2015-10-29 14:24:50 +0000 |
20 | @@ -151,11 +151,11 @@ |
21 | self.code.build() |
22 | self.mark_done('build') |
23 | |
24 | - def _migratable_fileset_for(self, stage): |
25 | + def migratable_fileset_for(self, stage): |
26 | plugin_fileset = self.code.snap_fileset() |
27 | fileset = getattr(self.code.options, stage, ['*']) or ['*'] |
28 | fileset.extend(plugin_fileset) |
29 | - return migratable_filesets(fileset, self.installdir) |
30 | + return _migratable_filesets(fileset, self.installdir) |
31 | |
32 | def _organize(self): |
33 | organize_fileset = getattr(self.code.options, 'organize', {}) or {} |
34 | @@ -185,7 +185,7 @@ |
35 | |
36 | self.notify_stage("Staging") |
37 | self._organize() |
38 | - snap_files, snap_dirs = self._migratable_fileset_for('stage') |
39 | + snap_files, snap_dirs = self.migratable_fileset_for('stage') |
40 | |
41 | try: |
42 | _migrate_files(snap_files, snap_dirs, self.installdir, |
43 | @@ -205,7 +205,7 @@ |
44 | self.makedirs() |
45 | |
46 | self.notify_stage("Snapping") |
47 | - snap_files, snap_dirs = self._migratable_fileset_for('snap') |
48 | + snap_files, snap_dirs = self.migratable_fileset_for('snap') |
49 | |
50 | try: |
51 | _migrate_files(snap_files, snap_dirs, self.stagedir, self.snapdir) |
52 | @@ -274,7 +274,7 @@ |
53 | return PluginHandler(plugin_name, part_name, properties) |
54 | |
55 | |
56 | -def migratable_filesets(fileset, srcdir): |
57 | +def _migratable_filesets(fileset, srcdir): |
58 | includes, excludes = _get_file_list(fileset) |
59 | |
60 | include_files = _generate_include_set(srcdir, includes) |
61 | |
62 | === added file 'snapcraft/plugins/awscli.py' |
63 | --- snapcraft/plugins/awscli.py 1970-01-01 00:00:00 +0000 |
64 | +++ snapcraft/plugins/awscli.py 2015-10-29 14:24:50 +0000 |
65 | @@ -0,0 +1,105 @@ |
66 | +# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- |
67 | +# |
68 | +# Copyright (C) 2015 Canonical Ltd. |
69 | +# |
70 | +# This program is free software: you can redistribute it and/or modify |
71 | +# it under the terms of the GNU General Public License version 3 as |
72 | +# published by the Free Software Foundation. |
73 | +# |
74 | +# This program is distributed in the hope that it will be useful, |
75 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
76 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
77 | +# GNU General Public License for more details. |
78 | +# |
79 | +# You should have received a copy of the GNU General Public License |
80 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
81 | + |
82 | +"""This plugin brings in the AWS command line interface and configures it. |
83 | + |
84 | +The AWS command line can be used for a variety of things including accessing |
85 | +s3 or setting up ec2 services. It also is used for setting IoT things. This |
86 | +plugin takes configuration values for your AWS keys, downloads and installs |
87 | +the client into your snap, and configures it with those keys. |
88 | + |
89 | +Settings for this plugin include: |
90 | + |
91 | + - accesskeyid: |
92 | + (string) |
93 | + AWS Access Key to use |
94 | + - secreetaccesskey: |
95 | + (string) |
96 | + AWS Secret Key |
97 | + - region |
98 | + (string) |
99 | + Region of EC2 to use, defaults to us-east-1 |
100 | + |
101 | +It is important to not that the key will be stored in the snap itself and |
102 | +could be harvested with appropriate tools. |
103 | +""" |
104 | + |
105 | +import os |
106 | +import os.path |
107 | +import snapcraft.plugins.python3 |
108 | + |
109 | + |
110 | +class AWSCLIPlugin(snapcraft.plugins.python3.Python3Plugin): |
111 | + |
112 | + @classmethod |
113 | + def schema(cls): |
114 | + schema = super().schema() |
115 | + schema['properties']['accesskeyid'] = { |
116 | + 'type': 'string', |
117 | + 'default': '' |
118 | + } |
119 | + schema['properties']['secretaccesskey'] = { |
120 | + 'type': 'string', |
121 | + 'default': '' |
122 | + } |
123 | + schema['properties']['region'] = { |
124 | + 'type': 'string', |
125 | + 'default': 'us-east-1' |
126 | + } |
127 | + |
128 | + schema['required'] = ['accesskeyid', 'secretaccesskey'] |
129 | + |
130 | + return schema |
131 | + |
132 | + def __init__(self, name, options): |
133 | + options.source = "https://github.com/aws/aws-cli.git" |
134 | + options.source_type = 'git' |
135 | + super().__init__(name, options) |
136 | + |
137 | + def build(self): |
138 | + super().build() |
139 | + |
140 | + aws = ['python3', os.path.join(self.installdir, 'usr', 'bin', 'aws')] |
141 | + |
142 | + self.run(aws + ['configure', |
143 | + 'set', 'region', self.options.region]) |
144 | + self.run(aws + ['configure', |
145 | + 'set', 'aws_access_key_id', self.options.accesskeyid]) |
146 | + self.run(aws + ['configure', |
147 | + 'set', 'aws_secret_access_key', |
148 | + self.options.secretaccesskey]) |
149 | + |
150 | + def env(self, root): |
151 | + env = super().env(root) |
152 | + env.extend(['AWS_ACCESS_KEY_ID=%s' % self.options.accesskeyid, |
153 | + 'AWS_SECRET_ACCESS_KEY=%s' % self.options.secretaccesskey]) |
154 | + return env |
155 | + |
156 | + def snap_fileset(self): |
157 | + fileset = super().snap_fileset() |
158 | + fileset.append('-usr/bin/pip*') |
159 | + fileset.append('-usr/lib/python3/dist-packages/easy-install.pth') |
160 | + fileset.append('-usr/lib/python*/__pycache__/*.pyc') |
161 | + fileset.append('-usr/lib/python*/*/__pycache__/*.pyc') |
162 | + fileset.append('-usr/lib/python*/*/*/__pycache__/*.pyc') |
163 | + fileset.append('-usr/lib/python*/*/*/*/__pycache__/*.pyc') |
164 | + fileset.append('-usr/lib/python*/*/*/*/*/__pycache__/*.pyc') |
165 | + fileset.append('-usr/lib/python*/*/*/*/*/*/__pycache__/*.pyc') |
166 | + fileset.append('-usr/lib/python*/*/*/*/*/*/*/__pycache__/*.pyc') |
167 | + fileset.append('-usr/lib/python*/*/*/*/*/*/*/*/__pycache__/*.pyc') |
168 | + fileset.append('-usr/lib/python*/*/*/*/*/*/*/*/*/__pycache__/*.pyc') |
169 | + fileset.append('-usr/lib/python*/*/*/*/*/*/*/*/*/*/__pycache__/*.pyc') |
170 | + return fileset |
171 | |
172 | === added file 'snapcraft/plugins/awsiot.py' |
173 | --- snapcraft/plugins/awsiot.py 1970-01-01 00:00:00 +0000 |
174 | +++ snapcraft/plugins/awsiot.py 2015-10-29 14:24:50 +0000 |
175 | @@ -0,0 +1,227 @@ |
176 | +# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- |
177 | +# |
178 | +# Copyright (C) 2015 Canonical Ltd. |
179 | +# |
180 | +# This program is free software: you can redistribute it and/or modify |
181 | +# it under the terms of the GNU General Public License version 3 as |
182 | +# published by the Free Software Foundation. |
183 | +# |
184 | +# This program is distributed in the hope that it will be useful, |
185 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
186 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
187 | +# GNU General Public License for more details. |
188 | +# |
189 | +# You should have received a copy of the GNU General Public License |
190 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
191 | + |
192 | +"""A plugin to configure an AWS IoT thing and setup for MQTT certs |
193 | + |
194 | +The AWS IoT feature requires registration and configuration to communicate |
195 | +with the EC2 cloud. This plugin has configuration options for many of |
196 | +these settings, and some it can generate if there isn't an appropriate |
197 | +value to get. It will provide a basis for any software designed to |
198 | +communicate with the EC2 IoT interfaces. |
199 | + |
200 | + - generatekeys |
201 | + (string) |
202 | + True if new keys should be generated by Amazon, |
203 | + otherwise generate keys locally |
204 | + - policydocument |
205 | + (string) |
206 | + Which policy document should be used. Optional. |
207 | + A doc which allows all IoT will be used if not specified |
208 | + - policyname |
209 | + (string) |
210 | + Which policy name should be used. Optional. |
211 | + 'PubSubToAnyTopic' will be used if not specified |
212 | + - thing |
213 | + (string) |
214 | + The thing to create |
215 | + - endpoint |
216 | + (string) |
217 | + AWS Endpoint to use if non-default |
218 | + |
219 | +""" |
220 | + |
221 | +import os |
222 | +import snapcraft |
223 | +import urllib.request |
224 | +import os.path |
225 | +import json |
226 | +import subprocess |
227 | + |
228 | + |
229 | +class AWSIoTPlugin(snapcraft.BasePlugin): |
230 | + |
231 | + @classmethod |
232 | + def schema(cls): |
233 | + return { |
234 | + '$schema': 'http://json-schema.org/draft-04/schema#', |
235 | + 'type': 'object', |
236 | + 'properties': { |
237 | + 'generatekeys': { |
238 | + 'type': 'boolean', |
239 | + 'default': True |
240 | + }, |
241 | + 'policydocument': { |
242 | + 'type': 'string', |
243 | + 'default': '' |
244 | + }, |
245 | + 'policyname': { |
246 | + 'type': 'string', |
247 | + 'default': 'PubSubToAnyTopic' |
248 | + }, |
249 | + 'thing': { |
250 | + 'type': 'string', |
251 | + }, |
252 | + 'endpoint': { |
253 | + 'type': 'string', |
254 | + }, |
255 | + }, |
256 | + 'required': ['thing'] |
257 | + } |
258 | + |
259 | + def __init__(self, name, options): |
260 | + super().__init__(name, options) |
261 | + self.aws = ['python3', |
262 | + os.path.join(self.stagedir, 'usr', 'bin', 'aws'), |
263 | + 'iot'] |
264 | + |
265 | + if (options.endpoint): |
266 | + self.aws.extend(['--endpoint', options.endpoint]) |
267 | + |
268 | + def pull(self): |
269 | + return True |
270 | + |
271 | + def run_to_file(self, cmds, filename): |
272 | + output = self.run_output(cmds, cwd=self.builddir) |
273 | + with open(filename, 'w') as f: |
274 | + f.write(output) |
275 | + |
276 | + def _keys_from_aws(self, certsdir): |
277 | + # generate new keys |
278 | + self.run_to_file(self.aws + ['create-keys-and-certificate', |
279 | + '--set-as-active'], |
280 | + os.path.join(certsdir, 'certs.json')) |
281 | + # separate into different files |
282 | + with open(os.path.join(certsdir, 'certs.json')) as data_file: |
283 | + certsjson = json.load(data_file) |
284 | + |
285 | + with open(os.path.join(certsdir, 'cert.pem'), 'w') as text_file: |
286 | + text_file.write(self.data['certificatePem']) |
287 | + with open(os.path.join(certsdir, 'privateKey.pem'), 'w') \ |
288 | + as text_file: |
289 | + text_file.write(self.data['keyPair']['PrivateKey']) |
290 | + with open(os.path.join(certsdir, 'publicKey.pem'), 'w') \ |
291 | + as text_file: |
292 | + text_file.write(self.data['keyPair']['PublicKey']) |
293 | + |
294 | + return certsjson |
295 | + |
296 | + def _keys_from_local(self, certsdir): |
297 | + certsjson = None |
298 | + |
299 | + # generate private key |
300 | + csr = os.path.join(certsdir, 'cert.csr') |
301 | + self.run(['openssl', 'genrsa', |
302 | + '-out', os.path.join(certsdir, 'privateKey.pem'), |
303 | + '2048']) |
304 | + self.run(['openssl', 'req', '-new', |
305 | + '-key', os.path.join(certsdir, 'privateKey.pem'), |
306 | + '-out', csr]) |
307 | + |
308 | + # generate new keys based on a csr |
309 | + certresp = os.path.join(self.builddir, 'certresponse.txt') |
310 | + self.run_to_file(self.aws + ['create-certificate-from-csr', |
311 | + '--certificate-signing-request', csr, |
312 | + '--set-as-active'], |
313 | + certresp) |
314 | + with open(certresp) as data_file: |
315 | + certsjson = json.load(data_file) |
316 | + |
317 | + self.run_to_file(self.aws + ['describe-certificate', |
318 | + '--certificate-id', |
319 | + self.data['arn'].split(':cert/')[1], |
320 | + '--output', |
321 | + 'text', |
322 | + '--query', |
323 | + '{}Description.{}Pem'.format( |
324 | + 'certificate') |
325 | + ], |
326 | + os.path.join(certsdir, 'cert.pem')) |
327 | + |
328 | + return certsjson |
329 | + |
330 | + def build(self): |
331 | + certsdir = os.path.join(self.builddir, 'certs') |
332 | + # Make the certs directory if it does not exist |
333 | + os.makedirs(certsdir, exist_ok=True) |
334 | + |
335 | + # What should we do with certificates? |
336 | + if self.options.generatekeys: |
337 | + self.data = self._keys_from_aws(certsdir) |
338 | + else: |
339 | + self.data = self._keys_from_local(certsdir) |
340 | + |
341 | + # Extra check, but good to ensure |
342 | + if self.data is None: |
343 | + return |
344 | + |
345 | + # Get the root certificate |
346 | + self.filename = urllib.request.urlretrieve( |
347 | + 'https://www.symantec.com/content/en/us/enterprise/verisign/roots/' |
348 | + 'VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem', |
349 | + filename=os.path.join(certsdir, 'rootCA.pem')) |
350 | + |
351 | + # attach policy to certificate |
352 | + if self.options.policydocument is not '': |
353 | + self.pd = ('{\n' |
354 | + ' "Version": "2012-10-17",\n' |
355 | + ' "Statement": [{\n' |
356 | + ' "Effect": "Allow",\n' |
357 | + ' "Action":["iot:*"],\n' |
358 | + ' "Resource": ["*"]\n' |
359 | + ' }]\n' |
360 | + '}\n' |
361 | + ) |
362 | + self.options.policydocument = "policydocument" |
363 | + with open(self.options.policydocument, "w") as text_file: |
364 | + text_file.write(self.pd) |
365 | + |
366 | + arnresp = os.path.join(self.builddir, 'arnresponse.txt') |
367 | + try: |
368 | + self.run_to_file(self.aws + ['create-policy', |
369 | + '--policy-name', |
370 | + self.options.policyname, |
371 | + '--policy-document', |
372 | + 'file://' + |
373 | + self.options.policydocument], |
374 | + arnresp) |
375 | + except subprocess.CalledProcessError: |
376 | + print("If the policy name already exists ' \ |
377 | + 'then creating it will fail. You can ignore this error.") |
378 | + self.run_to_file(self.aws + ['get-policy', |
379 | + '--policy-name', |
380 | + self.options.policyname], |
381 | + arnresp) |
382 | + |
383 | + with open('arnresponse.txt') as data_file: |
384 | + self.data = json.load(data_file) |
385 | + |
386 | + self.run(self.aws + ['attach-principal-policy', |
387 | + '-‐principal-arn', |
388 | + self.data["policyArn"], |
389 | + '--policy-name', |
390 | + self.options.policyname]) |
391 | + |
392 | + self.run(self.aws + ['create-thing', |
393 | + '--thing-name', |
394 | + self.options.thing]) |
395 | + |
396 | + print("Created Thing: %s" % self.options.thing) |
397 | + |
398 | + def stage_fileset(self): |
399 | + fileset = super().stage_fileset() |
400 | + fileset.append('-certresponse.txt') |
401 | + fileset.append('-arnresponse.txt') |
402 | + return fileset |
403 | |
404 | === modified file 'snapcraft/plugins/python2.py' |
405 | --- snapcraft/plugins/python2.py 2015-10-27 19:23:02 +0000 |
406 | +++ snapcraft/plugins/python2.py 2015-10-29 14:24:50 +0000 |
407 | @@ -48,6 +48,15 @@ |
408 | schema['properties']['requirements'] = { |
409 | 'type': 'string', |
410 | } |
411 | + schema['properties']['pip-packages'] = { |
412 | + 'type': 'array', |
413 | + 'minitems': 1, |
414 | + 'uniqueItems': True, |
415 | + 'items': { |
416 | + 'type': 'string' |
417 | + }, |
418 | + 'default': [], |
419 | + } |
420 | schema.pop('required') |
421 | |
422 | return schema |
423 | @@ -76,7 +85,8 @@ |
424 | if self.options.requirements: |
425 | requirements = os.path.join(os.getcwd(), self.options.requirements) |
426 | |
427 | - if not os.path.exists(setup) and not self.options.requirements: |
428 | + if not os.path.exists(setup) and not \ |
429 | + (self.options.requirements or self.options.pip_packages): |
430 | return |
431 | |
432 | easy_install = os.path.join( |
433 | @@ -100,6 +110,9 @@ |
434 | if self.options.requirements: |
435 | self.run(pip_install + ['--requirement', requirements]) |
436 | |
437 | + if self.options.pip_packages: |
438 | + self.run(pip_install + ['--upgrade'] + self.options.pip_packages) |
439 | + |
440 | if os.path.exists(setup): |
441 | self.run(pip_install + ['.', ]) |
442 | |
443 | |
444 | === modified file 'snapcraft/plugins/python3.py' |
445 | --- snapcraft/plugins/python3.py 2015-10-27 19:23:02 +0000 |
446 | +++ snapcraft/plugins/python3.py 2015-10-29 14:24:50 +0000 |
447 | @@ -48,6 +48,15 @@ |
448 | schema['properties']['requirements'] = { |
449 | 'type': 'string', |
450 | } |
451 | + schema['properties']['pip-packages'] = { |
452 | + 'type': 'array', |
453 | + 'minitems': 1, |
454 | + 'uniqueItems': True, |
455 | + 'items': { |
456 | + 'type': 'string' |
457 | + }, |
458 | + 'default': [], |
459 | + } |
460 | schema.pop('required') |
461 | |
462 | return schema |
463 | @@ -76,7 +85,8 @@ |
464 | if self.options.requirements: |
465 | requirements = os.path.join(os.getcwd(), self.options.requirements) |
466 | |
467 | - if not os.path.exists(setup) and not self.options.requirements: |
468 | + if not os.path.exists(setup) and not \ |
469 | + (self.options.requirements or self.options.pip_packages): |
470 | return |
471 | |
472 | easy_install = os.path.join( |
473 | @@ -99,6 +109,9 @@ |
474 | if self.options.requirements: |
475 | self.run(pip_install + ['--requirement', requirements]) |
476 | |
477 | + if self.options.pip_packages: |
478 | + self.run(pip_install + ['--upgrade'] + self.options.pip_packages) |
479 | + |
480 | if os.path.exists(setup): |
481 | self.run(pip_install + ['.', ]) |
482 | |
483 | |
484 | === modified file 'snapcraft/tests/test_cmds.py' |
485 | --- snapcraft/tests/test_cmds.py 2015-10-26 15:54:30 +0000 |
486 | +++ snapcraft/tests/test_cmds.py 2015-10-29 14:24:50 +0000 |
487 | @@ -25,6 +25,7 @@ |
488 | from snapcraft import ( |
489 | cmds, |
490 | common, |
491 | + lifecycle, |
492 | tests |
493 | ) |
494 | |
495 | @@ -39,16 +40,12 @@ |
496 | self.addCleanup(tmpdirObject.cleanup) |
497 | tmpdir = tmpdirObject.name |
498 | |
499 | - part1 = mock.Mock() |
500 | - part1.name = 'part1' |
501 | - part1.code.options.stage = ['*'] |
502 | + part1 = lifecycle.load_plugin('part1', 'jdk', {'source': '.'}) |
503 | part1.installdir = tmpdir + '/install1' |
504 | os.makedirs(part1.installdir + '/a') |
505 | open(part1.installdir + '/a/1', mode='w').close() |
506 | |
507 | - part2 = mock.Mock() |
508 | - part2.name = 'part2' |
509 | - part2.code.options.stage = ['*'] |
510 | + part2 = lifecycle.load_plugin('part2', 'jdk', {'source': '.'}) |
511 | part2.installdir = tmpdir + '/install2' |
512 | os.makedirs(part2.installdir + '/a') |
513 | with open(part2.installdir + '/1', mode='w') as f: |
514 | @@ -57,9 +54,7 @@ |
515 | with open(part2.installdir + '/a/2', mode='w') as f: |
516 | f.write('a/2') |
517 | |
518 | - part3 = mock.Mock() |
519 | - part3.name = 'part3' |
520 | - part3.code.options.stage = ['*'] |
521 | + part3 = lifecycle.load_plugin('part3', 'jdk', {'source': '.'}) |
522 | part3.installdir = tmpdir + '/install3' |
523 | os.makedirs(part3.installdir + '/a') |
524 | os.makedirs(part3.installdir + '/b') |
525 | @@ -84,6 +79,8 @@ |
526 | def test_list_plugins(self, mock_stdout): |
527 | expected_list = '''ant |
528 | autotools |
529 | +awscli |
530 | +awsiot |
531 | catkin |
532 | cmake |
533 | copy |
534 | |
535 | === modified file 'snapcraft/tests/test_lifecycle.py' |
536 | --- snapcraft/tests/test_lifecycle.py 2015-10-21 20:03:15 +0000 |
537 | +++ snapcraft/tests/test_lifecycle.py 2015-10-29 14:24:50 +0000 |
538 | @@ -165,7 +165,7 @@ |
539 | dstdir = tmpdir + '/stage' |
540 | os.makedirs(dstdir) |
541 | |
542 | - files, dirs = snapcraft.lifecycle.migratable_filesets( |
543 | + files, dirs = snapcraft.lifecycle._migratable_filesets( |
544 | filesets[key]['fileset'], srcdir) |
545 | snapcraft.lifecycle._migrate_files(files, dirs, srcdir, dstdir) |
546 |
Looks good, I don't follow, can I see an example for this? I guess that adding the docstring like I ask for can clarify lots here.