Merge lp:~makyo/juju-quickstart/ssh-2-create-keys into lp:juju-quickstart
- ssh-2-create-keys
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 31 |
Proposed branch: | lp:~makyo/juju-quickstart/ssh-2-create-keys |
Merge into: | lp:juju-quickstart |
Diff against target: |
174 lines (+104/-26) 2 files modified
quickstart/app.py (+43/-12) quickstart/tests/test_app.py (+61/-14) |
To merge this branch: | bzr merge lp:~makyo/juju-quickstart/ssh-2-create-keys |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju GUI Hackers | Pending | ||
Review via email:
|
Commit message
Description of the change
Generate SSH keys if none exist.
This is step 2: provide the user with the option to let Quickstart generate SSH keys.
Note: this is now an incremental branch, with better checks on ssh identities in the future; there's a card to represent this on the kanban board.
To test: make check
to QA:
1. mv ~/.ssh ~/.ssh.old
2a. .venv/bin/python juju-quickstart # should ask if you want to generate; say no, should exit
2b. .venv/bin/python juju-quickstart # should ask if you want to generate; say yes, shoud prompt for password, then continue, generating a new key
3. mv ~/.ssh.old ~/.ssh
4. ssh-agent
5. ssh-add
6. .venv/bin/python juju-quickstart # should bootstrap
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Madison Scott-Clary (makyo) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Francesco Banconi (frankban) wrote : | # |
LGTM with (possible) changes, thank you!
https:/
File quickstart/app.py (right):
https:/
quickstart/
create_
This can be written just as:
if create_keys.lower() == 'y':
In general, for sequences (like strings), use the fact that if they are
empty they are also False, e.g.:
use "if seq" instead of "if len(seq) [> 0]".
https:/
quickstart/
Thinking about this in retrospective, I am not sure we need the user to
have an ssh agent set up. It seems all we need are ssh keys, so IIUC the
return codes you described, a 130 could still be ok for us. What do you
think? Feel free to disagree.
https:/
quickstart/
utils.call(
Here, if the user decides to continue, we just generate ssh keys without
starting the agent. This is ok IMHO, but then we don't want to require
the agent on the next quickstart run.
https:/
quickstart/
{}'.format(error))
All the other quickstart messages are lower cased: we might want to
change this, but for now let's follow that pattern.
https:/
quickstart/
{}'.format(
Ditto.
https:/
File quickstart/
https:/
quickstart/
assert_called_with? <shrug>
https:/
quickstart/
mock_call.
assert_
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Madison Scott-Clary (makyo) wrote : | # |
Thanks for the review; need to switch check for keys as noted below, but
everything else is done.
https:/
File quickstart/app.py (right):
https:/
quickstart/
create_
On 2013/12/09 10:45:41, frankban wrote:
> This can be written just as:
> if create_keys.lower() == 'y':
> In general, for sequences (like strings), use the fact that if they
are empty
> they are also False, e.g.:
> use "if seq" instead of "if len(seq) [> 0]".
Done.
https:/
quickstart/
On 2013/12/09 10:45:41, frankban wrote:
> Thinking about this in retrospective, I am not sure we need the user
to have an
> ssh agent set up. It seems all we need are ssh keys, so IIUC the
return codes
> you described, a 130 could still be ok for us. What do you think? Feel
free to
> disagree.
I ran into that with this branch, and agree with you. I don't think we
need to start the agent. The problem I was running into in the original
branch is that an agent was already running as part of working with bzr.
Going to switch to just keygen and use a different test, not `ssh-add
-l`; I was under the impression that would list keys regardless of agent
status, but testing from a tty, that's not the case.
https:/
quickstart/
utils.call(
On 2013/12/09 10:45:41, frankban wrote:
> Here, if the user decides to continue, we just generate ssh keys
without
> starting the agent. This is ok IMHO, but then we don't want to require
the agent
> on the next quickstart run.
Agreed, but see note about the current ssh-add -l test.
https:/
quickstart/
{}'.format(error))
On 2013/12/09 10:45:41, frankban wrote:
> All the other quickstart messages are lower cased: we might want to
change this,
> but for now let's follow that pattern.
Done.
https:/
quickstart/
{}'.format(
On 2013/12/09 10:45:41, frankban wrote:
> Ditto.
Done.
https:/
File quickstart/
https:/
quickstart/
On 2013/12/09 10:45:41, frankban wrote:
> assert_called_with? <shrug>
Done.
https:/
quickstart/
mock_call.
On 2013/12/09 10:45:41, frankban wrote:
> assert_
Done.
- 31. By Madison Scott-Clary
-
Responding to review.
- 32. By Madison Scott-Clary
-
Switching to find from ssh-add
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Madison Scott-Clary (makyo) wrote : | # |
Please take a look.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Francesco Banconi (frankban) wrote : | # |
LGTM thank you!
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Madison Scott-Clary (makyo) wrote : | # |
*** Submitted:
Generate SSH keys if none exist.
This is step 2: provide the user with the option to let Quickstart
generate SSH keys.
Note: this is now an incremental branch, with better checks on ssh
identities in the future; there's a card to represent this on the kanban
board.
To test: make check
to QA:
1. mv ~/.ssh ~/.ssh.old
2a. .venv/bin/python juju-quickstart # should ask if you want to
generate; say no, should exit
2b. .venv/bin/python juju-quickstart # should ask if you want to
generate; say yes, shoud prompt for password, then continue, generating
a new key
3. mv ~/.ssh.old ~/.ssh
4. ssh-agent
5. ssh-add
6. .venv/bin/python juju-quickstart # should bootstrap
R=frankban
CC=
https:/
Preview Diff
1 | === modified file 'quickstart/app.py' |
2 | --- quickstart/app.py 2013-12-06 17:18:22 +0000 |
3 | +++ quickstart/app.py 2013-12-09 18:12:20 +0000 |
4 | @@ -24,6 +24,7 @@ |
5 | import functools |
6 | import json |
7 | import logging |
8 | +import os |
9 | import time |
10 | |
11 | import jujuclient |
12 | @@ -88,18 +89,48 @@ |
13 | |
14 | |
15 | def ensure_ssh_keys(): |
16 | - retcode = utils.call('/usr/bin/ssh-add', '-l')[0] |
17 | - if retcode > 0: |
18 | - # The return code will be 130 if no agent was found; it will be 127 if |
19 | - # no keys were found. The return code will be 0 (with an output of |
20 | - # keys) if everything was successful. |
21 | - raise ProgramExit('No ssh keys were found.\n' |
22 | - 'Please run these commands and follow the ' |
23 | - 'instructions they produce before running ' |
24 | - 'quickstart again:\n\n' |
25 | - 'ssh-keygen -t rsa\n' |
26 | - 'ssh-agent\n' |
27 | - 'ssh-add') |
28 | + """Ensure that SSH keys are available. |
29 | + |
30 | + Allow the user to let quickstart create SSH keys, or quit by raising a |
31 | + ProgramExit if they would like to create the key themselves. |
32 | + """ |
33 | + ssh_dir = os.path.join(os.path.expanduser('~'), '.ssh') |
34 | + retcode = utils.call('/usr/bin/find', ssh_dir, '-name', 'id_*.pub')[0] |
35 | + if retcode > 0: |
36 | + print('No SSH keys were found in ~/.ssh\n\n' |
37 | + 'Would you like quickstart to create them for you? Note that ' |
38 | + 'this will create a new SSH key with the passphrase you provide ' |
39 | + 'available to your SSH agent. ssh-keygen will ask for a ' |
40 | + 'passphrase, but juju quickstart will not have access to it.\n' |
41 | + 'Alternatively, you may create the keys on your own.\n') |
42 | + create_keys = raw_input('Continue [y/N]? ') |
43 | + if create_keys.lower() == 'y': |
44 | + create_ssh_keys() |
45 | + else: |
46 | + raise ProgramExit('Please run these commands and follow the ' |
47 | + 'instructions they produce before running ' |
48 | + 'quickstart again:\n\n' |
49 | + 'ssh-keygen -b 4096 -t rsa') |
50 | + |
51 | + |
52 | +def create_ssh_keys(): |
53 | + """Create SSH keys for the user |
54 | + |
55 | + Raises ProgramExit if the keys were not created successfully. |
56 | + |
57 | + NB: this involves user interaction for entering the passphrase; this may |
58 | + have to change if creating SSH keys takes place in the urwid interface. |
59 | + """ |
60 | + key_file = os.path.join(os.path.expanduser('~'), '.ssh', 'id_juju_rsa') |
61 | + retcode, _, error = utils.call('/usr/bin/ssh-keygen', |
62 | + '-q', # silent |
63 | + '-b', '4096', # 4096 bytes |
64 | + '-t', 'rsa', # RSA type |
65 | + '-C', 'Generated with Juju Quickstart', |
66 | + '-f', key_file) |
67 | + if retcode > 0: |
68 | + raise ProgramExit('error generating ssh key! {}'.format(error)) |
69 | + print('a new ssh key was generated in {}'.format(key_file)) |
70 | |
71 | |
72 | def ensure_environments(): |
73 | |
74 | === modified file 'quickstart/tests/test_app.py' |
75 | --- quickstart/tests/test_app.py 2013-12-06 17:18:22 +0000 |
76 | +++ quickstart/tests/test_app.py 2013-12-09 18:12:20 +0000 |
77 | @@ -20,6 +20,7 @@ |
78 | |
79 | from contextlib import contextmanager |
80 | import json |
81 | +import os |
82 | import unittest |
83 | |
84 | import jujuclient |
85 | @@ -176,29 +177,75 @@ |
86 | self.assertEqual(3, mock_call.call_count) |
87 | |
88 | |
89 | +@helpers.mock_print |
90 | class TestEnsureSSHKeys( |
91 | helpers.CallTestsMixin, ProgramExitTestsMixin, unittest.TestCase): |
92 | |
93 | - def test_success(self): |
94 | + print_msg = 'No SSH keys were found in ~/.ssh\n\n' \ |
95 | + 'Would you like quickstart to create them for you? Note ' \ |
96 | + 'that this will create a new SSH key with the passphrase ' \ |
97 | + 'you provide available to your SSH agent. ssh-keygen will ' \ |
98 | + 'ask for a passphrase, but juju quickstart will not have ' \ |
99 | + 'access to it.\nAlternatively, you may create the keys on ' \ |
100 | + 'your own.\n' |
101 | + ssh_key_find = ('/usr/bin/find', |
102 | + os.path.join(os.path.expanduser('~'), '.ssh'), |
103 | + '-name', 'id_*.pub') |
104 | + |
105 | + def patch_raw_input(self, return_value='N'): |
106 | + mock_raw_input = mock.Mock(return_value=return_value) |
107 | + return mock.patch('__builtin__.raw_input', mock_raw_input) |
108 | + |
109 | + def test_success(self, mock_print): |
110 | with self.patch_call(0) as mock_call: |
111 | app.ensure_ssh_keys() |
112 | - mock_call.assert_has_calls([ |
113 | - mock.call('/usr/bin/ssh-add', '-l') |
114 | - ]) |
115 | + mock_call.assert_called_with(*self.ssh_key_find) |
116 | |
117 | - def test_failure(self): |
118 | - msg = 'No ssh keys were found.\n' \ |
119 | - 'Please run these commands and follow the instructions they ' \ |
120 | + def test_failure_no_keygen(self, mock_print): |
121 | + msg = 'Please run these commands and follow the instructions they ' \ |
122 | 'produce before running quickstart again:\n\n' \ |
123 | - 'ssh-keygen -t rsa\n' \ |
124 | - 'ssh-agent\n' \ |
125 | - 'ssh-add' |
126 | + 'ssh-keygen -b 4096 -t rsa' |
127 | with self.assert_program_exit(msg): |
128 | with self.patch_call(130) as mock_call: |
129 | - app.ensure_ssh_keys() |
130 | - mock_call.assert_has_calls([ |
131 | - mock.call('/usr/bin/ssh-add', '-l') |
132 | - ]) |
133 | + with self.patch_raw_input() as mock_raw_input: |
134 | + app.ensure_ssh_keys() |
135 | + mock_call.assert_called_with(*self.ssh_key_find) |
136 | + mock_print.assert_has_calls([mock.call(self.print_msg)]) |
137 | + self.assertTrue(mock_raw_input.called) |
138 | + |
139 | + def test_failure_with_keygen(self, mock_print): |
140 | + with self.patch_call(130) as mock_call: |
141 | + with self.patch_raw_input(return_value='Y') as mock_raw_input: |
142 | + with mock.patch('quickstart.app.create_ssh_keys') \ |
143 | + as mock_create_ssh_keys: |
144 | + app.ensure_ssh_keys() |
145 | + mock_call.assert_called_with(*self.ssh_key_find) |
146 | + mock_print.assert_has_calls([mock.call(self.print_msg)]) |
147 | + self.assertTrue(mock_raw_input.called) |
148 | + self.assertTrue(mock_create_ssh_keys.called) |
149 | + |
150 | + |
151 | +@helpers.mock_print |
152 | +class TestCreateSSHKeys( |
153 | + helpers.CallTestsMixin, ProgramExitTestsMixin, unittest.TestCase): |
154 | + |
155 | + def test_success(self, mock_print): |
156 | + key_file = os.path.join(os.path.expanduser('~'), '.ssh', 'id_juju_rsa') |
157 | + with self.patch_call(0) as mock_call: |
158 | + app.create_ssh_keys() |
159 | + mock_call.assert_called_once_with('/usr/bin/ssh-keygen', |
160 | + '-q', '-b', '4096', '-t', 'rsa', |
161 | + '-C', |
162 | + 'Generated with Juju Quickstart', |
163 | + '-f', key_file) |
164 | + mock_print.assert_called_with('a new ssh key was generated in {}' |
165 | + .format(key_file)) |
166 | + |
167 | + def test_failure(self, mock_print): |
168 | + with self.assert_program_exit('error generating ssh key! Oh no!'): |
169 | + with self.patch_call(1, error='Oh no!') as mock_call: |
170 | + app.create_ssh_keys() |
171 | + self.assertTrue(mock_call.called) |
172 | |
173 | |
174 | @helpers.mock_print |
Reviewers: mp+198135_ code.launchpad. net,
Message:
Please take a look.
Description:
Generate SSH keys if none exist.
This is step 2: provide the user with the option to let Quickstart
generate SSH keys.
To test: make check
to QA:
1. mv ~/.ssh ~/.ssh.old
2a. .venv/bin/python juju-quickstart # should ask if you want to
generate; say no, should exit
2b. .venv/bin/python juju-quickstart # should ask if you want to
generate; say yes, shoud prompt for password, then continue, generating
a new key
3. mv ~/.ssh.old ~/.ssh
4. ssh-agent
5. ssh-add
6. .venv/bin/python juju-quickstart # should bootstrap
https:/ /code.launchpad .net/~makyo/ juju-quickstart /ssh-2- create- keys/+merge/ 198135
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/36080044/
Affected files (+108, -20 lines): tests/test_ app.py
A [revision details]
M quickstart/app.py
M quickstart/