Hello community,

here is the log from the commit of package google-daemon for openSUSE:Factory 
checked in at 2016-02-05 00:32:12
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/google-daemon (Old)
 and      /work/SRC/openSUSE:Factory/.google-daemon.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "google-daemon"

Changes:
--------
--- /work/SRC/openSUSE:Factory/google-daemon/google-daemon.changes      
2015-12-03 13:31:58.000000000 +0100
+++ /work/SRC/openSUSE:Factory/.google-daemon.new/google-daemon.changes 
2016-02-05 00:32:18.000000000 +0100
@@ -1,0 +2,9 @@
+Thu Jan 28 00:03:50 UTC 2016 - rjsch...@suse.com
+
+- Update to version 1.3.1 (bsc#963879,bsc#963880)
+  + Refactored accounts daemon.
+  + Support "ssh-keys" in project metadata in addition to "sshKeys".
+  + Support instance "override-ssh-keys" in addition to "sshKeys"
+  + Support "additional-ssh-keys" in instance metadata.
+
+-------------------------------------------------------------------

Old:
----
  google-daemon-1.2.10.tar.bz2

New:
----
  google-daemon-1.3.1.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ google-daemon.spec ++++++
--- /var/tmp/diff_new_pack.hmMFjT/_old  2016-02-05 00:32:19.000000000 +0100
+++ /var/tmp/diff_new_pack.hmMFjT/_new  2016-02-05 00:32:19.000000000 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package google-daemon
 #
-# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,7 +17,7 @@
 
 
 Name:           google-daemon
-Version:        1.2.10
+Version:        1.3.1
 Release:        0
 Summary:        VM management inside GCE
 License:        Apache-2.0

++++++ google-daemon-1.2.10.tar.bz2 -> google-daemon-1.3.1.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/google-daemon-1.2.10/usr/share/google/google_daemon/accounts_manager.py 
new/google-daemon-1.3.1/usr/share/google/google_daemon/accounts_manager.py
--- old/google-daemon-1.2.10/usr/share/google/google_daemon/accounts_manager.py 
2015-09-24 21:47:38.000000000 +0200
+++ new/google-daemon-1.3.1/usr/share/google/google_daemon/accounts_manager.py  
2016-01-20 19:41:29.000000000 +0100
@@ -14,7 +14,6 @@
 
 """Main driver logic for managing accounts on GCE instances."""
 
-import json
 import logging
 import os
 import pwd
@@ -55,35 +54,30 @@
       reader, writer = os.pipe() # these are file descriptors, not file objects
       pid = os.fork()
       if pid:
-        # we are the parent
+        # We are the parent.
         os.close(writer)
-        reader = os.fdopen(reader) # turn r into a file object
-        json_tags = reader.read()
-        if json_tags:
-          etags = json.loads(json_tags)
-          if etags:
-            self.desired_accounts.attributes_etag = etags[0]
-            self.desired_accounts.instance_sshkeys_etag = etags[1]
+        reader = os.fdopen(reader) # turn reader into a file object
+        etag = reader.read()
+        if etag:
+          self.desired_accounts.etag = etag
         reader.close()
-        logging.debug('New etag: %s', self.desired_accounts.attributes_etag)
+        logging.debug('New etag: %s', self.desired_accounts.etag)
         os.waitpid(pid, 0)
       else:
-        # we are the child
+        # We are the child.
         os.close(reader)
         writer = os.fdopen(writer, 'w')
         try:
           self.RegenerateKeysAndUpdateAccounts()
         except Exception as e:
           logging.warning('error while trying to update accounts: %s', e)
-          # An error happened while trying to update the accounts. Lets sleep a
-          # bit to avoid getting stuck in a loop for intermittent errors.
+          # An error happened while trying to update the accounts.
+          # Sleep for five seconds before trying again.
           time.sleep(5)
 
-        # Write the etag to pass to parent
-        json_tags = json.dumps(
-            [self.desired_accounts.attributes_etag,
-             self.desired_accounts.instance_sshkeys_etag])
-        writer.write(json_tags)
+        # Write the etag to pass to parent.
+        etag = self.desired_accounts.etag or ''
+        writer.write(etag)
         writer.close()
 
         # The use of os._exit here is recommended for subprocesses spawned
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/google-daemon-1.2.10/usr/share/google/google_daemon/desired_accounts.py 
new/google-daemon-1.3.1/usr/share/google/google_daemon/desired_accounts.py
--- old/google-daemon-1.2.10/usr/share/google/google_daemon/desired_accounts.py 
2015-09-24 21:47:38.000000000 +0200
+++ new/google-daemon-1.3.1/usr/share/google/google_daemon/desired_accounts.py  
2016-01-20 19:41:29.000000000 +0100
@@ -22,17 +22,13 @@
 import urllib2
 
 
-METADATA_V1_URL_PREFIX = 'http://169.254.169.254/computeMetadata/v1/'
-ATTRIBUTES_URL = METADATA_V1_URL_PREFIX + '?recursive=true&%s'
-INSTANCE_SSHKEYS_URL = (
-    METADATA_V1_URL_PREFIX + 'instance/attributes/sshKeys?%s')
-PROJECT_SSHKEYS_URL = (
-    METADATA_V1_URL_PREFIX + 'project/attributes/sshKeys?%s')
-WAIT_FOR_CHANGE = 'wait_for_change=true&last_etag=%s&timeout_sec=%s'
+METADATA_URL = 'http://metadata.google.internal/computeMetadata/v1'
+METADATA_HANG = ('/?recursive=true&alt=json&wait_for_change=true'
+                 '&timeout_sec=%s&last_etag=%s')
 
 
 def KeyHasExpired(key):
-  """ Check to see whether an SSH key has expired.
+  """Check to see whether an SSH key has expired.
 
   Uses Google-specific (for now) semantics of the OpenSSH public key format's
   comment field to determine if an SSH key is past its expiration timestamp, 
and
@@ -46,7 +42,8 @@
 
   Returns:
     True if the key has Google-specific comment semantics and has an expiration
-    timestamp in the past, or False otherwise."""
+    timestamp in the past, or False otherwise.
+  """
 
   logging.debug('Processing key: %s', key)
 
@@ -77,26 +74,25 @@
     return False
 
   expire_str = json_obj['expireOn']
+  format_str = '%Y-%m-%dT%H:%M:%S+0000'
 
   try:
-    expire_time = datetime.datetime.strptime(expire_str,
-                                             '%Y-%m-%dT%H:%M:%S+0000')
+    expire_time = datetime.datetime.strptime(expire_str, format_str)
   except ValueError:
     logging.error(
-        'Expiration timestamp "%s" not in format %Y-%m-%dT%H:%M:%S+0000.',
-        expire_str)
+        'Expiration timestamp "%s" not in format %s.', expire_str, format_str)
     logging.error('Not expiring key.')
     return False
 
   # Expire the key if and only if we have exceeded the expiration timestamp.
-  return (datetime.datetime.utcnow() > expire_time)
+  return datetime.datetime.utcnow() > expire_time
 
 
 def AccountDataToDictionary(data):
-  """Given sshKeys attribute data, construct a usermap.
+  """Given SSH key data, construct a usermap.
 
   Args:
-    data: The data returned from the metadata server's sshKeys attribute.
+    data: The data returned from the metadata server's SSH key attributes.
 
   Returns:
     A map of {'username': ssh_keys_list}.
@@ -109,17 +105,17 @@
     split_line = line.split(':', 1)
     if len(split_line) != 2:
       logging.warning(
-          'sshKey is not a complete entry: %s', split_line)
+          'SSH key is not a complete entry: %s', split_line)
       continue
     user, key = split_line
     if KeyHasExpired(key):
       logging.debug(
           'Skipping expired SSH key for user %s: %s', user, key)
       continue
-    if not user in usermap:
+    if user not in usermap:
       usermap[user] = []
     usermap[user].append(key)
-  logging.debug('User accounts: {0}'.format(usermap))
+  logging.debug('User accounts: %s', usermap)
   return usermap
 
 
@@ -129,48 +125,36 @@
   def __init__(self, time_module=time, urllib2_module=urllib2):
     self.urllib2 = urllib2_module
     self.time = time_module
-    self.attributes_etag = 0
-    self.instance_sshkeys_etag = 0
+    self.etag = 0
 
-  def _MakeHangingGetRequest(self, url, etag, timeout_secs):
-    """Makes a get request for the url and specifies wait_for_change.
-    """
-    wait_for_change_query = WAIT_FOR_CHANGE % (etag, timeout_secs)
-    request_url = url % wait_for_change_query
+  def _WaitForUpdate(self, timeout_secs):
+    """Makes a hanging get request for the contents of the metadata server."""
+    request_url = METADATA_URL + METADATA_HANG % (timeout_secs, self.etag)
     logging.debug('Getting url: %s', request_url)
     request = urllib2.Request(request_url)
     request.add_header('Metadata-Flavor', 'Google')
     return self.urllib2.urlopen(request, timeout=timeout_secs*1.1)
 
-  def _GetAttribute(self,
-                   attribute_url,
-                   etag=0,
-                   timeout_secs=60):
-    """Fetches the attribute available at the attribute_url.
+  def _GetMetadataUpdate(self, timeout_secs=60):
+    """Fetches the content of the metadata server.
 
     Args:
-      attribute_url: The url to fetch. It must have a place holder where the
-          query with etag, and timeout can be specified to allow hanging gets.
-      etag: The etag to use when making the query. Don't specify if you want
-          the get to return immediately.
       timeout_secs: The timeout in seconds.
 
     Returns:
-      Tuple containing the string value of attribute and new etag.
-      If attribute doesn't exist, None.
+      The JSON formatted string content of the metadata server.
     """
     try:
-      response = self._MakeHangingGetRequest(
-          attribute_url, etag=etag, timeout_secs=timeout_secs)
+      response = self._WaitForUpdate(timeout_secs=timeout_secs)
       response_info = response.info()
       if response_info and response_info.has_key('etag'):
-        etag = response_info.getheader('etag')
-      attribute_value = response.read()
-      logging.debug('response: %s', attribute_value)
-      return (attribute_value, etag)
+        self.etag = response_info.getheader('etag')
+      content = response.read()
+      logging.debug('response: %s', content)
+      return content
     except urllib2.HTTPError as e:
       if e.code == 404:
-        # The attribute doesn't exist. Return None.
+        # The metadata server content doesn't exist. Return None.
         # No need to log a warning.
         return None
       # rethrow the exception since we don't know what it is. Let the
@@ -186,51 +170,25 @@
     """
     logging.debug('Getting desired accounts from metadata.')
     # Fetch the top level attribute with a hanging get
-    attribute_data = self._GetAttribute(
-        ATTRIBUTES_URL,
-        etag=self.attributes_etag)
-    if attribute_data:
-      # Store the project level attributes etag value. If
-      # we are able to successfully fetch the attributes we will
-      # update the class member with this value.
-      attributes_etag_cache = attribute_data[1]
-
-    # Something has changed. Assume it is the sshKeys. This is not
-    # ideal, however, given that metadata updates are not common
-    # making this assumption simplifies the code complexity.
-    #
-    # sshKeys attribute can exist in either the instance attributes
-    # collection or the project attributes collection. If it is present
-    # in the instance attributes collection, then that value is used
-    # and the project level value is ignored.
-    # Check if instance attributes collection has sshKeys attribute.
-    # We can run this call as a hanging get since if the instance
-    # level attribute exists we can ignore any changes to the project
-    # level key.
-    attribute_data = self._GetAttribute(
-        INSTANCE_SSHKEYS_URL,
-        etag = self.instance_sshkeys_etag)
-    if attribute_data:
-      logging.debug('Found instance sshKeys attribute.')
-      # There is an sshKeys attribute on the instance. Use it
-      account_data = attribute_data[0]
-      self.instance_sshkeys_etag = attribute_data[1]
-    else:
-      # Fetch the sshKeys attribute from project collection. We cannot
-      # use a hanging get here since it is possible this call may take
-      # a long time and during that the instance metadata can change which
-      # we will miss.
-      logging.debug(
-          'Instance sshKeys attribute not found. Falling back to project')
-      attribute_data = self._GetAttribute(PROJECT_SSHKEYS_URL)
-      if attribute_data:
-        logging.debug('Project sshKeys attribute found.')
-        # There is an sshKeys attribute. Use it
-        account_data = attribute_data[0]
+    metadata_content = self._GetMetadataUpdate()
+    metadata_dict = json.loads(metadata_content or '{}')
+    account_data = None
+
+    try:
+      instance_data = metadata_dict['instance']['attributes']
+      project_data = metadata_dict['project']['attributes']
+      # Instance SSH keys that will override keys in project metadata.
+      instance_override = instance_data.get('override-ssh-keys')
+      instance_ssh = instance_data.get('sshKeys')
+      if instance_override or instance_ssh:
+        valid_keys = [instance_override, instance_ssh]
       else:
-        logging.debug('Project sshKeys attribute not found.')
-        # sshKeys doesn't exist for either project or instance.
-        account_data = None
+        valid_keys = [project_data.get('ssh-keys'), 
project_data.get('sshKeys')]
+      # Additional SSH keys the instance should accept.
+      valid_keys.append(instance_data.get('additional-ssh-keys'))
+      valid_keys = [key for key in valid_keys if key]
+      account_data = '\n'.join(valid_keys)
+    except KeyError:
+      logging.debug('Project or instance attributes were not found.')
 
-    self.attributes_etag = attributes_etag_cache
     return AccountDataToDictionary(account_data)


Reply via email to