← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~jtv/maas/metadata into lp:maas

 

Jeroen T. Vermeulen has proposed merging lp:~jtv/maas/metadata into lp:maas.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~jtv/maas/metadata/+merge/91995

This puts in place just the bare bones for serving up a metadata API, as in http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/AESDG-chapter-instancedata.html

I haven't built a test runner yet, so I don't know whether these tests will pass.  I'll do that next; it will require some buildout magic.

The next step, of course, is to fill in the actual data.  We'll need to look into the grimy details a bit more to get those clear.

It's all django.  There's no piston, because despite this being an API, the match was very poor:
 * Piston would give us easy support for POST and such, but in this case we only need GET.
 * There's authentication support, but the metadata service only cares about the requester's MAC.
 * Metadata goes out in a format that's simpler than anything piston supports out of the box.
 * We have no models yet.  Piston steers you towards a strict coupling between model and views.

We may change our minds about the auth.  The “authorize anyone, but give them data associated with their own MAC address” 

You'll note that we still have some overarching stuff in src/maasserver that really ought to be in src/maas.
-- 
https://code.launchpad.net/~jtv/maas/metadata/+merge/91995
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jtv/maas/metadata into lp:maas.
=== modified file 'src/maas/urls.py'
--- src/maas/urls.py	2012-02-01 15:09:40 +0000
+++ src/maas/urls.py	2012-02-08 09:03:07 +0000
@@ -22,6 +22,7 @@
 
 urlpatterns = patterns('',
     url(r'^', include('maasserver.urls')),
+    url(r'^metadata', include('metadataserver.urls')),
 )
 
 if settings.STATIC_LOCAL_SERVE:

=== modified file 'src/maasserver/management/commands/runserver.py'
--- src/maasserver/management/commands/runserver.py	2012-02-07 16:13:13 +0000
+++ src/maasserver/management/commands/runserver.py	2012-02-08 09:03:07 +0000
@@ -21,28 +21,52 @@
     )
 
 
-error_template = """
-<html>
-<head><title>Oops! - %(id)s</title></head>
-<body>
-<h1>Oops!</h1>
-<p>
-  Something broke while generating this page.
-</p>
-<p>
-  If the problem persists, please
-  <a href="https://bugs.launchpad.net/maas/";>file a bug</a>.  Make a note of
-  the "oops id": <strong>%(id)s</strong>
-</p>
-</html>
-""".lstrip()
-
-
-def render_error(report):
-    """Produce an HTML error report, in raw bytes (not unicode)."""
-    return (error_template % report).encode('ascii')
-
-
+<<<<<<< TREE
+error_template = """
+<html>
+<head><title>Oops! - %(id)s</title></head>
+<body>
+<h1>Oops!</h1>
+<p>
+  Something broke while generating this page.
+</p>
+<p>
+  If the problem persists, please
+  <a href="https://bugs.launchpad.net/maas/";>file a bug</a>.  Make a note of
+  the "oops id": <strong>%(id)s</strong>
+</p>
+</html>
+""".lstrip()
+
+
+def render_error(report):
+    """Produce an HTML error report, in raw bytes (not unicode)."""
+    return (error_template % report).encode('ascii')
+
+
+=======
+error_template = """
+<html>
+<head><title>Oops! - %(id)s</title></head>
+<body>
+<h1>Oops!</h1>
+<p>
+  Something broke while generating this page.
+</p>
+<p>
+  If the problem persists, please
+  <a href="https://bugs.launchpad.net/maas/";>file a bug</a>.  Make a note of
+  the "oops id": <strong>%(id)s</strong>
+</html>
+""".lstrip()
+
+
+def render_error(report):
+    """Produce an HTML error report, in raw bytes (not unicode)."""
+    return (error_template % report).encode('ascii')
+
+
+>>>>>>> MERGE-SOURCE
 class Command(BaseRunserverCommand):
     """Customized "runserver" command that wraps the WSGI handler."""
 

=== modified file 'src/maasserver/middleware.py'
--- src/maasserver/middleware.py	2012-01-31 18:23:35 +0000
+++ src/maasserver/middleware.py	2012-02-08 09:03:07 +0000
@@ -24,22 +24,29 @@
 class AccessMiddleware(object):
     """Protect access to views.
 
-    - login/logout/api-doc urls: authorize unconditionally
-    - static resources urls: authorize unconditionally
-    - API urls: deny (Forbidden error - 401) anonymous requests
-    - views urls: redirect anonymous requests to login page
+    Most UI views are visible only to logged-in users, but there are pages
+    that are accessible to anonymous users (e.g. the login page!) or that
+    use other authentication (e.g. the MaaS API, which is managed through
+    piston).
     """
 
     def __init__(self):
-        self.public_urls = re.compile(
-            "|".join(
-                (reverse('login'),
-                 reverse('logout'),
-                 reverse('favicon'),
-                 reverse('robots'),
-                 reverse('api-doc'),
-                 settings.API_URL_REGEXP,  # API calls are protected by piston.
-                 settings.STATIC_URL)))
+        # URL prefixes that do not require authentication by Django.
+        public_url_roots = [
+            # Login/logout pages: must be visible to anonymous users.
+            reverse('login'),
+            reverse('logout'),
+            # Static resources are publicly visible.
+            settings.STATIC_URL,
+            reverse('favicon'),
+            reverse('robots'),
+            reverse('api-doc'),
+            # Metadata service is for use by nodes; no login.
+            reverse('metadata'),
+            # API calls are protected by piston.
+            settings.API_URL_REGEXP,
+            ]
+        self.public_urls = re.compile("|".join(public_url_roots))
         self.login_url = reverse('login')
 
     def process_request(self, request):

=== added directory 'src/metadataserver'
=== added file 'src/metadataserver/__init__.py'
=== added file 'src/metadataserver/api.py'
--- src/metadataserver/api.py	1970-01-01 00:00:00 +0000
+++ src/metadataserver/api.py	2012-02-08 09:03:07 +0000
@@ -0,0 +1,66 @@
+# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Metadata API."""
+
+from __future__ import (
+    print_function,
+    unicode_literals,
+    )
+
+__metaclass__ = type
+__all__ = [
+    'metadata_index',
+    'meta_data',
+    'version',
+    'user_data',
+    ]
+
+from django.http import HttpResponse
+
+
+class UnknownMetadataVersion(RuntimeError):
+    """Not a known metadata version."""
+
+
+def make_text_response(contents):
+    """Create a response containing `contents` as plain text."""
+    return HttpResponse(contents, mimetype='text/plain')
+
+
+def make_list_response(items):
+    """Create an `HttpResponse` listing `items`, one per line."""
+    return make_text_response('\n'.join(items))
+
+
+def check_version(version):
+    """Check that `version` is a supported metadata version."""
+    if version != 'latest':
+        raise UnknownMetadataVersion("Unknown metadata version: %s" % version)
+
+
+def metadata_index(request):
+    """View: top-level metadata listing."""
+    return make_list_response(['latest'])
+
+
+def version(request, version):
+    """View: listing for a given metadata version."""
+    check_version(version)
+    return make_list_response(['meta-data', 'user-data'])
+
+
+def meta_data(request, version):
+    """View: meta-data listing for a given version."""
+    check_version(version)
+    items = [
+        'kernel-id',
+        ]
+    return make_list_response(items)
+
+
+def user_data(request, version):
+    """View: user-data blob for a given version."""
+    check_version(version)
+    data = "User data here."
+    return make_text_response(data)

=== added file 'src/metadataserver/models.py'
--- src/metadataserver/models.py	1970-01-01 00:00:00 +0000
+++ src/metadataserver/models.py	2012-02-08 09:03:07 +0000
@@ -0,0 +1,17 @@
+# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Model for the metadata server."""
+
+from __future__ import (
+    print_function,
+    unicode_literals,
+    )
+
+__metaclass__ = type
+__all__ = [
+    ]
+
+#from django.db import models
+
+# Nothing here yet.

=== added file 'src/metadataserver/tests.py'
--- src/metadataserver/tests.py	1970-01-01 00:00:00 +0000
+++ src/metadataserver/tests.py	2012-02-08 09:03:07 +0000
@@ -0,0 +1,16 @@
+"""
+This file demonstrates writing tests using the unittest module. These will pass
+when you run "manage.py test".
+
+Replace this with more appropriate tests for your application.
+"""
+
+from django.test import TestCase
+
+
+class SimpleTest(TestCase):
+    def test_basic_addition(self):
+        """
+        Tests that 1 + 1 always equals 2.
+        """
+        self.assertEqual(1 + 1, 2)

=== added file 'src/metadataserver/urls.py'
--- src/metadataserver/urls.py	1970-01-01 00:00:00 +0000
+++ src/metadataserver/urls.py	2012-02-08 09:03:07 +0000
@@ -0,0 +1,39 @@
+# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Metadata API URLs."""
+
+from __future__ import (
+    print_function,
+    unicode_literals,
+    )
+
+__metaclass__ = type
+__all__ = [
+    'urlpatterns',
+    ]
+
+from django.conf.urls.defaults import (
+    patterns,
+    url,
+    )
+
+from metadataserver.api import (
+    meta_data,
+    metadata_index,
+    version,
+    user_data,
+    )
+
+
+urlpatterns = patterns(
+    '',
+    url(
+        r'(?P<version>[^/]+)/meta-data/', meta_data,
+        name='metadata_meta_data'),
+    url(
+        r'(?P<version>[^/]+)/user-data/', user_data,
+        name='metadata_user_data'),
+    url(r'(?P<version>[^/]+)/', version, name='metadata_version'),
+    url(r'', metadata_index, name='metadata'),
+    )

=== added file 'src/metadataserver/views.py'
--- src/metadataserver/views.py	1970-01-01 00:00:00 +0000
+++ src/metadataserver/views.py	2012-02-08 09:03:07 +0000
@@ -0,0 +1,1 @@
+# Create your views here.