Merge lp:~patrick-hetu/jrapi/ssh-master-key into lp:jrapi
- ssh-master-key
- Merge into 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 |
| Related bugs: | |
| Related blueprints: | Deploy with an ssh master key (Undefined) |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Juan L. Negron | Pending | ||
| Review via email: | |||
Commit message
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
| 1 | === modified file 'debian/jrapi.postinst' |
| 2 | --- debian/jrapi.postinst 2012-05-25 20:48:33 +0000 |
| 3 | +++ debian/jrapi.postinst 2012-08-26 19:06:19 +0000 |
| 4 | @@ -35,11 +35,17 @@ |
| 5 | usermod -g jrapi jrapi |
| 6 | fi |
| 7 | |
| 8 | + if [ ! -e /var/lib/jrapi/.ssh/ ]; then |
| 9 | + mkdir /var/lib/jrapi/.ssh/ |
| 10 | + ssh-keygen -q -t rsa -f /var/lib/jrapi/.ssh/id_rsa -C 'juju_master_key' -N '' |
| 11 | + fi |
| 12 | + |
| 13 | if [ -z "$2" ]; then |
| 14 | # New install - blanket permissions |
| 15 | chown -R jrapi:jrapi /var/lib/jrapi/ /etc/jrapi /var/log/jrapi |
| 16 | fi |
| 17 | |
| 18 | + |
| 19 | chmod 0600 /etc/jrapi/jrapi.conf |
| 20 | chmod 0700 /etc/jrapi |
| 21 | chmod 0700 /var/log/jrapi |
| 22 | |
| 23 | === modified file 'jrapi/api/base.py' |
| 24 | --- jrapi/api/base.py 2012-07-20 00:14:50 +0000 |
| 25 | +++ jrapi/api/base.py 2012-08-26 19:06:19 +0000 |
| 26 | @@ -45,17 +45,8 @@ |
| 27 | :param failure: The failure object ( type ) |
| 28 | :param request: The twisted request object |
| 29 | ''' |
| 30 | - if failure.type == errors.EnvironmentNotFound: |
| 31 | - error_type = "EnvironmentNotFound" |
| 32 | - msg = failure.getErrorMessage() |
| 33 | - request.setResponseCode(404) |
| 34 | - else: |
| 35 | - error_type = "Unknown" |
| 36 | - msg = failure.getErrorMessage() |
| 37 | - request.setResponseCode(500) |
| 38 | - |
| 39 | + msg = failure.getErrorMessage() |
| 40 | logging.error(msg) |
| 41 | - request.write(jsonify({"error": {error_type: msg}})) |
| 42 | |
| 43 | request.finish() |
| 44 | |
| 45 | |
| 46 | === modified file 'jrapi/api/environment.py' |
| 47 | --- jrapi/api/environment.py 2012-07-24 17:17:20 +0000 |
| 48 | +++ jrapi/api/environment.py 2012-08-26 19:06:19 +0000 |
| 49 | @@ -60,7 +60,8 @@ |
| 50 | try: |
| 51 | env_conf_yaml = storage.get_environment(username, name) |
| 52 | except IOError: |
| 53 | - raise EnvironmentNotFound('Environment not found.') |
| 54 | + request.setResponseCode(404, "Environment not found") |
| 55 | + raise EnvironmentNotFound("Environment not found.") |
| 56 | |
| 57 | env_config = EnvironmentsConfig() |
| 58 | |
| 59 | @@ -82,19 +83,18 @@ |
| 60 | |
| 61 | # continue when we will have a dns_name |
| 62 | dns_name = machine.dns_name |
| 63 | + |
| 64 | LOG.debug("Got dns_name: %s" % dns_name) |
| 65 | |
| 66 | if not dns_name: |
| 67 | request.setResponseCode(503) |
| 68 | raise EnvironmentNotFound("Environment not ready yet.") |
| 69 | |
| 70 | + environment.dns_name = dns_name |
| 71 | + |
| 72 | # get the fingerprint |
| 73 | LOG.debug("add_known_host") |
| 74 | - storage.add_known_host(username, name, dns_name) # #@UndefinedVariable |
| 75 | - |
| 76 | - LOG.debug("add_ssh_config") |
| 77 | - # add ssh config for the new host |
| 78 | - storage.add_ssh_config(username, name, dns_name) # #@UndefinedVariable |
| 79 | + storage.add_known_host(username, name, dns_name) |
| 80 | |
| 81 | returnValue(environment) |
| 82 | |
| 83 | @@ -161,6 +161,7 @@ |
| 84 | return self |
| 85 | else: |
| 86 | deferred = get_environment(request, name, self.username) |
| 87 | + deferred.addErrback(self._errorRender, request) |
| 88 | environment = Environment(deferred, name, self.username) |
| 89 | return environment |
| 90 | |
| 91 | @@ -186,20 +187,13 @@ |
| 92 | if not hasattr(conf, 'admin-secret'): |
| 93 | conf['admin-secret'] = uuid.uuid4().hex |
| 94 | |
| 95 | - # upload/create ssh private/public key |
| 96 | - private_key_path = storage.add_sshkey( # @UndefinedVariable |
| 97 | - self.username, conf['name']) |
| 98 | - |
| 99 | - LOG.debug('Setting private_key_path to : %s' % \ |
| 100 | - private_key_path + '.pub') |
| 101 | - conf['authorized-keys-path'] = private_key_path + '.pub' |
| 102 | + if 'authorized-keys' in conf.keys(): |
| 103 | + conf['authorized-keys'] += '\n %s' % (open('/var/lib/jrapi/.ssh/id_rsa.pub').read()) |
| 104 | + else: |
| 105 | + conf['authorized-keys'] = open('/var/lib/jrapi/.ssh/id_rsa.pub').read() |
| 106 | |
| 107 | env_conf_yaml = yaml.safe_dump({'environments': {conf['name']: conf}}) |
| 108 | |
| 109 | - LOG.debug("Add environment: %s" % env_conf_yaml) |
| 110 | - storage.add_environment(self.username, # #@UndefinedVariable |
| 111 | - conf['name'], env_conf_yaml) |
| 112 | - |
| 113 | env_config = EnvironmentsConfig() |
| 114 | |
| 115 | LOG.debug("Configuring the environment") |
| 116 | @@ -217,6 +211,9 @@ |
| 117 | LOG.debug("Launch the actual bootstrap") |
| 118 | yield provider.bootstrap(my_constraints) |
| 119 | |
| 120 | + LOG.debug("Save the environment: %s" % env_conf_yaml) |
| 121 | + storage.add_environment(self.username, conf['name'], env_conf_yaml) |
| 122 | + |
| 123 | request.setResponseCode(201) |
| 124 | |
| 125 | |
| 126 | @@ -255,9 +252,10 @@ |
| 127 | ''' |
| 128 | LOG.debug("Received an environment details request") |
| 129 | |
| 130 | - result = self._get(request) |
| 131 | - |
| 132 | - return jsonify(result) |
| 133 | + self.deferred.addCallback(self._get, request) |
| 134 | + self.deferred.addCallback(self._delayedRender, request) |
| 135 | + self.deferred.addErrback(self._errorRender, request) |
| 136 | + return NOT_DONE_YET |
| 137 | |
| 138 | def render_DELETE(self, request): |
| 139 | ''' |
| 140 | @@ -277,7 +275,7 @@ |
| 141 | LOG.debug('Envrionment getChild with name: "%s"' % name) |
| 142 | return self |
| 143 | |
| 144 | - def _get(self, request): |
| 145 | + def _get(self, environment, request): |
| 146 | ''' |
| 147 | Returns the details of a single environment |
| 148 | :param request: A Twisted request object |
| 149 | @@ -286,9 +284,9 @@ |
| 150 | |
| 151 | try: |
| 152 | env_conf_yaml = storage.get_environment(self.username, self.name) |
| 153 | - except EnvironmentNotFound: |
| 154 | - request.setResponseCode(404) |
| 155 | - return {'error': {'EnvironmentNotFound': "Environment not found"}} |
| 156 | + except IOError: |
| 157 | + request.setResponseCode(404, "Environment not found") |
| 158 | + raise EnvironmentNotFound("Environment not ready yet.") |
| 159 | |
| 160 | env_conf = yaml.load(env_conf_yaml) |
| 161 | |
| 162 | @@ -297,8 +295,6 @@ |
| 163 | env_conf['environments'][self.name]} |
| 164 | single_environment['environment']['name'] = self.name |
| 165 | |
| 166 | - del single_environment['environment']['authorized-keys-path'] |
| 167 | - |
| 168 | request.setResponseCode(200) |
| 169 | return single_environment |
| 170 | |
| 171 | @@ -312,7 +308,6 @@ |
| 172 | provider = environment.get_machine_provider() |
| 173 | yield provider.destroy_environment() |
| 174 | |
| 175 | - storage.delete_environment( # #@UndefinedVariable |
| 176 | - self.username, self.name) |
| 177 | + storage.delete_environment(self.username, environment.name, environment.dns_name) |
| 178 | |
| 179 | request.setResponseCode(204) |
| 180 | |
| 181 | === modified file 'jrapi/storage/dummy.py' |
| 182 | --- jrapi/storage/dummy.py 2012-07-14 14:53:32 +0000 |
| 183 | +++ jrapi/storage/dummy.py 2012-08-26 19:06:19 +0000 |
| 184 | @@ -16,14 +16,11 @@ |
| 185 | # You should have received a copy of the GNU Affero General Public License |
| 186 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 187 | |
| 188 | -from juju import errors |
| 189 | - |
| 190 | DUMMY_ENV = """ |
| 191 | environments: |
| 192 | dummy: |
| 193 | type: dummy |
| 194 | foo: bar |
| 195 | - authorized-keys-path: /should/not/see/id_rsa |
| 196 | """ |
| 197 | |
| 198 | DUMMY_ENV_JSON ="""{ |
| 199 | @@ -44,7 +41,7 @@ |
| 200 | if env_name == 'dummy': |
| 201 | return DUMMY_ENV |
| 202 | else: |
| 203 | - raise errors.EnvironmentNotFound('Environment Not found') |
| 204 | + raise IOError |
| 205 | |
| 206 | def get_environments(self, username): |
| 207 | return [{'name':'sample'}] |
| 208 | |
| 209 | === modified file 'jrapi/storage/filesystem.py' |
| 210 | --- jrapi/storage/filesystem.py 2012-07-24 17:17:20 +0000 |
| 211 | +++ jrapi/storage/filesystem.py 2012-08-26 19:06:19 +0000 |
| 212 | @@ -22,7 +22,6 @@ |
| 213 | import subprocess |
| 214 | |
| 215 | from jrapi.config import CONF |
| 216 | -from jrapi.common import ssh_keygen |
| 217 | from jrapi.openstack.common import cfg |
| 218 | |
| 219 | from jrapi.storage.errors import BadStoreConfiguration |
| 220 | @@ -71,12 +70,12 @@ |
| 221 | # LOG.error(reason) |
| 222 | # raise IOError |
| 223 | |
| 224 | -# try: |
| 225 | -# os.makedirs(env_path) |
| 226 | -# except IOError: |
| 227 | -# reason = _("Unable to create user dir: %s") % user_path |
| 228 | -# LOG.error(reason) |
| 229 | -# raise |
| 230 | + try: |
| 231 | + os.makedirs(env_path) |
| 232 | + except IOError: |
| 233 | + reason = _("Unable to create user dir: %s") % user_path |
| 234 | + LOG.error(reason) |
| 235 | + raise |
| 236 | |
| 237 | |
| 238 | env_conf_file = os.path.join(env_path, "environments.yaml") |
| 239 | @@ -118,15 +117,14 @@ |
| 240 | |
| 241 | return environments |
| 242 | |
| 243 | - |
| 244 | - def delete_environment(self, username, env_name): |
| 245 | + def delete_environment(self, username, env_name, ip): |
| 246 | user_path = self._get_user_path(username) |
| 247 | env_path = os.path.join(user_path, env_name) |
| 248 | |
| 249 | user_known_host_file = os.path.join(env_path, "known_host") |
| 250 | user_identity_file = os.path.join(env_path, "id_rsa") |
| 251 | |
| 252 | - self.remove_ssh_config(username, env_name) |
| 253 | + subprocess.Popen('ssh-keygen -R %s' % ip) |
| 254 | |
| 255 | if os.path.exists(env_path): |
| 256 | try: |
| 257 | @@ -145,45 +143,8 @@ |
| 258 | LOG.error(reason) |
| 259 | raise IOError |
| 260 | |
| 261 | - |
| 262 | - |
| 263 | - def add_sshkey(self, username, env_name): |
| 264 | - user_path = self._get_user_path(username) |
| 265 | - env_path = os.path.join(user_path, env_name) |
| 266 | - key_file = os.path.join(user_path, env_name, "id_rsa") |
| 267 | - |
| 268 | - try: |
| 269 | - os.makedirs(env_path) |
| 270 | - except IOError: |
| 271 | - reason = "Unable to create user dir: %s" % user_path |
| 272 | - LOG.error(reason) |
| 273 | - raise |
| 274 | - |
| 275 | - if not os.path.exists(key_file): |
| 276 | - key_dict = ssh_keygen() |
| 277 | - |
| 278 | - private_key_fp = open("%s/id_rsa" % env_path, "w") |
| 279 | - private_key_fp.write(key_dict['private_key']) |
| 280 | - private_key_fp.close() |
| 281 | - |
| 282 | - public_key_fp = open("%s/id_rsa.pub" % env_path, "w") |
| 283 | - public_key_fp.write(key_dict['public_key']) |
| 284 | - public_key_fp.close() |
| 285 | - |
| 286 | - os.chmod("%s/id_rsa" % env_path, 0600) |
| 287 | - os.chmod("%s/id_rsa.pub" % env_path, 0600) |
| 288 | - |
| 289 | - return key_file |
| 290 | - |
| 291 | - |
| 292 | def add_known_host(self, username, env_name, ip): |
| 293 | - user_path = self._get_user_path(username) |
| 294 | - env_path = os.path.join(user_path, env_name) |
| 295 | - |
| 296 | - known_host_file = os.path.join(env_path, "known_host") |
| 297 | - |
| 298 | - if os.path.exists(known_host_file): |
| 299 | - return |
| 300 | + known_host_file = os.path.join(self.datadir, ".ssh/known_host") |
| 301 | |
| 302 | output = subprocess.check_output( |
| 303 | ["ssh-keyscan", "-H", "-t", "ecdsa-sha2-nistp256", ip], |
| 304 | @@ -195,37 +156,3 @@ |
| 305 | except IOError as e: |
| 306 | raise |
| 307 | |
| 308 | - def add_ssh_config(self, username, env_name, ip): |
| 309 | - user_path = self._get_user_path(username) |
| 310 | - env_path = os.path.join(user_path, env_name) |
| 311 | - |
| 312 | - ssh_config_file = os.path.join(self.datadir, ".ssh/config") |
| 313 | - user_known_host_file = os.path.join(env_path, "known_host") |
| 314 | - user_identity_file = os.path.join(env_path, "id_rsa") |
| 315 | - |
| 316 | - config_block = """Host %s |
| 317 | - UserKnownHostsFile=%s |
| 318 | - IdentityFile=%s |
| 319 | -""" % (ip, user_known_host_file, user_identity_file) |
| 320 | - |
| 321 | - with open(ssh_config_file, "r+") as f: |
| 322 | - old = f.readlines() # read everything in the file |
| 323 | - if "Host %s\n" % ip in old: |
| 324 | - return |
| 325 | - else: |
| 326 | - f.seek(0) # rewind |
| 327 | - f.write(config_block + "".join(old)) |
| 328 | - |
| 329 | - def remove_ssh_config(self, username, env_name): |
| 330 | - user_path = self._get_user_path(username) |
| 331 | - env_path = os.path.join(user_path, env_name) |
| 332 | - |
| 333 | - ssh_config_file = os.path.join(self.datadir, ".ssh/config") |
| 334 | - user_known_host_file = os.path.join(env_path, "known_host") |
| 335 | - user_identity_file = os.path.join(env_path, "id_rsa") |
| 336 | - |
| 337 | - with open(ssh_config_file, "r+") as f: |
| 338 | - old = f.read() |
| 339 | - old = re.sub(r'.* #%s' % env_path, '', old) |
| 340 | - f.seek(0) |
| 341 | - f.write(old) |
| 342 | |
| 343 | === modified file 'tests/test_environment.py' |
| 344 | --- tests/test_environment.py 2012-07-14 14:53:32 +0000 |
| 345 | +++ tests/test_environment.py 2012-08-26 19:06:19 +0000 |
| 346 | @@ -2,7 +2,6 @@ |
| 347 | |
| 348 | from juju.environment.tests.test_config import EnvironmentsConfigTestBase |
| 349 | |
| 350 | -from jrapi.auth import JRAPIAuthSessionWrapper |
| 351 | from jrapi.apiroot import APIRoot |
| 352 | from jrapi.storage.dummy import DUMMY_ENV, DUMMY_ENV_JSON |
| 353 | |
| 354 | @@ -14,16 +13,11 @@ |
| 355 | @inlineCallbacks |
| 356 | def setUp(self): |
| 357 | yield super(EnvironmentTest, self).setUp() |
| 358 | - self.web = DummySite(JRAPIAuthSessionWrapper(APIRoot())) |
| 359 | + self.web = DummySite(APIRoot()) |
| 360 | self.write_config(DUMMY_ENV) |
| 361 | self.config.load() |
| 362 | |
| 363 | @inlineCallbacks |
| 364 | - def test_bootstrap(self): |
| 365 | - response = yield self.web.put("/environments/", content=DUMMY_ENV_JSON) |
| 366 | - self.assertEqual(response.responseCode, 201), |
| 367 | - |
| 368 | - @inlineCallbacks |
| 369 | def test_list(self): |
| 370 | response = yield self.web.get("/environments/") |
| 371 | self.assertEqual(response.responseCode, 200), |
| 372 | @@ -45,23 +39,8 @@ |
| 373 | def test_get_not_existing(self): |
| 374 | response = yield self.web.get("/environments/not_existing/") |
| 375 | self.assertEqual(response.responseCode, 404), |
| 376 | - self.assertEqual(response.value(), """{ |
| 377 | - "error": { |
| 378 | - "EnvironmentNotFound": "Environment not found" |
| 379 | - } |
| 380 | -}""") |
| 381 | - |
| 382 | -# @inlineCallbacks |
| 383 | -# def test_zdestroy(self): |
| 384 | -# response = yield self.web.delete("/environments/dummy/") |
| 385 | -# self.assertEqual(response.responseCode, 204), |
| 386 | - |
| 387 | -# @inlineCallbacks |
| 388 | -# def test_zdestroy_not_existing(self): |
| 389 | -# response = yield self.web.delete("/environments/not_existing/") |
| 390 | -# self.assertEqual(response.responseCode, 404), |
| 391 | -# self.assertEqual(response.value(), """{ |
| 392 | -# "error": { |
| 393 | -# "EnvironmentNotFound": "juju environment not found: Environment ready yet." |
| 394 | -# } |
| 395 | -#}""") |
| 396 | + |
| 397 | + @inlineCallbacks |
| 398 | + def test_zdestroy_not_existing(self): |
| 399 | + response = yield self.web.delete("/environments/not_existing/") |
| 400 | + self.assertEqual(response.responseCode, 404), |