Merge lp:~wwitzel3/cloud-init/gce into lp:~cloud-init-dev/cloud-init/trunk

Proposed by Wayne Witzel III
Status: Merged
Merged at revision: 1048
Proposed branch: lp:~wwitzel3/cloud-init/gce
Merge into: lp:~cloud-init-dev/cloud-init/trunk
Diff against target: 154 lines (+53/-17)
2 files modified
cloudinit/sources/DataSourceGCE.py (+10/-1)
tests/unittests/test_datasource/test_gce.py (+43/-16)
To merge this branch: bzr merge lp:~wwitzel3/cloud-init/gce
Reviewer Review Type Date Requested Status
Eric Snow (community) Approve
cloud-init Commiters Pending
Review via email: mp+245209@code.launchpad.net

Commit message

Enable user-data encoding support for GCE. (fixes lp:1404311)

Description of the change

Enable user-data encoding support for GCE. Extended and updated tests to support checking the user-data encoding.

To post a comment you must log in.
Revision history for this message
Eric Snow (ericsnowcurrently) wrote :

This follows the precedent of modules for other providers, so LGTM.

review: Approve
lp:~wwitzel3/cloud-init/gce updated
1048. By Wayne Witzel III

Corrected errant logging message.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cloudinit/sources/DataSourceGCE.py'
2--- cloudinit/sources/DataSourceGCE.py 2014-02-14 19:29:02 +0000
3+++ cloudinit/sources/DataSourceGCE.py 2015-01-06 16:41:42 +0000
4@@ -15,6 +15,8 @@
5 # along with this program. If not, see <http://www.gnu.org/licenses/>.
6
7
8+from base64 import b64decode
9+
10 from cloudinit import log as logging
11 from cloudinit import util
12 from cloudinit import sources
13@@ -27,7 +29,6 @@
14 }
15 REQUIRED_FIELDS = ('instance-id', 'availability-zone', 'local-hostname')
16
17-
18 class DataSourceGCE(sources.DataSource):
19 def __init__(self, sys_cfg, distro, paths):
20 sources.DataSource.__init__(self, sys_cfg, distro, paths)
21@@ -58,6 +59,7 @@
22 ('local-hostname', 'instance/hostname', True),
23 ('public-keys', 'project/attributes/sshKeys', False),
24 ('user-data', 'instance/attributes/user-data', False),
25+ ('user-data-encoding', 'instance/attributes/user-data-encoding', False),
26 ]
27
28 # if we cannot resolve the metadata server, then no point in trying
29@@ -101,6 +103,13 @@
30 lines = self.metadata['public-keys'].splitlines()
31 self.metadata['public-keys'] = [self._trim_key(k) for k in lines]
32
33+ encoding = self.metadata.get('user-data-encoding')
34+ if encoding:
35+ if encoding == 'base64':
36+ self.metadata['user-data'] = b64decode(self.metadata['user-data'])
37+ else:
38+ LOG.warn('unknown user-data-encoding: %s, ignoring', encoding)
39+
40 return found
41
42 @property
43
44=== modified file 'tests/unittests/test_datasource/test_gce.py'
45--- tests/unittests/test_datasource/test_gce.py 2014-07-24 12:49:42 +0000
46+++ tests/unittests/test_datasource/test_gce.py 2015-01-06 16:41:42 +0000
47@@ -18,6 +18,7 @@
48 import httpretty
49 import re
50
51+from base64 import b64encode, b64decode
52 from urlparse import urlparse
53
54 from cloudinit import settings
55@@ -30,29 +31,45 @@
56 'instance/id': '123',
57 'instance/zone': 'foo/bar',
58 'project/attributes/sshKeys': 'user:ssh-rsa AA2..+aRD0fyVw== root@server',
59- 'instance/hostname': 'server.project-name.local',
60+ 'instance/hostname': 'server.project-foo.local',
61 'instance/attributes/user-data': '/bin/echo foo\n',
62+ 'instance/attributes/user-data-encoding':'',
63 }
64
65 GCE_META_PARTIAL = {
66- 'instance/id': '123',
67- 'instance/hostname': 'server.project-name.local',
68+ 'instance/id': '1234',
69+ 'instance/hostname': 'server.project-bar.local',
70+ 'instance/zone': 'bar/baz',
71+}
72+
73+GCE_META_ENCODING = {
74+ 'instance/id': '12345',
75+ 'instance/hostname': 'server.project-baz.local',
76+ 'instance/zone': 'baz/bang',
77+ 'instance/attributes/user-data': b64encode('/bin/echo baz\n'),
78+ 'instance/attributes/user-data-encoding': 'base64',
79 }
80
81 HEADERS = {'X-Google-Metadata-Request': 'True'}
82 MD_URL_RE = re.compile(r'http://metadata.google.internal./computeMetadata/v1/.*')
83
84
85-def _request_callback(method, uri, headers):
86- url_path = urlparse(uri).path
87- if url_path.startswith('/computeMetadata/v1/'):
88- path = url_path.split('/computeMetadata/v1/')[1:][0]
89- else:
90- path = None
91- if path in GCE_META:
92- return (200, headers, GCE_META.get(path))
93- else:
94- return (404, headers, '')
95+def _new_request_callback(gce_meta=None):
96+ if not gce_meta:
97+ gce_meta = GCE_META
98+
99+ def _request_callback(method, uri, headers):
100+ url_path = urlparse(uri).path
101+ if url_path.startswith('/computeMetadata/v1/'):
102+ path = url_path.split('/computeMetadata/v1/')[1:][0]
103+ else:
104+ path = None
105+ if path in gce_meta:
106+ return (200, headers, gce_meta.get(path))
107+ else:
108+ return (404, headers, '')
109+
110+ return _request_callback
111
112
113 class TestDataSourceGCE(test_helpers.HttprettyTestCase):
114@@ -67,7 +84,7 @@
115 def test_connection(self):
116 httpretty.register_uri(
117 httpretty.GET, MD_URL_RE,
118- body=_request_callback)
119+ body=_new_request_callback())
120
121 success = self.ds.get_data()
122 self.assertTrue(success)
123@@ -79,7 +96,7 @@
124 def test_metadata(self):
125 httpretty.register_uri(
126 httpretty.GET, MD_URL_RE,
127- body=_request_callback)
128+ body=_new_request_callback())
129 self.ds.get_data()
130
131 self.assertEqual(GCE_META.get('instance/hostname'),
132@@ -103,7 +120,7 @@
133 def test_metadata_partial(self):
134 httpretty.register_uri(
135 httpretty.GET, MD_URL_RE,
136- body=_request_callback)
137+ body=_new_request_callback(GCE_META_PARTIAL))
138 self.ds.get_data()
139
140 self.assertEqual(GCE_META_PARTIAL.get('instance/id'),
141@@ -111,3 +128,13 @@
142
143 self.assertEqual(GCE_META_PARTIAL.get('instance/hostname'),
144 self.ds.get_hostname())
145+
146+ @httpretty.activate
147+ def test_metadata_encoding(self):
148+ httpretty.register_uri(
149+ httpretty.GET, MD_URL_RE,
150+ body=_new_request_callback(GCE_META_ENCODING))
151+ self.ds.get_data()
152+
153+ decoded = b64decode(GCE_META_ENCODING.get('instance/attributes/user-data'))
154+ self.assertEqual(decoded, self.ds.get_userdata_raw())