Skip to content

Commit 676aa57

Browse files
committed
Fixed #418 -- handle deis keys:add ~/.ssh/mykey.pub properly.
1 parent ebd177f commit 676aa57

3 files changed

Lines changed: 95 additions & 65 deletions

File tree

client/deis.py

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"""
4444

4545
from __future__ import print_function
46+
from collections import namedtuple
4647
from cookielib import MozillaCookieJar
4748
from getpass import getpass
4849
from itertools import cycle
@@ -55,6 +56,7 @@
5556
import re
5657
import subprocess
5758
import sys
59+
import tempfile
5860
import time
5961
import urlparse
6062
import webbrowser
@@ -63,7 +65,6 @@
6365
from docopt import docopt
6466
from docopt import DocoptExit
6567
import requests
66-
import tempfile
6768

6869
__version__ = '0.3.1'
6970

@@ -1214,57 +1215,60 @@ def keys_add(self, args):
12141215
12151216
Usage: deis keys:add [<key>]
12161217
"""
1218+
1219+
Key = namedtuple('Key', 'path name type str comment')
1220+
1221+
def parse_key(path):
1222+
"""Parse an SSH public key path into a Key namedtuple."""
1223+
name = path.split(os.path.sep)[-1]
1224+
with open(path) as f:
1225+
data = f.read()
1226+
match = re.match(r'^(ssh-...) ([^ ]+) ?(.*)', data)
1227+
if not match:
1228+
print("Could not parse SSH public key {0}".format(name))
1229+
return
1230+
key_type, key_str, key_comment = match.groups()
1231+
return Key(path, name, key_type, key_str, key_comment)
1232+
12171233
path = args.get('<key>')
12181234
if not path:
1235+
# find public keys and prompt the user to pick one
12191236
ssh_dir = os.path.expanduser('~/.ssh')
1220-
pubkeys = glob.glob(os.path.join(ssh_dir, '*.pub'))
1221-
pubkeys_list = []
1222-
if not pubkeys:
1237+
pubkey_paths = glob.glob(os.path.join(ssh_dir, '*.pub'))
1238+
if not pubkey_paths:
12231239
print('No SSH public keys found')
12241240
return
1241+
pubkeys_list = [parse_key(k) for k in pubkey_paths]
12251242
print('Found the following SSH public keys:')
1226-
for i, k in enumerate(pubkeys):
1227-
key = k.split(os.path.sep)[-1]
1228-
with open(k) as f:
1229-
data = f.read()
1230-
match = re.match(r'^(ssh-...) ([^ ]+) ?(.*)', data)
1231-
if not match:
1232-
print("Could not parse SSH public key {0}".format(key))
1233-
return
1234-
key_type, key_str, _key_comment = match.groups()
1235-
pubkeys_list.append([k, key, key_type, key_str, _key_comment])
1236-
print("{0}) {1} {2}".format(i + 1, key, _key_comment))
1237-
1243+
for i, key_ in enumerate(pubkeys_list):
1244+
print("{}) {} {}".format(i + 1, key_.name, key_.comment))
12381245
inp = raw_input('Which would you like to use with Deis? ')
12391246
try:
12401247
selected_key = pubkeys_list[int(inp) - 1]
1241-
path = selected_key[0]
12421248
except:
12431249
print('Aborting')
12441250
return
1245-
1246-
if not selected_key[4]:
1247-
body = {
1248-
'id': selected_key[1].replace('.pub', ''),
1249-
'public': "{0} {1}".format(
1250-
selected_key[2], selected_key[3]
1251-
)
1252-
}
1253-
sys.stdout.write("Uploading {} to Deis...".format(selected_key[1]))
1254-
else:
1255-
body = {
1256-
'id': selected_key[4],
1257-
'public': "{0} {1}".format(
1258-
selected_key[2], selected_key[3]
1259-
)
1260-
}
1261-
sys.stdout.write("Uploading {} to Deis...".format(selected_key[4]))
1262-
sys.stdout.flush()
1263-
response = self._dispatch('post', '/api/keys', json.dumps(body))
1264-
if response.status_code == requests.codes.created: # @UndefinedVariable
1265-
print('done')
1266-
else:
1267-
raise ResponseError(response)
1251+
else:
1252+
# check the specified key format
1253+
selected_key = parse_key(path)
1254+
if not selected_key:
1255+
return
1256+
# Upload the key to Deis
1257+
if selected_key.comment:
1258+
key_id = selected_key.comment
1259+
else:
1260+
key_id = selected_key.name.replace('.pub', '')
1261+
body = {
1262+
'id': key_id,
1263+
'public': "{} {}".format(selected_key.type, selected_key.str)
1264+
}
1265+
sys.stdout.write("Uploading {} to Deis...".format(key_id))
1266+
sys.stdout.flush()
1267+
response = self._dispatch('post', '/api/keys', json.dumps(body))
1268+
if response.status_code == requests.codes.created: # @UndefinedVariable
1269+
print('done')
1270+
else:
1271+
raise ResponseError(response)
12681272

12691273
def keys_list(self, args):
12701274
"""

client/tests/test_keys.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,32 @@
77

88
from __future__ import unicode_literals
99
from unittest import TestCase
10+
import glob
11+
import os.path
12+
13+
import pexpect
14+
15+
from .utils import DEIS
16+
from .utils import purge
17+
from .utils import register
1018

1119

1220
class KeysTest(TestCase):
1321

14-
pass
22+
@classmethod
23+
def setUpClass(cls):
24+
cls.username, cls.password = register(False, False)
25+
26+
@classmethod
27+
def tearDownClass(cls):
28+
purge(cls.username, cls.password)
29+
30+
def test_add(self):
31+
# test adding a specified key--the "choose a key" path is well
32+
# covered in utils.register()
33+
ssh_dir = os.path.expanduser('~/.ssh')
34+
pubkey = glob.glob(os.path.join(ssh_dir, '*.pub'))[0]
35+
child = pexpect.spawn("{} keys:add {}".format(DEIS, pubkey))
36+
child.expect('Uploading')
37+
child.expect('...done')
38+
child.expect(pexpect.EOF)

client/tests/utils.py

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def random_repo():
8585
return name, EXAMPLES[name]
8686

8787

88-
def register():
88+
def register(add_keys=True, add_providers=True):
8989
"""Register a new Deis user from the command line."""
9090
username = "autotester-{}".format(uuid4().hex[:4])
9191
password = 'password'
@@ -136,28 +136,30 @@ def register():
136136
child.expect("Logged in as {}".format(username))
137137
child.expect(pexpect.EOF)
138138
# add keys
139-
child = pexpect.spawn("{} keys:add".format(DEIS))
140-
child.expect('Which would you like to use with Deis')
141-
for index, key in re.findall('(\d)\) ([ \S]+)', child.before):
142-
if username in key:
143-
child.sendline(index)
144-
break
145-
child.expect('Uploading')
146-
child.expect('...done')
147-
child.expect(pexpect.EOF)
139+
if add_keys:
140+
child = pexpect.spawn("{} keys:add".format(DEIS))
141+
child.expect('Which would you like to use with Deis')
142+
for index, key in re.findall('(\d)\) ([ \S]+)', child.before):
143+
if username in key:
144+
child.sendline(index)
145+
break
146+
child.expect('Uploading')
147+
child.expect('...done')
148+
child.expect(pexpect.EOF)
148149
# discover providers
149-
child = pexpect.spawn("{} providers:discover".format(DEIS))
150-
opt = child.expect(['Import EC2 credentials\? \(y/n\) :',
151-
'No EC2 credentials discovered.'])
152-
if opt == 0:
153-
child.sendline('y')
154-
opt = child.expect(['Import Rackspace credentials\? \(y/n\) :',
155-
'No Rackspace credentials discovered.'])
156-
if opt == 0:
157-
child.sendline('y')
158-
opt = child.expect(['Import DigitalOcean credentials\? \(y/n\) :',
159-
'No DigitalOcean credentials discovered.'])
160-
if opt == 0:
161-
child.sendline('y')
162-
child.expect(pexpect.EOF)
150+
if add_providers:
151+
child = pexpect.spawn("{} providers:discover".format(DEIS))
152+
opt = child.expect(['Import EC2 credentials\? \(y/n\) :',
153+
'No EC2 credentials discovered.'])
154+
if opt == 0:
155+
child.sendline('y')
156+
opt = child.expect(['Import Rackspace credentials\? \(y/n\) :',
157+
'No Rackspace credentials discovered.'])
158+
if opt == 0:
159+
child.sendline('y')
160+
opt = child.expect(['Import DigitalOcean credentials\? \(y/n\) :',
161+
'No DigitalOcean credentials discovered.'])
162+
if opt == 0:
163+
child.sendline('y')
164+
child.expect(pexpect.EOF)
163165
return username, password

0 commit comments

Comments
 (0)