|
@@ -1,6 +1,3 @@
|
|
|
-from collections import namedtuple
|
|
|
|
|
-import hashlib
|
|
|
|
|
-
|
|
|
|
|
# based on http://stackoverflow.com/a/39126754
|
|
# based on http://stackoverflow.com/a/39126754
|
|
|
from cryptography.hazmat.backends import default_backend
|
|
from cryptography.hazmat.backends import default_backend
|
|
|
from cryptography.hazmat.primitives import serialization as crypt_serialization
|
|
from cryptography.hazmat.primitives import serialization as crypt_serialization
|
|
@@ -9,9 +6,6 @@ from cryptography.hazmat.primitives.asymmetric import rsa
|
|
|
from retrying import retry
|
|
from retrying import retry
|
|
|
|
|
|
|
|
|
|
|
|
|
-GCEKeyInfo = namedtuple('GCEKeyInfo', 'format public_key email')
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
def gce_projects(provider):
|
|
def gce_projects(provider):
|
|
|
return provider.gce_compute.projects()
|
|
return provider.gce_compute.projects()
|
|
|
|
|
|
|
@@ -42,17 +36,7 @@ def iter_all(resource, **kwargs):
|
|
|
token = response['nextPageToken']
|
|
token = response['nextPageToken']
|
|
|
|
|
|
|
|
|
|
|
|
|
-def _iter_gce_key_pairs(provider):
|
|
|
|
|
- """
|
|
|
|
|
- Iterates through the project's metadata, yielding a GCEKeyInfo object
|
|
|
|
|
- for each entry in commonInstanceMetaData/items
|
|
|
|
|
- """
|
|
|
|
|
- metadata = _get_common_metadata(provider)
|
|
|
|
|
- for kpinfo in _iter_gce_ssh_keys(metadata):
|
|
|
|
|
- yield kpinfo
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-def _get_common_metadata(provider):
|
|
|
|
|
|
|
+def get_common_metadata(provider):
|
|
|
"""
|
|
"""
|
|
|
Get a project's commonInstanceMetadata entry
|
|
Get a project's commonInstanceMetadata entry
|
|
|
"""
|
|
"""
|
|
@@ -61,40 +45,6 @@ def _get_common_metadata(provider):
|
|
|
return metadata["commonInstanceMetadata"]
|
|
return metadata["commonInstanceMetadata"]
|
|
|
|
|
|
|
|
|
|
|
|
|
-def _get_or_add_sshkey_entry(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(metadata):
|
|
|
|
|
- """
|
|
|
|
|
- Iterates through the ssh keys given a commonInstanceMetadata dict,
|
|
|
|
|
- yielding a GCEKeyInfo object for each entry in
|
|
|
|
|
- commonInstanceMetaData/items
|
|
|
|
|
- """
|
|
|
|
|
- sshkeys = _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 GCEKeyInfo(
|
|
|
|
|
- elems[0], elems[1].encode('ascii'), elems[2])
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
def gce_metadata_save_op(provider, callback):
|
|
def gce_metadata_save_op(provider, callback):
|
|
|
"""
|
|
"""
|
|
|
Carries out a metadata save operation. In GCE, a fingerprint based
|
|
Carries out a metadata save operation. In GCE, a fingerprint based
|
|
@@ -105,18 +55,11 @@ def gce_metadata_save_op(provider, callback):
|
|
|
immediately afterwards, ensuring that update conflicts can be detected.
|
|
immediately afterwards, ensuring that update conflicts can be detected.
|
|
|
"""
|
|
"""
|
|
|
def _save_common_metadata(provider):
|
|
def _save_common_metadata(provider):
|
|
|
- metadata = _get_common_metadata(provider)
|
|
|
|
|
- # add a new entry if one doesn't exist
|
|
|
|
|
- sshkey_entry = _get_or_add_sshkey_entry(metadata)
|
|
|
|
|
- gce_kp_list = callback(_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()
|
|
|
|
|
- # common_metadata will have the current fingerprint at this point
|
|
|
|
|
|
|
+ # get the latest metadata (so we get the latest fingerprint)
|
|
|
|
|
+ metadata = get_common_metadata(provider)
|
|
|
|
|
+ # allow callback to do processing on it
|
|
|
|
|
+ callback(metadata)
|
|
|
|
|
+ # save the metadata
|
|
|
operation = gce_projects(provider).setCommonInstanceMetadata(
|
|
operation = gce_projects(provider).setCommonInstanceMetadata(
|
|
|
project=provider.project_name, body=metadata).execute()
|
|
project=provider.project_name, body=metadata).execute()
|
|
|
provider.wait_for_operation(operation)
|
|
provider.wait_for_operation(operation)
|
|
@@ -126,35 +69,24 @@ def gce_metadata_save_op(provider, callback):
|
|
|
retry_decorator(_save_common_metadata)(provider)
|
|
retry_decorator(_save_common_metadata)(provider)
|
|
|
|
|
|
|
|
|
|
|
|
|
-def gce_kp_to_id(gce_kp):
|
|
|
|
|
- """
|
|
|
|
|
- Accept a GCEKeyInfo object and return a unique
|
|
|
|
|
- ID for it
|
|
|
|
|
- """
|
|
|
|
|
- md5 = hashlib.md5()
|
|
|
|
|
- md5.update(gce_kp.public_key)
|
|
|
|
|
- return md5.hexdigest()
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
def modify_or_add_metadata_item(provider, key, value):
|
|
def modify_or_add_metadata_item(provider, key, value):
|
|
|
- metadata = _get_common_metadata(provider)
|
|
|
|
|
- entries = [item for item in metadata.get('items', [])
|
|
|
|
|
- if item['key'] == key]
|
|
|
|
|
- if entries:
|
|
|
|
|
- entries[-1]['value'] = value
|
|
|
|
|
- else:
|
|
|
|
|
- entry = {'key': key, 'value': value}
|
|
|
|
|
- if 'items' not in metadata:
|
|
|
|
|
- metadata['items'] = [entry]
|
|
|
|
|
|
|
+ def _update_metadata_key(metadata):
|
|
|
|
|
+ entries = [item for item in metadata.get('items', [])
|
|
|
|
|
+ if item['key'] == key]
|
|
|
|
|
+ if entries:
|
|
|
|
|
+ entries[-1]['value'] = value
|
|
|
else:
|
|
else:
|
|
|
- metadata['items'].append(entry)
|
|
|
|
|
- operation = gce_projects(provider).setCommonInstanceMetadata(
|
|
|
|
|
- project=provider.project_name, body=metadata).execute()
|
|
|
|
|
- provider.wait_for_operation(operation)
|
|
|
|
|
|
|
+ entry = {'key': key, 'value': value}
|
|
|
|
|
+ if 'items' not in metadata:
|
|
|
|
|
+ metadata['items'] = [entry]
|
|
|
|
|
+ else:
|
|
|
|
|
+ metadata['items'].append(entry)
|
|
|
|
|
+
|
|
|
|
|
+ gce_metadata_save_op(provider, _update_metadata_key)
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_metadata_item_value(provider, key):
|
|
def get_metadata_item_value(provider, key):
|
|
|
- metadata = _get_common_metadata(provider)
|
|
|
|
|
|
|
+ metadata = get_common_metadata(provider)
|
|
|
entries = [item['value'] for item in metadata.get('items', [])
|
|
entries = [item['value'] for item in metadata.get('items', [])
|
|
|
if item['key'] == key]
|
|
if item['key'] == key]
|
|
|
if entries:
|
|
if entries:
|