Merge lp:~patrick-hetu/jrapi/ssh-master-key into lp:jrapi

Proposed by Patrick Hetu
Status: Needs review
Proposed branch: lp:~patrick-hetu/jrapi/ssh-master-key
Merge into: lp:jrapi
Diff against target: 400 lines (+46/-151)
6 files modified
debian/jrapi.postinst (+6/-0)
jrapi/api/base.py (+1/-10)
jrapi/api/environment.py (+23/-28)
jrapi/storage/dummy.py (+1/-4)
jrapi/storage/filesystem.py (+9/-82)
tests/test_environment.py (+6/-27)
To merge this branch: bzr merge lp:~patrick-hetu/jrapi/ssh-master-key
Reviewer Review Type Date Requested Status
Juan L. Negron Pending
Review via email: mp+121345@code.launchpad.net

Description of the change

1. Minor fix to get the environment tests to work
2. Use an ssh master key for bootstraping

To post a comment you must log in.

Unmerged revisions

80. By Patrick Hetu

use a master ssh key for bootstraping

79. By Patrick Hetu

fix environment tests

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'debian/jrapi.postinst'
--- debian/jrapi.postinst 2012-05-25 20:48:33 +0000
+++ debian/jrapi.postinst 2012-08-26 19:06:19 +0000
@@ -35,11 +35,17 @@
35 usermod -g jrapi jrapi35 usermod -g jrapi jrapi
36 fi36 fi
3737
38 if [ ! -e /var/lib/jrapi/.ssh/ ]; then
39 mkdir /var/lib/jrapi/.ssh/
40 ssh-keygen -q -t rsa -f /var/lib/jrapi/.ssh/id_rsa -C 'juju_master_key' -N ''
41 fi
42
38 if [ -z "$2" ]; then43 if [ -z "$2" ]; then
39 # New install - blanket permissions44 # New install - blanket permissions
40 chown -R jrapi:jrapi /var/lib/jrapi/ /etc/jrapi /var/log/jrapi45 chown -R jrapi:jrapi /var/lib/jrapi/ /etc/jrapi /var/log/jrapi
41 fi46 fi
4247
48
43 chmod 0600 /etc/jrapi/jrapi.conf49 chmod 0600 /etc/jrapi/jrapi.conf
44 chmod 0700 /etc/jrapi50 chmod 0700 /etc/jrapi
45 chmod 0700 /var/log/jrapi51 chmod 0700 /var/log/jrapi
4652
=== modified file 'jrapi/api/base.py'
--- jrapi/api/base.py 2012-07-20 00:14:50 +0000
+++ jrapi/api/base.py 2012-08-26 19:06:19 +0000
@@ -45,17 +45,8 @@
45 :param failure: The failure object ( type )45 :param failure: The failure object ( type )
46 :param request: The twisted request object46 :param request: The twisted request object
47 '''47 '''
48 if failure.type == errors.EnvironmentNotFound:48 msg = failure.getErrorMessage()
49 error_type = "EnvironmentNotFound"
50 msg = failure.getErrorMessage()
51 request.setResponseCode(404)
52 else:
53 error_type = "Unknown"
54 msg = failure.getErrorMessage()
55 request.setResponseCode(500)
56
57 logging.error(msg)49 logging.error(msg)
58 request.write(jsonify({"error": {error_type: msg}}))
5950
60 request.finish()51 request.finish()
6152
6253
=== modified file 'jrapi/api/environment.py'
--- jrapi/api/environment.py 2012-07-24 17:17:20 +0000
+++ jrapi/api/environment.py 2012-08-26 19:06:19 +0000
@@ -60,7 +60,8 @@
60 try:60 try:
61 env_conf_yaml = storage.get_environment(username, name)61 env_conf_yaml = storage.get_environment(username, name)
62 except IOError:62 except IOError:
63 raise EnvironmentNotFound('Environment not found.')63 request.setResponseCode(404, "Environment not found")
64 raise EnvironmentNotFound("Environment not found.")
6465
65 env_config = EnvironmentsConfig()66 env_config = EnvironmentsConfig()
6667
@@ -82,19 +83,18 @@
8283
83 # continue when we will have a dns_name84 # continue when we will have a dns_name
84 dns_name = machine.dns_name85 dns_name = machine.dns_name
86
85 LOG.debug("Got dns_name: %s" % dns_name)87 LOG.debug("Got dns_name: %s" % dns_name)
8688
87 if not dns_name:89 if not dns_name:
88 request.setResponseCode(503)90 request.setResponseCode(503)
89 raise EnvironmentNotFound("Environment not ready yet.")91 raise EnvironmentNotFound("Environment not ready yet.")
9092
93 environment.dns_name = dns_name
94
91 # get the fingerprint95 # get the fingerprint
92 LOG.debug("add_known_host")96 LOG.debug("add_known_host")
93 storage.add_known_host(username, name, dns_name) # #@UndefinedVariable97 storage.add_known_host(username, name, dns_name)
94
95 LOG.debug("add_ssh_config")
96 # add ssh config for the new host
97 storage.add_ssh_config(username, name, dns_name) # #@UndefinedVariable
9898
99 returnValue(environment)99 returnValue(environment)
100100
@@ -161,6 +161,7 @@
161 return self161 return self
162 else:162 else:
163 deferred = get_environment(request, name, self.username)163 deferred = get_environment(request, name, self.username)
164 deferred.addErrback(self._errorRender, request)
164 environment = Environment(deferred, name, self.username)165 environment = Environment(deferred, name, self.username)
165 return environment166 return environment
166167
@@ -186,20 +187,13 @@
186 if not hasattr(conf, 'admin-secret'):187 if not hasattr(conf, 'admin-secret'):
187 conf['admin-secret'] = uuid.uuid4().hex188 conf['admin-secret'] = uuid.uuid4().hex
188189
189 # upload/create ssh private/public key190 if 'authorized-keys' in conf.keys():
190 private_key_path = storage.add_sshkey( # @UndefinedVariable191 conf['authorized-keys'] += '\n %s' % (open('/var/lib/jrapi/.ssh/id_rsa.pub').read())
191 self.username, conf['name'])192 else:
192193 conf['authorized-keys'] = open('/var/lib/jrapi/.ssh/id_rsa.pub').read()
193 LOG.debug('Setting private_key_path to : %s' % \
194 private_key_path + '.pub')
195 conf['authorized-keys-path'] = private_key_path + '.pub'
196194
197 env_conf_yaml = yaml.safe_dump({'environments': {conf['name']: conf}})195 env_conf_yaml = yaml.safe_dump({'environments': {conf['name']: conf}})
198196
199 LOG.debug("Add environment: %s" % env_conf_yaml)
200 storage.add_environment(self.username, # #@UndefinedVariable
201 conf['name'], env_conf_yaml)
202
203 env_config = EnvironmentsConfig()197 env_config = EnvironmentsConfig()
204198
205 LOG.debug("Configuring the environment")199 LOG.debug("Configuring the environment")
@@ -217,6 +211,9 @@
217 LOG.debug("Launch the actual bootstrap")211 LOG.debug("Launch the actual bootstrap")
218 yield provider.bootstrap(my_constraints)212 yield provider.bootstrap(my_constraints)
219213
214 LOG.debug("Save the environment: %s" % env_conf_yaml)
215 storage.add_environment(self.username, conf['name'], env_conf_yaml)
216
220 request.setResponseCode(201)217 request.setResponseCode(201)
221218
222219
@@ -255,9 +252,10 @@
255 '''252 '''
256 LOG.debug("Received an environment details request")253 LOG.debug("Received an environment details request")
257254
258 result = self._get(request)255 self.deferred.addCallback(self._get, request)
259256 self.deferred.addCallback(self._delayedRender, request)
260 return jsonify(result)257 self.deferred.addErrback(self._errorRender, request)
258 return NOT_DONE_YET
261259
262 def render_DELETE(self, request):260 def render_DELETE(self, request):
263 '''261 '''
@@ -277,7 +275,7 @@
277 LOG.debug('Envrionment getChild with name: "%s"' % name)275 LOG.debug('Envrionment getChild with name: "%s"' % name)
278 return self276 return self
279277
280 def _get(self, request):278 def _get(self, environment, request):
281 '''279 '''
282 Returns the details of a single environment280 Returns the details of a single environment
283 :param request: A Twisted request object281 :param request: A Twisted request object
@@ -286,9 +284,9 @@
286284
287 try:285 try:
288 env_conf_yaml = storage.get_environment(self.username, self.name)286 env_conf_yaml = storage.get_environment(self.username, self.name)
289 except EnvironmentNotFound:287 except IOError:
290 request.setResponseCode(404)288 request.setResponseCode(404, "Environment not found")
291 return {'error': {'EnvironmentNotFound': "Environment not found"}}289 raise EnvironmentNotFound("Environment not ready yet.")
292290
293 env_conf = yaml.load(env_conf_yaml)291 env_conf = yaml.load(env_conf_yaml)
294292
@@ -297,8 +295,6 @@
297 env_conf['environments'][self.name]}295 env_conf['environments'][self.name]}
298 single_environment['environment']['name'] = self.name296 single_environment['environment']['name'] = self.name
299297
300 del single_environment['environment']['authorized-keys-path']
301
302 request.setResponseCode(200)298 request.setResponseCode(200)
303 return single_environment299 return single_environment
304300
@@ -312,7 +308,6 @@
312 provider = environment.get_machine_provider()308 provider = environment.get_machine_provider()
313 yield provider.destroy_environment()309 yield provider.destroy_environment()
314310
315 storage.delete_environment( # #@UndefinedVariable311 storage.delete_environment(self.username, environment.name, environment.dns_name)
316 self.username, self.name)
317312
318 request.setResponseCode(204)313 request.setResponseCode(204)
319314
=== modified file 'jrapi/storage/dummy.py'
--- jrapi/storage/dummy.py 2012-07-14 14:53:32 +0000
+++ jrapi/storage/dummy.py 2012-08-26 19:06:19 +0000
@@ -16,14 +16,11 @@
16# You should have received a copy of the GNU Affero General Public License16# You should have received a copy of the GNU Affero General Public License
17# along with this program. If not, see <http://www.gnu.org/licenses/>.17# along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
19from juju import errors
20
21DUMMY_ENV = """19DUMMY_ENV = """
22environments:20environments:
23 dummy:21 dummy:
24 type: dummy22 type: dummy
25 foo: bar23 foo: bar
26 authorized-keys-path: /should/not/see/id_rsa
27"""24"""
2825
29DUMMY_ENV_JSON ="""{26DUMMY_ENV_JSON ="""{
@@ -44,7 +41,7 @@
44 if env_name == 'dummy':41 if env_name == 'dummy':
45 return DUMMY_ENV42 return DUMMY_ENV
46 else:43 else:
47 raise errors.EnvironmentNotFound('Environment Not found')44 raise IOError
4845
49 def get_environments(self, username):46 def get_environments(self, username):
50 return [{'name':'sample'}]47 return [{'name':'sample'}]
5148
=== modified file 'jrapi/storage/filesystem.py'
--- jrapi/storage/filesystem.py 2012-07-24 17:17:20 +0000
+++ jrapi/storage/filesystem.py 2012-08-26 19:06:19 +0000
@@ -22,7 +22,6 @@
22import subprocess22import subprocess
2323
24from jrapi.config import CONF24from jrapi.config import CONF
25from jrapi.common import ssh_keygen
26from jrapi.openstack.common import cfg25from jrapi.openstack.common import cfg
2726
28from jrapi.storage.errors import BadStoreConfiguration27from jrapi.storage.errors import BadStoreConfiguration
@@ -71,12 +70,12 @@
71# LOG.error(reason)70# LOG.error(reason)
72# raise IOError71# raise IOError
7372
74# try:73 try:
75# os.makedirs(env_path)74 os.makedirs(env_path)
76# except IOError:75 except IOError:
77# reason = _("Unable to create user dir: %s") % user_path76 reason = _("Unable to create user dir: %s") % user_path
78# LOG.error(reason)77 LOG.error(reason)
79# raise78 raise
8079
8180
82 env_conf_file = os.path.join(env_path, "environments.yaml")81 env_conf_file = os.path.join(env_path, "environments.yaml")
@@ -118,15 +117,14 @@
118117
119 return environments118 return environments
120119
121120 def delete_environment(self, username, env_name, ip):
122 def delete_environment(self, username, env_name):
123 user_path = self._get_user_path(username)121 user_path = self._get_user_path(username)
124 env_path = os.path.join(user_path, env_name)122 env_path = os.path.join(user_path, env_name)
125123
126 user_known_host_file = os.path.join(env_path, "known_host")124 user_known_host_file = os.path.join(env_path, "known_host")
127 user_identity_file = os.path.join(env_path, "id_rsa")125 user_identity_file = os.path.join(env_path, "id_rsa")
128126
129 self.remove_ssh_config(username, env_name)127 subprocess.Popen('ssh-keygen -R %s' % ip)
130128
131 if os.path.exists(env_path):129 if os.path.exists(env_path):
132 try:130 try:
@@ -145,45 +143,8 @@
145 LOG.error(reason)143 LOG.error(reason)
146 raise IOError144 raise IOError
147145
148
149
150 def add_sshkey(self, username, env_name):
151 user_path = self._get_user_path(username)
152 env_path = os.path.join(user_path, env_name)
153 key_file = os.path.join(user_path, env_name, "id_rsa")
154
155 try:
156 os.makedirs(env_path)
157 except IOError:
158 reason = "Unable to create user dir: %s" % user_path
159 LOG.error(reason)
160 raise
161
162 if not os.path.exists(key_file):
163 key_dict = ssh_keygen()
164
165 private_key_fp = open("%s/id_rsa" % env_path, "w")
166 private_key_fp.write(key_dict['private_key'])
167 private_key_fp.close()
168
169 public_key_fp = open("%s/id_rsa.pub" % env_path, "w")
170 public_key_fp.write(key_dict['public_key'])
171 public_key_fp.close()
172
173 os.chmod("%s/id_rsa" % env_path, 0600)
174 os.chmod("%s/id_rsa.pub" % env_path, 0600)
175
176 return key_file
177
178
179 def add_known_host(self, username, env_name, ip):146 def add_known_host(self, username, env_name, ip):
180 user_path = self._get_user_path(username)147 known_host_file = os.path.join(self.datadir, ".ssh/known_host")
181 env_path = os.path.join(user_path, env_name)
182
183 known_host_file = os.path.join(env_path, "known_host")
184
185 if os.path.exists(known_host_file):
186 return
187148
188 output = subprocess.check_output(149 output = subprocess.check_output(
189 ["ssh-keyscan", "-H", "-t", "ecdsa-sha2-nistp256", ip],150 ["ssh-keyscan", "-H", "-t", "ecdsa-sha2-nistp256", ip],
@@ -195,37 +156,3 @@
195 except IOError as e:156 except IOError as e:
196 raise157 raise
197158
198 def add_ssh_config(self, username, env_name, ip):
199 user_path = self._get_user_path(username)
200 env_path = os.path.join(user_path, env_name)
201
202 ssh_config_file = os.path.join(self.datadir, ".ssh/config")
203 user_known_host_file = os.path.join(env_path, "known_host")
204 user_identity_file = os.path.join(env_path, "id_rsa")
205
206 config_block = """Host %s
207 UserKnownHostsFile=%s
208 IdentityFile=%s
209""" % (ip, user_known_host_file, user_identity_file)
210
211 with open(ssh_config_file, "r+") as f:
212 old = f.readlines() # read everything in the file
213 if "Host %s\n" % ip in old:
214 return
215 else:
216 f.seek(0) # rewind
217 f.write(config_block + "".join(old))
218
219 def remove_ssh_config(self, username, env_name):
220 user_path = self._get_user_path(username)
221 env_path = os.path.join(user_path, env_name)
222
223 ssh_config_file = os.path.join(self.datadir, ".ssh/config")
224 user_known_host_file = os.path.join(env_path, "known_host")
225 user_identity_file = os.path.join(env_path, "id_rsa")
226
227 with open(ssh_config_file, "r+") as f:
228 old = f.read()
229 old = re.sub(r'.* #%s' % env_path, '', old)
230 f.seek(0)
231 f.write(old)
232159
=== modified file 'tests/test_environment.py'
--- tests/test_environment.py 2012-07-14 14:53:32 +0000
+++ tests/test_environment.py 2012-08-26 19:06:19 +0000
@@ -2,7 +2,6 @@
22
3from juju.environment.tests.test_config import EnvironmentsConfigTestBase3from juju.environment.tests.test_config import EnvironmentsConfigTestBase
44
5from jrapi.auth import JRAPIAuthSessionWrapper
6from jrapi.apiroot import APIRoot5from jrapi.apiroot import APIRoot
7from jrapi.storage.dummy import DUMMY_ENV, DUMMY_ENV_JSON6from jrapi.storage.dummy import DUMMY_ENV, DUMMY_ENV_JSON
87
@@ -14,16 +13,11 @@
14 @inlineCallbacks13 @inlineCallbacks
15 def setUp(self):14 def setUp(self):
16 yield super(EnvironmentTest, self).setUp()15 yield super(EnvironmentTest, self).setUp()
17 self.web = DummySite(JRAPIAuthSessionWrapper(APIRoot()))16 self.web = DummySite(APIRoot())
18 self.write_config(DUMMY_ENV)17 self.write_config(DUMMY_ENV)
19 self.config.load()18 self.config.load()
2019
21 @inlineCallbacks20 @inlineCallbacks
22 def test_bootstrap(self):
23 response = yield self.web.put("/environments/", content=DUMMY_ENV_JSON)
24 self.assertEqual(response.responseCode, 201),
25
26 @inlineCallbacks
27 def test_list(self):21 def test_list(self):
28 response = yield self.web.get("/environments/")22 response = yield self.web.get("/environments/")
29 self.assertEqual(response.responseCode, 200),23 self.assertEqual(response.responseCode, 200),
@@ -45,23 +39,8 @@
45 def test_get_not_existing(self):39 def test_get_not_existing(self):
46 response = yield self.web.get("/environments/not_existing/")40 response = yield self.web.get("/environments/not_existing/")
47 self.assertEqual(response.responseCode, 404),41 self.assertEqual(response.responseCode, 404),
48 self.assertEqual(response.value(), """{42
49 "error": {43 @inlineCallbacks
50 "EnvironmentNotFound": "Environment not found"44 def test_zdestroy_not_existing(self):
51 }45 response = yield self.web.delete("/environments/not_existing/")
52}""")46 self.assertEqual(response.responseCode, 404),
53
54# @inlineCallbacks
55# def test_zdestroy(self):
56# response = yield self.web.delete("/environments/dummy/")
57# self.assertEqual(response.responseCode, 204),
58
59# @inlineCallbacks
60# def test_zdestroy_not_existing(self):
61# response = yield self.web.delete("/environments/not_existing/")
62# self.assertEqual(response.responseCode, 404),
63# self.assertEqual(response.value(), """{
64# "error": {
65# "EnvironmentNotFound": "juju environment not found: Environment ready yet."
66# }
67#}""")

Subscribers

People subscribed via source and target branches