Merge lp:~oddbloke/ubuntu/precise/cloud-init/lp1374600 into lp:ubuntu/precise-proposed/cloud-init

Proposed by Dan Watkins
Status: Merged
Merged at revision: 212
Proposed branch: lp:~oddbloke/ubuntu/precise/cloud-init/lp1374600
Merge into: lp:ubuntu/precise-proposed/cloud-init
Diff against target: 388 lines (+338/-3)
6 files modified
.pc/applied-patches (+1/-0)
cloudinit/DataSourceGCE.py (+156/-0)
debian/changelog (+7/-0)
debian/cloud-init.templates (+3/-3)
debian/patches/lp-1378441-backport-gce-data-source.patch (+170/-0)
debian/patches/series (+1/-0)
To merge this branch: bzr merge lp:~oddbloke/ubuntu/precise/cloud-init/lp1374600
Reviewer Review Type Date Requested Status
Scott Moser Needs Fixing
Review via email: mp+237813@code.launchpad.net

Description of the change

This backports the GCE data source from trusty; the intent is to SRU it in the near future.

To post a comment you must log in.
Revision history for this message
Scott Moser (smoser) wrote :

2 nit picks, other than that, assuming this is tested it looks fine.
The headers you're putting on the patch are described at http://dep.debian.net/deps/dep3/
so:
a.) add 'Origin: upstream'
    you can probably remove 'Author:' here (i realize we're probably not consistent in other patches on this).

b.) the bug number you referenced is a private bug (1374600), while there is a public bug (bug 1378441). Seems to make sense to reference the public bug.

c.) reference the ubuntu bug in the changelog message.

so make those changes, and i'll upload for you.

review: Needs Fixing
212. By Dan Watkins

debian/patches/lp-1378441-backport-gce-data-source.patch: backported
Google Compute Engine data source. (LP: #1378441)

Revision history for this message
Dan Watkins (oddbloke) wrote :

I've just pushed up those changes.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.pc/applied-patches'
2--- .pc/applied-patches 2014-09-17 19:44:07 +0000
3+++ .pc/applied-patches 2014-10-10 08:21:23 +0000
4@@ -24,3 +24,4 @@
5 lp-1302229-fix_futils_azure.patch
6 lp-1363260-add-cloudsigma_ds.patch
7 lp-1336855-grub_xvda.patch
8+lp-1378441-backport-gce-data-source.patch
9
10=== added directory '.pc/lp-1378441-backport-gce-data-source.patch'
11=== added directory '.pc/lp-1378441-backport-gce-data-source.patch/cloudinit'
12=== added file '.pc/lp-1378441-backport-gce-data-source.patch/cloudinit/DataSourceGCE.py'
13=== added file '.pc/lp-1378441-backport-gce-data-source.patch/cloudinit/url_helper.py'
14=== added file 'cloudinit/DataSourceGCE.py'
15--- cloudinit/DataSourceGCE.py 1970-01-01 00:00:00 +0000
16+++ cloudinit/DataSourceGCE.py 2014-10-10 08:21:23 +0000
17@@ -0,0 +1,156 @@
18+# vi: ts=4 expandtab
19+#
20+# Author: Vaidas Jablonskis <jablonskis@gmail.com>
21+#
22+# This program is free software: you can redistribute it and/or modify
23+# it under the terms of the GNU General Public License version 3, as
24+# published by the Free Software Foundation.
25+#
26+# This program is distributed in the hope that it will be useful,
27+# but WITHOUT ANY WARRANTY; without even the implied warranty of
28+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29+# GNU General Public License for more details.
30+#
31+# You should have received a copy of the GNU General Public License
32+# along with this program. If not, see <http://www.gnu.org/licenses/>.
33+
34+import logging
35+import urllib2
36+
37+from cloudinit import util
38+from cloudinit import DataSource as sources
39+
40+LOG = logging.getLogger(__name__)
41+
42+BUILTIN_DS_CONFIG = {
43+ 'metadata_url': 'http://metadata.google.internal./computeMetadata/v1/'
44+}
45+REQUIRED_FIELDS = ('instance-id', 'availability-zone', 'local-hostname')
46+
47+
48+class _FakeResponse(object):
49+
50+ def __init__(self, code, contents):
51+ self.code = code
52+ self.contents = contents
53+
54+
55+class url_helper(object):
56+ # This is a shim designed to look like url_helper.py from future versions
57+ # of cloud-init.
58+
59+ @staticmethod
60+ def readurl(url, headers):
61+ request = urllib2.Request(url, headers=headers)
62+ response = urllib2.urlopen(request)
63+ return _FakeResponse(code=response.getcode(), contents=response.read())
64+
65+ UrlError = urllib2.URLError
66+
67+
68+class DataSourceGCE(sources.DataSource):
69+ def __init__(self, sys_cfg):
70+ sources.DataSource.__init__(self, sys_cfg)
71+ self.metadata = dict()
72+ self.ds_cfg = util.mergedict(
73+ util.get_cfg_by_path(sys_cfg, ["datasource", "GCE"], {}),
74+ BUILTIN_DS_CONFIG)
75+ self.metadata_address = self.ds_cfg['metadata_url']
76+
77+ # GCE takes sshKeys attribute in the format of '<user>:<public_key>'
78+ # so we have to trim each key to remove the username part
79+ def _trim_key(self, public_key):
80+ try:
81+ index = public_key.index(':')
82+ if index > 0:
83+ return public_key[(index + 1):]
84+ except:
85+ return public_key
86+
87+ def get_data(self):
88+ # GCE metadata server requires a custom header since v1
89+ headers = {'X-Google-Metadata-Request': 'True'}
90+
91+ # url_map: (our-key, path, required)
92+ url_map = [
93+ ('instance-id', 'instance/id', True),
94+ ('availability-zone', 'instance/zone', True),
95+ ('local-hostname', 'instance/hostname', True),
96+ ('public-keys', 'project/attributes/sshKeys', False),
97+ ('user-data', 'instance/attributes/user-data', False),
98+ ]
99+
100+ # if we cannot resolve the metadata server, then no point in trying
101+ if not util.is_resolvable_url(self.metadata_address):
102+ LOG.debug("%s is not resolvable", self.metadata_address)
103+ return False
104+
105+ # iterate over url_map keys to get metadata items
106+ found = False
107+ for (mkey, path, required) in url_map:
108+ try:
109+ resp = url_helper.readurl(url=self.metadata_address + path,
110+ headers=headers)
111+ if resp.code == 200:
112+ found = True
113+ self.metadata[mkey] = resp.contents
114+ else:
115+ if required:
116+ msg = "required url %s returned code %s. not GCE"
117+ if not found:
118+ LOG.debug(msg, path, resp.code)
119+ else:
120+ LOG.warn(msg, path, resp.code)
121+ return False
122+ else:
123+ self.metadata[mkey] = None
124+ except url_helper.UrlError as e:
125+ if required:
126+ msg = "required url %s raised exception %s. not GCE"
127+ if not found:
128+ LOG.debug(msg, path, e)
129+ else:
130+ LOG.warn(msg, path, e)
131+ return False
132+ msg = "Failed to get %s metadata item: %s."
133+ LOG.debug(msg, path, e)
134+
135+ self.metadata[mkey] = None
136+
137+ if self.metadata['public-keys']:
138+ lines = self.metadata['public-keys'].splitlines()
139+ self.metadata['public-keys'] = [self._trim_key(k) for k in lines]
140+
141+ return found
142+
143+ @property
144+ def launch_index(self):
145+ # GCE does not provide lauch_index property
146+ return None
147+
148+ def get_instance_id(self):
149+ return self.metadata['instance-id']
150+
151+ def get_public_ssh_keys(self):
152+ return self.metadata['public-keys']
153+
154+ def get_hostname(self, fqdn=False, _resolve_ip=False):
155+ return self.metadata['local-hostname']
156+
157+ @property
158+ def userdata_raw(self):
159+ return self.metadata['user-data'] or ''
160+
161+ @property
162+ def availability_zone(self):
163+ return self.metadata['availability-zone']
164+
165+# Used to match classes to dependencies
166+datasources = [
167+ (DataSourceGCE, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
168+]
169+
170+
171+# Return a list of data sources that match this set of dependencies
172+def get_datasource_list(depends):
173+ return sources.list_from_depends(depends, datasources)
174
175=== modified file 'debian/changelog'
176--- debian/changelog 2014-09-18 22:26:16 +0000
177+++ debian/changelog 2014-10-10 08:21:23 +0000
178@@ -1,3 +1,10 @@
179+cloud-init (0.6.3-0ubuntu1.15) precise; urgency=medium
180+
181+ * debian/patches/lp-1378441-backport-gce-data-source.patch: backported
182+ Google Compute Engine data source. (LP: #1378441)
183+
184+ -- Daniel Watkins <daniel.watkins@canonical.com> Thu, 10 Oct 2014 09:24:57 +0100
185+
186 cloud-init (0.6.3-0ubuntu1.14) precise-proposed; urgency=medium
187
188 * debian/patches/lp-1363260-add-cloudsigma_ds.patch: backport from
189
190=== modified file 'debian/cloud-init.templates'
191--- debian/cloud-init.templates 2014-09-17 19:44:07 +0000
192+++ debian/cloud-init.templates 2014-10-10 08:21:23 +0000
193@@ -1,8 +1,8 @@
194 Template: cloud-init/datasources
195 Type: multiselect
196-Default: NoCloud, ConfigDrive, Azure, OVF, MAAS, CloudSigma
197-Choices-C: NoCloud, ConfigDrive, Azure, OVF, MAAS, Ec2, SmartOS, CloudSigma
198-Choices: NoCloud: Reads info from /var/lib/cloud/seed only, ConfigDrive: Reads data from Openstack Config Drive, Azure: read from MS Azure cdrom. Requires walinux-agent, OVF: Reads data from OVF Transports, MAAS: Reads data from Ubuntu MAAS, Ec2: reads data from EC2 Metadata service, SmartOS: reads data from serial console, CloudSigma: reads data from serial console
199+Default: NoCloud, ConfigDrive, Azure, OVF, MAAS, CloudSigma, GCE
200+Choices-C: NoCloud, ConfigDrive, Azure, OVF, MAAS, Ec2, SmartOS, CloudSigma, GCE
201+Choices: NoCloud: Reads info from /var/lib/cloud/seed only, ConfigDrive: Reads data from Openstack Config Drive, Azure: read from MS Azure cdrom. Requires walinux-agent, OVF: Reads data from OVF Transports, MAAS: Reads data from Ubuntu MAAS, Ec2: reads data from EC2 Metadata service, SmartOS: reads data from serial console, CloudSigma: reads data from serial console, GCE: reads data from Google Compute Engine metadata service
202 Description: Which data sources should be searched?
203 Cloud-init supports searching different "Data Sources" for information
204 that it uses to configure a cloud instance.
205
206=== added file 'debian/patches/lp-1378441-backport-gce-data-source.patch'
207--- debian/patches/lp-1378441-backport-gce-data-source.patch 1970-01-01 00:00:00 +0000
208+++ debian/patches/lp-1378441-backport-gce-data-source.patch 2014-10-10 08:21:23 +0000
209@@ -0,0 +1,170 @@
210+Origin: upstream
211+Bug: https://launchpad.net/bugs/1378441
212+Forwarded: not-needed
213+Description: Add support for Google Compute Engine
214+ In order to use cloud-init on GCE, we need a GCE data source. This backports
215+ the GCE datasource from trusty, DataSourceGCE.py.
216+ .
217+ This is accompanied by a debian/cloud-init.templates change to enable debconf
218+ to select the 'GCE' datasource.
219+Index: precise-proposed/cloudinit/DataSourceGCE.py
220+===================================================================
221+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
222++++ precise-proposed/cloudinit/DataSourceGCE.py 2014-10-07 15:43:05.054129022 +0100
223+@@ -0,0 +1,156 @@
224++# vi: ts=4 expandtab
225++#
226++# Author: Vaidas Jablonskis <jablonskis@gmail.com>
227++#
228++# This program is free software: you can redistribute it and/or modify
229++# it under the terms of the GNU General Public License version 3, as
230++# published by the Free Software Foundation.
231++#
232++# This program is distributed in the hope that it will be useful,
233++# but WITHOUT ANY WARRANTY; without even the implied warranty of
234++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
235++# GNU General Public License for more details.
236++#
237++# You should have received a copy of the GNU General Public License
238++# along with this program. If not, see <http://www.gnu.org/licenses/>.
239++
240++import logging
241++import urllib2
242++
243++from cloudinit import util
244++from cloudinit import DataSource as sources
245++
246++LOG = logging.getLogger(__name__)
247++
248++BUILTIN_DS_CONFIG = {
249++ 'metadata_url': 'http://metadata.google.internal./computeMetadata/v1/'
250++}
251++REQUIRED_FIELDS = ('instance-id', 'availability-zone', 'local-hostname')
252++
253++
254++class _FakeResponse(object):
255++
256++ def __init__(self, code, contents):
257++ self.code = code
258++ self.contents = contents
259++
260++
261++class url_helper(object):
262++ # This is a shim designed to look like url_helper.py from future versions
263++ # of cloud-init.
264++
265++ @staticmethod
266++ def readurl(url, headers):
267++ request = urllib2.Request(url, headers=headers)
268++ response = urllib2.urlopen(request)
269++ return _FakeResponse(code=response.getcode(), contents=response.read())
270++
271++ UrlError = urllib2.URLError
272++
273++
274++class DataSourceGCE(sources.DataSource):
275++ def __init__(self, sys_cfg):
276++ sources.DataSource.__init__(self, sys_cfg)
277++ self.metadata = dict()
278++ self.ds_cfg = util.mergedict(
279++ util.get_cfg_by_path(sys_cfg, ["datasource", "GCE"], {}),
280++ BUILTIN_DS_CONFIG)
281++ self.metadata_address = self.ds_cfg['metadata_url']
282++
283++ # GCE takes sshKeys attribute in the format of '<user>:<public_key>'
284++ # so we have to trim each key to remove the username part
285++ def _trim_key(self, public_key):
286++ try:
287++ index = public_key.index(':')
288++ if index > 0:
289++ return public_key[(index + 1):]
290++ except:
291++ return public_key
292++
293++ def get_data(self):
294++ # GCE metadata server requires a custom header since v1
295++ headers = {'X-Google-Metadata-Request': 'True'}
296++
297++ # url_map: (our-key, path, required)
298++ url_map = [
299++ ('instance-id', 'instance/id', True),
300++ ('availability-zone', 'instance/zone', True),
301++ ('local-hostname', 'instance/hostname', True),
302++ ('public-keys', 'project/attributes/sshKeys', False),
303++ ('user-data', 'instance/attributes/user-data', False),
304++ ]
305++
306++ # if we cannot resolve the metadata server, then no point in trying
307++ if not util.is_resolvable_url(self.metadata_address):
308++ LOG.debug("%s is not resolvable", self.metadata_address)
309++ return False
310++
311++ # iterate over url_map keys to get metadata items
312++ found = False
313++ for (mkey, path, required) in url_map:
314++ try:
315++ resp = url_helper.readurl(url=self.metadata_address + path,
316++ headers=headers)
317++ if resp.code == 200:
318++ found = True
319++ self.metadata[mkey] = resp.contents
320++ else:
321++ if required:
322++ msg = "required url %s returned code %s. not GCE"
323++ if not found:
324++ LOG.debug(msg, path, resp.code)
325++ else:
326++ LOG.warn(msg, path, resp.code)
327++ return False
328++ else:
329++ self.metadata[mkey] = None
330++ except url_helper.UrlError as e:
331++ if required:
332++ msg = "required url %s raised exception %s. not GCE"
333++ if not found:
334++ LOG.debug(msg, path, e)
335++ else:
336++ LOG.warn(msg, path, e)
337++ return False
338++ msg = "Failed to get %s metadata item: %s."
339++ LOG.debug(msg, path, e)
340++
341++ self.metadata[mkey] = None
342++
343++ if self.metadata['public-keys']:
344++ lines = self.metadata['public-keys'].splitlines()
345++ self.metadata['public-keys'] = [self._trim_key(k) for k in lines]
346++
347++ return found
348++
349++ @property
350++ def launch_index(self):
351++ # GCE does not provide lauch_index property
352++ return None
353++
354++ def get_instance_id(self):
355++ return self.metadata['instance-id']
356++
357++ def get_public_ssh_keys(self):
358++ return self.metadata['public-keys']
359++
360++ def get_hostname(self, fqdn=False, _resolve_ip=False):
361++ return self.metadata['local-hostname']
362++
363++ @property
364++ def userdata_raw(self):
365++ return self.metadata['user-data'] or ''
366++
367++ @property
368++ def availability_zone(self):
369++ return self.metadata['availability-zone']
370++
371++# Used to match classes to dependencies
372++datasources = [
373++ (DataSourceGCE, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
374++]
375++
376++
377++# Return a list of data sources that match this set of dependencies
378++def get_datasource_list(depends):
379++ return sources.list_from_depends(depends, datasources)
380
381=== modified file 'debian/patches/series'
382--- debian/patches/series 2014-09-17 19:44:07 +0000
383+++ debian/patches/series 2014-10-10 08:21:23 +0000
384@@ -24,3 +24,4 @@
385 lp-1302229-fix_futils_azure.patch
386 lp-1363260-add-cloudsigma_ds.patch
387 lp-1336855-grub_xvda.patch
388+lp-1378441-backport-gce-data-source.patch

Subscribers

People subscribed via source and target branches

to all changes: