--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+'''
+Utility for fetching a resource (e.g. a template) from a URL.
+'''
+
+import urllib2
+import urlparse
+
+from heat.common import exception
+
+from heat.openstack.common import log as logging
+
+logger = logging.getLogger(__name__)
+
+
+def get(url):
+ '''
+ Get the data at the specifier URL.
+
+ The URL must use the http: or https: schemes.
+ Raise an IOError if getting the data fails.
+ '''
+ logger.info(_('Fetching data from %s') % url)
+
+ components = urlparse.urlparse(url)
+
+ if components.scheme not in ('http', 'https'):
+ raise urllib2.URLError('Invalid URL scheme %s' % components.scheme)
+
+ response = urllib2.urlopen(url)
+ return response.read()
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import mox
+import nose
+from nose.plugins.attrib import attr
+import StringIO
+import unittest
+import urllib2
+
+from heat.common import urlfetch
+
+
+@attr(tag=['unit', 'urlfetch'])
+@attr(speed='fast')
+class UrlFetchTest(unittest.TestCase):
+ def setUp(self):
+ self.m = mox.Mox()
+ self.m.StubOutWithMock(urllib2, 'urlopen')
+
+ def tearDown(self):
+ self.m.UnsetStubs()
+
+ def test_file_scheme(self):
+ self.m.ReplayAll()
+ self.assertRaises(IOError, urlfetch.get, 'file:///etc/profile')
+ self.m.VerifyAll()
+
+ def test_http_scheme(self):
+ url = 'http://example.com/template'
+ data = '{ "foo": "bar" }'
+
+ urllib2.urlopen(url).AndReturn(StringIO.StringIO(data))
+ self.m.ReplayAll()
+
+ self.assertEqual(urlfetch.get(url), data)
+ self.m.VerifyAll()
+
+ def test_https_scheme(self):
+ url = 'https://example.com/template'
+ data = '{ "foo": "bar" }'
+
+ urllib2.urlopen(url).AndReturn(StringIO.StringIO(data))
+ self.m.ReplayAll()
+
+ self.assertEqual(urlfetch.get(url), data)
+ self.m.VerifyAll()
+
+ def test_http_error(self):
+ url = 'http://example.com/template'
+
+ urllib2.urlopen(url).AndRaise(urllib2.URLError('fubar'))
+ self.m.ReplayAll()
+
+ self.assertRaises(IOError, urlfetch.get, url)
+ self.m.VerifyAll()
+
+ def test_garbage(self):
+ self.m.ReplayAll()
+ self.assertRaises(IOError, urlfetch.get, 'wibble')
+ self.m.VerifyAll()