← Back to team overview

cloud-init-dev team mailing list archive

[Merge] lp:~wwitzel3/cloud-init/gce into lp:cloud-init

 

Wayne Witzel III has proposed merging lp:~wwitzel3/cloud-init/gce into lp:cloud-init.

Requested reviews:
  cloud init development team (cloud-init-dev)
Related bugs:
  Bug #1404311 in cloud-init: "gce metadata api doesn't properly stream binary data"
  https://bugs.launchpad.net/cloud-init/+bug/1404311

For more details, see:
https://code.launchpad.net/~wwitzel3/cloud-init/gce/+merge/245209

Enable user-data encoding support for GCE.
-- 
Your team cloud init development team is requested to review the proposed merge of lp:~wwitzel3/cloud-init/gce into lp:cloud-init.
=== modified file 'cloudinit/sources/DataSourceGCE.py'
--- cloudinit/sources/DataSourceGCE.py	2014-02-14 19:29:02 +0000
+++ cloudinit/sources/DataSourceGCE.py	2014-12-19 16:38:22 +0000
@@ -15,6 +15,8 @@
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 
+from base64 import b64decode
+
 from cloudinit import log as logging
 from cloudinit import util
 from cloudinit import sources
@@ -58,6 +60,7 @@
             ('local-hostname', 'instance/hostname', True),
             ('public-keys', 'project/attributes/sshKeys', False),
             ('user-data', 'instance/attributes/user-data', False),
+            ('user-data-encoding', 'instance/attributes/user-data-encoding', False),
         ]
 
         # if we cannot resolve the metadata server, then no point in trying
@@ -101,6 +104,12 @@
             lines = self.metadata['public-keys'].splitlines()
             self.metadata['public-keys'] = [self._trim_key(k) for k in lines]
 
+        if self.metadata.get('user-data-encoding'):
+            if self.metadata['user-data-encoding'] == 'base64':
+                self.metadata['user-data'] = b64decode(self.metadata['user-data'])
+            else:
+                LOG.warn('user-data-encoding: unknown encoding specified', None, None)
+
         return found
 
     @property

=== modified file 'tests/unittests/test_datasource/test_gce.py'
--- tests/unittests/test_datasource/test_gce.py	2014-07-24 12:49:42 +0000
+++ tests/unittests/test_datasource/test_gce.py	2014-12-19 16:38:22 +0000
@@ -18,6 +18,7 @@
 import httpretty
 import re
 
+from base64 import b64encode, b64decode
 from urlparse import urlparse
 
 from cloudinit import settings
@@ -30,29 +31,45 @@
     'instance/id': '123',
     'instance/zone': 'foo/bar',
     'project/attributes/sshKeys': 'user:ssh-rsa AA2..+aRD0fyVw== root@server',
-    'instance/hostname': 'server.project-name.local',
+    'instance/hostname': 'server.project-foo.local',
     'instance/attributes/user-data': '/bin/echo foo\n',
+    'instance/attributes/user-data-encoding':'',
 }
 
 GCE_META_PARTIAL = {
-    'instance/id': '123',
-    'instance/hostname': 'server.project-name.local',
+    'instance/id': '1234',
+    'instance/hostname': 'server.project-bar.local',
+    'instance/zone': 'bar/baz',
+}
+
+GCE_META_ENCODING = {
+    'instance/id': '12345',
+    'instance/hostname': 'server.project-baz.local',
+    'instance/zone': 'baz/bang',
+    'instance/attributes/user-data': b64encode('/bin/echo baz\n'),
+    'instance/attributes/user-data-encoding': 'base64',
 }
 
 HEADERS = {'X-Google-Metadata-Request': 'True'}
 MD_URL_RE = re.compile(r'http://metadata.google.internal./computeMetadata/v1/.*')
 
 
-def _request_callback(method, uri, headers):
-    url_path = urlparse(uri).path
-    if url_path.startswith('/computeMetadata/v1/'):
-        path = url_path.split('/computeMetadata/v1/')[1:][0]
-    else:
-        path = None
-    if path in GCE_META:
-        return (200, headers, GCE_META.get(path))
-    else:
-        return (404, headers, '')
+def _new_request_callback(gce_meta=None):
+    if not gce_meta:
+        gce_meta = GCE_META
+
+    def _request_callback(method, uri, headers):
+        url_path = urlparse(uri).path
+        if url_path.startswith('/computeMetadata/v1/'):
+            path = url_path.split('/computeMetadata/v1/')[1:][0]
+        else:
+            path = None
+        if path in gce_meta:
+            return (200, headers, gce_meta.get(path))
+        else:
+            return (404, headers, '')
+
+    return _request_callback
 
 
 class TestDataSourceGCE(test_helpers.HttprettyTestCase):
@@ -67,7 +84,7 @@
     def test_connection(self):
         httpretty.register_uri(
             httpretty.GET, MD_URL_RE,
-            body=_request_callback)
+            body=_new_request_callback())
 
         success = self.ds.get_data()
         self.assertTrue(success)
@@ -79,7 +96,7 @@
     def test_metadata(self):
         httpretty.register_uri(
             httpretty.GET, MD_URL_RE,
-            body=_request_callback)
+            body=_new_request_callback())
         self.ds.get_data()
 
         self.assertEqual(GCE_META.get('instance/hostname'),
@@ -103,7 +120,7 @@
     def test_metadata_partial(self):
         httpretty.register_uri(
             httpretty.GET, MD_URL_RE,
-            body=_request_callback)
+            body=_new_request_callback(GCE_META_PARTIAL))
         self.ds.get_data()
 
         self.assertEqual(GCE_META_PARTIAL.get('instance/id'),
@@ -111,3 +128,13 @@
 
         self.assertEqual(GCE_META_PARTIAL.get('instance/hostname'),
                          self.ds.get_hostname())
+
+    @httpretty.activate
+    def test_metadata_encoding(self):
+        httpretty.register_uri(
+            httpretty.GET, MD_URL_RE,
+            body=_new_request_callback(GCE_META_ENCODING))
+        self.ds.get_data()
+
+        decoded = b64decode(GCE_META_ENCODING.get('instance/attributes/user-data'))
+        self.assertEqual(decoded, self.ds.get_userdata_raw())


Follow ups