Просмотр исходного кода

GCE KeyPair using general metadata

almahmoud 7 лет назад
Родитель
Сommit
398a3dbd67

+ 0 - 20
cloudbridge/cloud/providers/gce/helpers.py

@@ -1,11 +1,6 @@
 import fnmatch
 import re
 
-# based on http://stackoverflow.com/a/39126754
-from cryptography.hazmat.backends import default_backend
-from cryptography.hazmat.primitives import serialization as crypt_serialization
-from cryptography.hazmat.primitives.asymmetric import rsa
-
 from googleapiclient.errors import HttpError
 
 import tenacity
@@ -17,21 +12,6 @@ def gce_projects(provider):
     return provider.gce_compute.projects()
 
 
-def generate_key_pair():
-    key_pair = rsa.generate_private_key(
-        backend=default_backend(),
-        public_exponent=65537,
-        key_size=2048)
-    private_key = key_pair.private_bytes(
-        crypt_serialization.Encoding.PEM,
-        crypt_serialization.PrivateFormat.PKCS8,
-        crypt_serialization.NoEncryption())
-    public_key = key_pair.public_key().public_bytes(
-        crypt_serialization.Encoding.OpenSSH,
-        crypt_serialization.PublicFormat.OpenSSH)
-    return private_key.decode(), public_key.decode()
-
-
 def iter_all(resource, **kwargs):
     token = None
     while True:

+ 9 - 5
cloudbridge/cloud/providers/gce/resources.py

@@ -9,6 +9,7 @@ import io
 import math
 import time
 import uuid
+from collections import namedtuple
 
 import googleapiclient
 
@@ -59,19 +60,22 @@ except NameError:
 
 class GCEKeyPair(BaseKeyPair):
 
-    def __init__(self, provider, kp_id, kp_name, private_key=None):
+    tag_format = "CB_{}_KEY_PAIR"
+    GCEKeyInfo = namedtuple('GCEKeyInfo', 'name public_key')
+
+    def __init__(self, provider, kp_info, private_key=None):
         super(GCEKeyPair, self).__init__(provider, None)
-        self._kp_id = kp_id
-        self._kp_name = kp_name
+        # "GCEKeyPair." needed to properly evaluate the named tuple
+        self._key_pair = eval("GCEKeyPair." + kp_info)
         self._private_key = private_key
 
     @property
     def id(self):
-        return self._kp_id
+        return self._key_pair.name
 
     @property
     def name(self):
-        return self._kp_name
+        return self._key_pair.name
 
     def delete(self):
         self._provider.security.key_pairs.delete(self.id)

+ 14 - 90
cloudbridge/cloud/providers/gce/services.py

@@ -1,8 +1,6 @@
-import hashlib
 import logging
 import time
 import uuid
-from collections import namedtuple
 
 import googleapiclient
 
@@ -70,76 +68,9 @@ class GCESecurityService(BaseSecurityService):
 
 class GCEKeyPairService(BaseKeyPairService):
 
-    GCEKeyInfo = namedtuple('GCEKeyInfo', 'format public_key email')
-
     def __init__(self, provider):
         super(GCEKeyPairService, self).__init__(provider)
 
-    def _iter_gce_key_pairs(self, provider):
-        """
-        Iterates through the project's metadata, yielding a GCEKeyInfo object
-        for each entry in commonInstanceMetaData/items
-        """
-        metadata = helpers.get_common_metadata(provider)
-        for kpinfo in self._iter_gce_ssh_keys(metadata):
-            yield kpinfo
-
-    def _get_or_add_sshkey_entry(self, metadata):
-        """
-        Get the ssh-keys entry from commonInstanceMetadata/items.
-        If an entry does not exist, adds a new empty entry
-        """
-        sshkey_entry = None
-        entries = [item for item in metadata.get('items', [])
-                   if item['key'] == 'ssh-keys']
-        if entries:
-            sshkey_entry = entries[0]
-        else:  # add a new entry
-            sshkey_entry = {'key': 'ssh-keys', 'value': ''}
-            if 'items' not in metadata:
-                metadata['items'] = [sshkey_entry]
-            else:
-                metadata['items'].append(sshkey_entry)
-        return sshkey_entry
-
-    def _iter_gce_ssh_keys(self, metadata):
-        """
-        Iterates through the ssh keys given a commonInstanceMetadata dict,
-        yielding a GCEKeyInfo object for each entry in
-        commonInstanceMetaData/items
-        """
-        sshkeys = self._get_or_add_sshkey_entry(metadata)["value"]
-        for key in sshkeys.split("\n"):
-            # elems should be "ssh-rsa <public_key> <email>"
-            elems = key.split(" ")
-            if elems and elems[0]:  # ignore blank lines
-                yield GCEKeyPairService.GCEKeyInfo(
-                        elems[0], elems[1], elems[2])
-
-    def update_kps_in_metadata(self, provider, callback):
-        def _process_kps_from_metadata(metadata):
-            # add a new entry if one doesn't exist
-            sshkey_entry = self._get_or_add_sshkey_entry(metadata)
-            gce_kp_list = callback(self._iter_gce_ssh_keys(metadata))
-
-            entry = ""
-            for gce_kp in gce_kp_list:
-                entry = entry + u"{0} {1} {2}\n".format(gce_kp.format,
-                                                        gce_kp.public_key,
-                                                        gce_kp.email)
-            sshkey_entry["value"] = entry.rstrip()
-
-        helpers.gce_metadata_save_op(provider, _process_kps_from_metadata)
-
-    def gce_kp_to_id(self, gce_kp):
-        """
-        Accept a GCEKeyInfo object and return a unique
-        ID for it
-        """
-        md5 = hashlib.md5()
-        md5.update(" ".join([gce_kp.public_key, gce_kp.email]).encode())
-        return md5.hexdigest()
-
     def get(self, key_pair_id):
         """
         Returns a KeyPair given its ID.
@@ -151,10 +82,11 @@ class GCEKeyPairService(BaseKeyPairService):
             return None
 
     def list(self, limit=None, marker=None):
+        key_regex = GCEKeyPair.tag_format.replace("{}", "*")
         key_pairs = []
-        for gce_kp in self._iter_gce_key_pairs(self.provider):
-            kp_id = self.gce_kp_to_id(gce_kp)
-            key_pairs.append(GCEKeyPair(self.provider, kp_id, gce_kp.email))
+        for item in helpers.find_all_metadata_items(self.provider, key_regex):
+            kp_info = item['value']
+            key_pairs.append(GCEKeyPair(self.provider, kp_info))
         return ClientPagedResultList(self.provider, key_pairs,
                                      limit=limit, marker=marker)
 
@@ -184,24 +116,16 @@ class GCEKeyPairService(BaseKeyPairService):
 
         private_key = None
         if not public_key_material:
-            private_key, public_key_material = helpers.generate_key_pair()
-        parts = public_key_material.split(' ')
-        if len(parts) == 2:
-            public_key_material = parts[1]
-        kp_info = GCEKeyPairService.GCEKeyInfo(
-            '%s:ssh-rsa' % name, public_key_material, name)
-
-        def _add_kp(gce_kp_generator):
-            kp_list = []
-            # Add the new key pair
-            kp_list.append(kp_info)
-            for gce_kp in gce_kp_generator:
-                kp_list.append(gce_kp)
-            return kp_list
-
-        self.update_kps_in_metadata(self.provider, _add_kp)
-        return GCEKeyPair(self.provider, self.gce_kp_to_id(kp_info), name,
-                          private_key)
+            public_key_material, private_key = cb_helpers.generate_key_pair()
+        elif "ssh-rsa" not in public_key_material:
+            public_key_material = "ssh-rsa {}".format(public_key_material)
+        kp_info = str(GCEKeyPair.GCEKeyInfo(name, public_key_material))
+
+        metadata_key = GCEKeyPair.tag_format.format(name)
+        helpers.add_metadata_item(self.provider,
+                                  metadata_key,
+                                  kp_info)
+        return GCEKeyPair(self.provider, kp_info, private_key)
 
     def delete(self, key_pair_id):