Merge lp:~pfalcon/linaro-aws-tools/linaro-ami-ver-1.5 into lp:linaro-aws-tools
- linaro-ami-ver-1.5
- Merge into trunk
Status: | Merged | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Merged at revision: | 99 | ||||||||||||||||
Proposed branch: | lp:~pfalcon/linaro-aws-tools/linaro-ami-ver-1.5 | ||||||||||||||||
Merge into: | lp:linaro-aws-tools | ||||||||||||||||
Diff against target: |
480 lines (+217/-67) 11 files modified
linaro-ami/README (+36/-12) linaro-ami/linaro-ami (+15/-2) linaro-ami/linaro-ami.conf (+7/-1) linaro-ami/linaro_ami.py (+14/-5) linaro-ami/remote_executor.py (+18/-4) linaro-ami/setup.cfg (+3/-0) linaro-ami/tests/integration/__init__.py (+5/-0) linaro-ami/tests/integration/integration_test_support.py (+42/-0) linaro-ami/tests/integration/test_linaro_ami.py (+9/-3) linaro-ami/tests/integration/test_remote_executor_int.py (+58/-0) linaro-ami/tests/unit/test_remote_executor.py (+10/-40) |
||||||||||||||||
To merge this branch: | bzr merge lp:~pfalcon/linaro-aws-tools/linaro-ami-ver-1.5 | ||||||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Stevan Radaković | code | Approve | |
Данило Шеган | Pending | ||
Review via email: mp+115088@code.launchpad.net |
Commit message
Description of the change
These changes fix/elaborated number of issues as tracked by linked bugs, specifically:
1. Recovers support for --key/--private-key switch (as present in many utils from ec2-api-tools) to specify user's EC2-regestered SSH key, to make sure that object ownership is properly recorded/given user has access to them for diagnostics.
2. Testsuite was elaborated and restructured to clearly separate unit and integration tests. Only quick-running, side-effects free unit tests were set up to run by default. Docs for running tests were updated.
3. Issues faced during working on lp:1019257 were fixed, with regression tests added, as well as fixes and tests for similar conditions.
Loïc Minier (lool) wrote : | # |
Stevan Radaković (stevanr) wrote : | # |
Looks good to me. I still agree with Loic though, as we had a lot of discussions on the same topic internally.
Stevan Radaković (stevanr) wrote : | # |
Hey Paul, great work.
README file doesn't contain anything about the 2 new (old) config options nor the instructions for the new form of testing remote executor (EC2_KEY instead of old EC2_PRIVATE_KEY).
I also couldn't run remote executor integration tests with any of my previous keys.
Otherwise, changes look good.
Paul Sokolovsky (pfalcon) wrote : | # |
> do we really need to bother with the key passing args?
The problem is that there're many those "keys" in AWS, each serving different purpose (and it's not easy to grasp the whole picture). Let's look at some part of the whole picture:
There're AWS_ACCESS_KEY_ID which is kinda login and AWS_SECRET_
[]
-K, --private-key KEY
Specify KEY as the private key to use. Defaults to the value of the
[]
-k, --key KEYPAIR
Specifies the key pair to use when launching the instance(s).
So, answering the original questions, if we should bother with all those keys, the good answer is that we should, inasmuch as AWS/EC2 itself does (in the form of its standard utilities in particular). Trying to circumvent AWS/EC2 credential process is bad idea because 1) it may lead to unexpected results (including in data integrity/security domain); 2) for people who know AWS/EC2 credentials semantics, it again will lead to unexpected results.
- 117. By Paul Sokolovsky
-
Update decsription of commands and options.
Paul Sokolovsky (pfalcon) wrote : | # |
> README file doesn't contain anything about the 2 new (old) config options
Ok, updated.
Paul Sokolovsky (pfalcon) wrote : | # |
> nor the instructions for the new form of testing remote executor
Weird, I'm sure I was updating that section of README! Now I see That I did that in wrong branch, let me port that over.
- 118. By Paul Sokolovsky
-
Mention that boto 2.0+ is required.
Earlier versions had different credentials setting method and may miss
some API calls.(Initially committed to wrong branch).
- 119. By Paul Sokolovsky
-
Update Tests section for elaborated unit/integration testsuite.
(Initially committed to wrong branch).
Paul Sokolovsky (pfalcon) wrote : | # |
> Weird, I'm sure I was updating that section of README! Now I see That I did that in wrong branch, let me port that over.
Here they go. That should make it clear how to run integration part of testsuite. In particular, EC2_KEY doesn't replaces EC2_PRIVATE_KEY, they're used both, essentially, EC2_PRIVATE_KEY is the analog of command-line --private-key option, and in the same vein, EC2_KEY is the analog of --key option.
I run complete testsuite as follows (EC2_PRIVATE_KEY is set in env to point to my priv key of keypair regiseterd as pfalcon@home):
EC2_KEY=
Running just unit tests is as easy as
nosetests
Stevan Radaković (stevanr) wrote : | # |
> > README file doesn't contain anything about the 2 new (old) config options
>
> Ok, updated.
Ok thanks.
Approve +1.
Paul Sokolovsky (pfalcon) wrote : | # |
Merged, thanks.
Preview Diff
1 | === modified file 'linaro-ami/README' |
2 | --- linaro-ami/README 2012-07-13 10:05:48 +0000 |
3 | +++ linaro-ami/README 2012-07-17 18:43:18 +0000 |
4 | @@ -22,18 +22,42 @@ |
5 | $ export AWS_ACCESS_KEY_ID=EXAMPLEKEY |
6 | $ export AWS_SECRET_ACCESS_KEY=EXAMPLESECRETKEY |
7 | |
8 | -Script Options |
9 | --------------- |
10 | - |
11 | -Currently, only one command is recognizable, 'create', which takes one |
12 | -additional argument which is the AMI name which should be used from the |
13 | -config file (see below). |
14 | - |
15 | -Script also has one configurable option: |
16 | - |
17 | ---config Path to the optional configuration file. Default value is |
18 | - linaro-ami.conf |
19 | - |
20 | +Script Commands And Options |
21 | +--------------------------- |
22 | + |
23 | +linaro-ami accepts following commands: |
24 | + |
25 | + * create |
26 | + |
27 | + Create a custom AMI. This command takes one additional argument which |
28 | + is the AMI section name as defined in the config file (see below). |
29 | + |
30 | + * list |
31 | + |
32 | + List existing custom AMIs. For each AMI, name, AMI id, description, |
33 | + and some other properties are printed. |
34 | + |
35 | + |
36 | +Script also accepts following options: |
37 | + |
38 | +--config |
39 | + |
40 | + Path to the configuration file. Default value is linaro-ami.conf |
41 | + in the current directory. |
42 | + |
43 | +--key=KEYPAIR |
44 | + |
45 | + Name of the EC2-registered SSH keypair name to use when creating |
46 | + work instances. If not supplied, a temporary key will be created |
47 | + and removed once processing finishes. This option is useful for |
48 | + debugging AMI creation process and to ensure that proper ownership |
49 | + information is recorded in EC2 at all times. |
50 | + |
51 | +--private-key=FILE |
52 | + |
53 | + This option should be supplied if --key was (or, alternatively, |
54 | + EC2_PRIVATE_KEY environment variable should be set) and point to |
55 | + private SSH key from the pair as named by --key. |
56 | |
57 | Configuration File Syntax |
58 | ------------------------- |
59 | |
60 | === modified file 'linaro-ami/linaro-ami' |
61 | --- linaro-ami/linaro-ami 2012-06-29 07:58:28 +0000 |
62 | +++ linaro-ami/linaro-ami 2012-07-17 18:43:18 +0000 |
63 | @@ -22,24 +22,37 @@ |
64 | optparser.add_option( |
65 | "--type", default=DEFAULT_AMI_TYPE, |
66 | help="List images of type (%default, use empty for any)") |
67 | + optparser.add_option( |
68 | + "-K", "--private-key", metavar="FILE", |
69 | + help="Private key file (env:EC2_PRIVATE_KEY)") |
70 | + optparser.add_option( |
71 | + "-k", "--key", metavar="KEY", |
72 | + help="Name of EC2-registered key provided by --private-key") |
73 | + |
74 | options, args = optparser.parse_args(sys.argv[1:]) |
75 | if len(args) < 1: |
76 | optparser.error("Wrong number of arguments") |
77 | |
78 | + if not options.private_key: |
79 | + options.private_key = os.environ.get("EC2_PRIVATE_KEY") |
80 | + |
81 | configuration = ConfigParser() |
82 | configuration.read(options.config) |
83 | aws_controller = AwsController() |
84 | remote_executor = RemoteExecutor() |
85 | |
86 | controller = linaro_ami.LinaroAMI(configuration, aws_controller, |
87 | - remote_executor, "ubuntu", options) |
88 | + remote_executor, |
89 | + "ubuntu", options.private_key, |
90 | + options) |
91 | if args[0] == "create": |
92 | if len(args) != 2: |
93 | optparser.error("Usage: %s create <AMI template name from config>" |
94 | % sys.argv[0]) |
95 | if not controller.has_ami(args[1]): |
96 | optparser.error("AMI template '%s' not found" % args[1]) |
97 | - controller.command_create(args[1]) |
98 | + ami_id = controller.command_create(args[1]) |
99 | + print "Created AMI: %s" % ami_id |
100 | elif args[0] == "delete": |
101 | # TODO |
102 | controller.command_delete() |
103 | |
104 | === modified file 'linaro-ami/linaro-ami.conf' |
105 | --- linaro-ami/linaro-ami.conf 2012-07-04 12:01:22 +0000 |
106 | +++ linaro-ami/linaro-ami.conf 2012-07-17 18:43:18 +0000 |
107 | @@ -4,7 +4,7 @@ |
108 | instance_type = t1.micro |
109 | log_file = linaro-ami.log |
110 | bzr_install = sudo apt-get --assume-yes install bzr |
111 | -key_name = ami-test-key |
112 | +key_name = linaro-ami-key |
113 | |
114 | [ab-natty-64bit] |
115 | base_ami = ami-87c31aee |
116 | @@ -12,6 +12,12 @@ |
117 | init_script = node/setup-build-android |
118 | description = Build Slave AMI for android-build.linaro.org based on Natty 64bit |
119 | |
120 | +[ci-natty-64bit] |
121 | +base_ami = ami-87c31aee |
122 | +init_script_repo = lp:~linaro-infrastructure/linaro-ci/lci-build-tools |
123 | +init_script = node/setup-natty-node |
124 | +description = Build Slave AMI for ci.linaro.org based on Natty 64bit |
125 | + |
126 | [ci-oneiric-64bit] |
127 | base_ami = ami-baba68d3 |
128 | init_script_repo = lp:~linaro-infrastructure/linaro-ci/lci-build-tools |
129 | |
130 | === modified file 'linaro-ami/linaro_ami.py' |
131 | --- linaro-ami/linaro_ami.py 2012-07-02 15:59:56 +0000 |
132 | +++ linaro-ami/linaro_ami.py 2012-07-17 18:43:18 +0000 |
133 | @@ -26,10 +26,12 @@ |
134 | """ |
135 | |
136 | def __init__(self, config, aws_controller, remote_executor, username, |
137 | + key_filename=None, |
138 | options={}, aws_access_key_id=None, |
139 | aws_secret_access_key=None): |
140 | self.log = logging.getLogger("linaro-ami") |
141 | self.username = username |
142 | + self.key_filename = key_filename |
143 | self.options = options |
144 | self.aws_access_key_id = aws_access_key_id |
145 | self.aws_secret_access_key = aws_secret_access_key |
146 | @@ -48,21 +50,27 @@ |
147 | based on the parameters provided. |
148 | """ |
149 | |
150 | - self.log.info("Command create start.") |
151 | + self.log.info("Starting instance to create AMI...") |
152 | self.aws.connect() |
153 | |
154 | instance = None |
155 | base_ami = self.config.get(ami_name, "base_ami") |
156 | instance_type = self.config.get(ami_name, "instance_type") |
157 | script_repo = self.config.get(ami_name, "init_script_repo") |
158 | - key_name = self.config.get(ami_name, "key_name") |
159 | + if self.options.key: |
160 | + key_autocreate = False |
161 | + key_name = self.options.key |
162 | + else: |
163 | + key_autocreate = True |
164 | + key_name = self.config.get(ami_name, "key_name") |
165 | |
166 | try: |
167 | - self.aws.import_key_pair(key_name, self.executor.get_public_key()) |
168 | + if key_autocreate: |
169 | + self.aws.import_key_pair(key_name, self.executor.get_public_key()) |
170 | instance = self.aws.run_instance_and_wait(base_ami, |
171 | key_name, instance_type) |
172 | self.log.info("Started instance: %s", instance.public_dns_name) |
173 | - self.executor.connect(instance.public_dns_name, self.username) |
174 | + self.executor.connect(instance.public_dns_name, self.username, self.key_filename) |
175 | self.log.debug("Connected to instance") |
176 | |
177 | self.log.info("Installing bzr.") |
178 | @@ -100,7 +108,8 @@ |
179 | except Exception, e: |
180 | logging.error(e.__class__.__name__ + ":" + str(e)) |
181 | finally: |
182 | - self.aws.delete_key_pair(key_name) |
183 | + if key_autocreate: |
184 | + self.aws.delete_key_pair(key_name) |
185 | self.executor.close() |
186 | if instance: |
187 | self.aws.terminate_instance(instance.id) |
188 | |
189 | === modified file 'linaro-ami/remote_executor.py' |
190 | --- linaro-ami/remote_executor.py 2012-07-03 15:24:06 +0000 |
191 | +++ linaro-ami/remote_executor.py 2012-07-17 18:43:18 +0000 |
192 | @@ -18,9 +18,12 @@ |
193 | # Key used for authentication on the server |
194 | self.key = paramiko.RSAKey.generate(1024) |
195 | |
196 | - def connect(self, address, username): |
197 | + def connect(self, address, username, key_filename=None): |
198 | """Connect to address on port 22 with username and provided RSA key.""" |
199 | - self.client.connect(address, username=username, pkey=self.key) |
200 | + if key_filename: |
201 | + self.client.connect(address, username=username, key_filename=key_filename) |
202 | + else: |
203 | + self.client.connect(address, username=username, pkey=self.key) |
204 | |
205 | def execute(self, command): |
206 | """Execute a command remotely, returns output from that command.""" |
207 | @@ -29,10 +32,21 @@ |
208 | # library to execute script remotely and return output. |
209 | transport = self.client.get_transport() |
210 | channel = transport.open_session() |
211 | - output_stream = channel.makefile() |
212 | + channel.set_combine_stderr(True) |
213 | channel.exec_command(command) |
214 | + # Closed remote stdin, so we don't get deadlock |
215 | + # on something trying to read it there. |
216 | + channel.shutdown_write() |
217 | + output = "" |
218 | + while True: |
219 | + # 512 below comes from a minimal buffer a canaonical unix pipe |
220 | + # may have. Of course, that might be not related to paramiko's |
221 | + # channel buffer, but good enoug anyway. |
222 | + data = channel.recv(512) |
223 | + if not data: |
224 | + break |
225 | + output += data |
226 | status = channel.recv_exit_status() |
227 | - output = output_stream.read() |
228 | channel.close() |
229 | # Only need status to know if command has failed actually |
230 | # We don't want continuation if user script failed |
231 | |
232 | === added file 'linaro-ami/setup.cfg' |
233 | --- linaro-ami/setup.cfg 1970-01-01 00:00:00 +0000 |
234 | +++ linaro-ami/setup.cfg 2012-07-17 18:43:18 +0000 |
235 | @@ -0,0 +1,3 @@ |
236 | +[nosetests] |
237 | +verbosity=2 |
238 | +tests=tests/unit/ |
239 | |
240 | === added directory 'linaro-ami/tests/integration' |
241 | === added file 'linaro-ami/tests/integration/__init__.py' |
242 | --- linaro-ami/tests/integration/__init__.py 1970-01-01 00:00:00 +0000 |
243 | +++ linaro-ami/tests/integration/__init__.py 2012-07-17 18:43:18 +0000 |
244 | @@ -0,0 +1,5 @@ |
245 | +from . import integration_test_support |
246 | + |
247 | + |
248 | +def teardown(): |
249 | + integration_test_support.remove_remote_hosts() |
250 | |
251 | === added file 'linaro-ami/tests/integration/integration_test_support.py' |
252 | --- linaro-ami/tests/integration/integration_test_support.py 1970-01-01 00:00:00 +0000 |
253 | +++ linaro-ami/tests/integration/integration_test_support.py 2012-07-17 18:43:18 +0000 |
254 | @@ -0,0 +1,42 @@ |
255 | +import logging |
256 | +import os |
257 | +import time |
258 | + |
259 | +import boto |
260 | + |
261 | + |
262 | +TEST_INSTANCE_NAME = "linaro-ami integration tests" |
263 | + |
264 | +def get_remote_host(): |
265 | + connection = boto.connect_ec2() |
266 | + for reservation in connection.get_all_instances(): |
267 | + for i in reservation.instances: |
268 | + if i.tags.get("Name") == TEST_INSTANCE_NAME and i.state == "running": |
269 | + print "Found existing inst" |
270 | + return i.dns_name |
271 | + print "Didn't find existing inst - creating" |
272 | + reservation = connection.run_instances("ami-87c31aee", key_name=os.environ["EC2_KEY"], |
273 | + instance_type="t1.micro") |
274 | + instance = reservation.instances[0] |
275 | + connection.create_tags([instance.id], {"Name": TEST_INSTANCE_NAME}) |
276 | + while instance.state != "running": |
277 | + instance.update() |
278 | + time.sleep(5) |
279 | + # Allow OS to boot and sshd to start |
280 | + time.sleep(20) |
281 | + return instance.dns_name |
282 | + |
283 | + |
284 | +def remove_remote_hosts(): |
285 | + connection = boto.connect_ec2() |
286 | + for reservation in connection.get_all_instances(): |
287 | + for i in reservation.instances: |
288 | + if i.state == "running": |
289 | + if i.tags.get("Name") == TEST_INSTANCE_NAME \ |
290 | + or i.key_name in ("ami-test-key", "infra-test-key"): |
291 | + print "Terminating test instance:", i.id |
292 | + connection.terminate_instances([i.id]) |
293 | + |
294 | + |
295 | +if __name__ == "__main__": |
296 | + print get_remote_host() |
297 | |
298 | === renamed file 'linaro-ami/tests/test_aws_controller.py' => 'linaro-ami/tests/integration/test_aws_controller.py' |
299 | === renamed file 'linaro-ami/tests/test_linaro_ami.py' => 'linaro-ami/tests/integration/test_linaro_ami.py' |
300 | --- linaro-ami/tests/test_linaro_ami.py 2012-07-02 15:59:56 +0000 |
301 | +++ linaro-ami/tests/integration/test_linaro_ami.py 2012-07-17 18:43:18 +0000 |
302 | @@ -2,12 +2,18 @@ |
303 | |
304 | import linaro_ami |
305 | |
306 | -from test_support import TestSupport |
307 | +from ..test_support import TestSupport |
308 | from ConfigParser import ConfigParser |
309 | from aws_controller import AwsController |
310 | from remote_executor import RemoteExecutor |
311 | |
312 | |
313 | +class MockOptions: |
314 | + |
315 | + def __getattr__(self, name): |
316 | + return None |
317 | + |
318 | + |
319 | class TestLinaroAMI: |
320 | |
321 | def __init__(self): |
322 | @@ -21,13 +27,13 @@ |
323 | remote_executor = RemoteExecutor() |
324 | |
325 | controller = linaro_ami.LinaroAMI(configuration, aws_controller, |
326 | - remote_executor, "ubuntu") |
327 | + remote_executor, "ubuntu", options=MockOptions()) |
328 | ami_id = controller.command_create("dummy-test") |
329 | print ami_id |
330 | all_images = controller.aws.list_images() |
331 | print all_images |
332 | result = filter(lambda i: i.id == ami_id, all_images) |
333 | - assert len(result) == 1 |
334 | + assert len(result) == 1, "Actual result: %s" % result |
335 | |
336 | def _test_command_list(self): |
337 | controller = linaro_ami.LinaroAMI("tests/data/linaro-ami.conf", |
338 | |
339 | === added file 'linaro-ami/tests/integration/test_remote_executor_int.py' |
340 | --- linaro-ami/tests/integration/test_remote_executor_int.py 1970-01-01 00:00:00 +0000 |
341 | +++ linaro-ami/tests/integration/test_remote_executor_int.py 2012-07-17 18:43:18 +0000 |
342 | @@ -0,0 +1,58 @@ |
343 | +import os |
344 | +import time |
345 | +import signal |
346 | +import random |
347 | + |
348 | +import remote_executor |
349 | +import paramiko |
350 | +from . import integration_test_support |
351 | +from remote_executor import RemoteCommandFailedError |
352 | +from ..test_support import TestSupport |
353 | + |
354 | + |
355 | +class TestRemoteExecutorIntegration: |
356 | + |
357 | + def __init__(self): |
358 | + TestSupport.setup_logging() |
359 | + self.instance_address = integration_test_support.get_remote_host() |
360 | + self.executor = remote_executor.RemoteExecutor() |
361 | + |
362 | + def test_stdout_capture(self): |
363 | + self.executor.connect(self.instance_address, "ubuntu", os.environ["EC2_PRIVATE_KEY"]) |
364 | + s = "test string " + str(random.random()) |
365 | + output = self.executor.execute("echo -n '%s'" % s) |
366 | + assert output == s |
367 | + |
368 | + def test_stderr_capture(self): |
369 | + self.executor.connect(self.instance_address, "ubuntu", os.environ["EC2_PRIVATE_KEY"]) |
370 | + s = "test string " + str(random.random()) |
371 | + output = self.executor.execute("echo -n '%s' 1>&2" % s) |
372 | + assert output == s |
373 | + |
374 | + def test_long_stdout(self): |
375 | + self.executor.connect(self.instance_address, "ubuntu", os.environ["EC2_PRIVATE_KEY"]) |
376 | + size = 500000 |
377 | + signal.alarm(10) |
378 | + output = self.executor.execute("cat /dev/urandom | head -c%d" % size) |
379 | + signal.alarm(0) |
380 | + assert len(output) == size |
381 | + |
382 | + def test_long_stderr(self): |
383 | + self.executor.connect(self.instance_address, "ubuntu", os.environ["EC2_PRIVATE_KEY"]) |
384 | + size = 50000 |
385 | + signal.alarm(10) |
386 | + output = self.executor.execute("cat /dev/urandom | head -c%d 1>&2" % size) |
387 | + signal.alarm(0) |
388 | + assert len(output) == size |
389 | + |
390 | + def test_blocked_stdin(self): |
391 | + # Test that we don't block indefinitely on remote command trying to read stdin |
392 | + self.executor.connect(self.instance_address, "ubuntu", os.environ["EC2_PRIVATE_KEY"]) |
393 | + # We may hang here, provide (abrupt) way out in such case |
394 | + # TODO: Unfortunately, using custom signal handler doesn't |
395 | + # work here (it's not being called, or maybe calling it |
396 | + # block the py interpreter or something) |
397 | + # signal.signal(signal.SIGALRM, sig_handler) |
398 | + signal.alarm(5) |
399 | + self.executor.execute("cat") |
400 | + signal.alarm(0) |
401 | |
402 | === added directory 'linaro-ami/tests/unit' |
403 | === added file 'linaro-ami/tests/unit/__init__.py' |
404 | === renamed file 'linaro-ami/tests/test_remote_executor.py' => 'linaro-ami/tests/unit/test_remote_executor.py' |
405 | --- linaro-ami/tests/test_remote_executor.py 2012-07-02 15:59:56 +0000 |
406 | +++ linaro-ami/tests/unit/test_remote_executor.py 2012-07-17 18:43:18 +0000 |
407 | @@ -1,9 +1,11 @@ |
408 | import time |
409 | import signal |
410 | |
411 | +from mock import Mock |
412 | + |
413 | import remote_executor |
414 | import paramiko |
415 | -from . import test_support |
416 | +from .. import test_support |
417 | from remote_executor import RemoteCommandFailedError |
418 | |
419 | class MockClient: |
420 | @@ -19,29 +21,12 @@ |
421 | class MockTransport: |
422 | |
423 | def open_session(self): |
424 | - return MockChannel() |
425 | - |
426 | -class MockOutputStream: |
427 | - |
428 | - def read(self): |
429 | - return "test" |
430 | - |
431 | -class MockChannel: |
432 | - |
433 | - def makefile(self): |
434 | - return MockOutputStream() |
435 | - |
436 | - def exec_command(self, command): |
437 | - return |
438 | - |
439 | - def get_pty(self): |
440 | - return |
441 | - |
442 | - def recv_exit_status(self): |
443 | - return 0 |
444 | - |
445 | - def close(self): |
446 | - return |
447 | + channel = Mock() |
448 | + # With mock 0.8, the below would be just = ["test", ""] |
449 | + channel.recv.side_effect = lambda sz, buf=["test", ""]: buf.pop(0) |
450 | + channel.recv_exit_status.return_value = 0 |
451 | + return channel |
452 | + |
453 | |
454 | class TestRemoteExecutor: |
455 | |
456 | @@ -57,23 +42,8 @@ |
457 | def test_execute(self): |
458 | self.executor.client = MockClient() |
459 | output = self.executor.execute("command") |
460 | - assert output == "test" |
461 | + assert output == "test", "Unexpected result: " + output |
462 | |
463 | def test_get_public_key(self): |
464 | public_key = self.executor.get_public_key() |
465 | assert ' ' in public_key |
466 | - |
467 | - def _test_execute_stdin(self): |
468 | - def sig_handler(signum, stack_frame): |
469 | - print "foo" |
470 | - assert False, "Remote execution hanged on stdin-reading command" |
471 | - self.connect() |
472 | - # We may hang here, provide (abrupt) way out in such case |
473 | - # TODO: Unfortunately, using custom signal handler doesn't |
474 | - # work here (it's not being called, or maybe calling it |
475 | - # block the py interpreter or something) |
476 | - # signal.signal(signal.SIGALRM, sig_handler) |
477 | - signal.alarm(5) |
478 | - stdin, stdout, stderr, status = self.executor.execute("cat") |
479 | - signal.alarm(0) |
480 | - assert status == -1, "Received unexpected status: %d" % status |
This looks good implementation wise, but do we really need to bother
with the key passing args? I personally rely on boto's env handling to
use different keys for other scripts; see the "run-boto" script in
~linaro/bin on people.linaro.org for an example. This limits the
complexity of the code and keeps the key handling external.
--
Loïc Minier