Merge lp:~julian-edwards/maas/anon-get-file into lp:~maas-committers/maas/trunk

Proposed by Julian Edwards
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: 335
Proposed branch: lp:~julian-edwards/maas/anon-get-file
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 118 lines (+62/-18)
2 files modified
src/maasserver/api.py (+29/-8)
src/maasserver/tests/test_api.py (+33/-10)
To merge this branch: bzr merge lp:~julian-edwards/maas/anon-get-file
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+98798@code.launchpad.net

Commit message

Allow anonymous filestorage retreival since Juju needs it.

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

Landing this, will comment afterwards ;)

review: Approve
Revision history for this message
Gavin Panella (allenap) wrote :

Looks good. It has already landed, but I have promised to follow-up
and repair according to my review comments. Hence these comments are
as much instructions for me.

[1]

+def get_file(request):
+ filename = request.GET.get("filename", None)

Could do with a docstring, though I guess it's fairly
self-explanatory.

[2]

+@api_operations
+class AnonFilesHandler(AnonymousBaseHandler):
+ """Anonymous file operations."""
+ allowed_methods = ('GET',)

This really needs explanation as to why it's a sensible thing to
do. From talking with Francis and Kapil, it seems that (a) file names
will be uuid-like or contain some unguessable nonce, and (b) this is
required so that all the agents within the environment can see the
file without needing provider credentials.

[3]

+ def setUp(self):
...
+ os.mkdir(self.tmpdir)

This is repeated twice, and ought to be extracted.

Revision history for this message
Julian Edwards (julian-edwards) wrote :

Thanks Gavin.

> This really needs explanation as to why it's a sensible thing to
> do. From talking with Francis and Kapil, it seems that (a) file names
> will be uuid-like or contain some unguessable nonce, and (b) this is
> required so that all the agents within the environment can see the
> file without needing provider credentials.

Yes, I had already talked about this with the Juju guys and there are
basically 2 choices here:

1. Anonymous access to get files (this is what Orchestra does).
2. We put an OAuth token in the URL itself (this is what EC2 does).

The former is massively easier at this moment! We can do #2 when there's more
time and it can be a v1.1 API.

I hate rushing crap through to meet deadlines :(

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/api.py'
2--- src/maasserver/api.py 2012-03-21 05:56:36 +0000
3+++ src/maasserver/api.py 2012-03-22 10:08:20 +0000
4@@ -479,10 +479,38 @@
5 return ('node_mac_handler', [node_system_id, mac_address])
6
7
8+def get_file(request):
9+ filename = request.GET.get("filename", None)
10+ if not filename:
11+ raise MAASAPIBadRequest("Filename not supplied")
12+ try:
13+ db_file = FileStorage.objects.get(filename=filename)
14+ except FileStorage.DoesNotExist:
15+ raise MAASAPINotFound("File not found")
16+ return HttpResponse(db_file.data.read(), status=httplib.OK)
17+
18+
19+@api_operations
20+class AnonFilesHandler(AnonymousBaseHandler):
21+ """Anonymous file operations."""
22+ allowed_methods = ('GET',)
23+
24+ @api_exported('get', 'GET')
25+ def get(self, request):
26+ """Get a named file from the file storage.
27+
28+ :param filename: The exact name of the file you want to get.
29+ :type filename: string
30+ :return: The file is returned in the response content.
31+ """
32+ return get_file(request)
33+
34+
35 @api_operations
36 class FilesHandler(BaseHandler):
37 """File management operations."""
38 allowed_methods = ('GET', 'POST',)
39+ anonymous = AnonFilesHandler
40
41 @api_exported('get', 'GET')
42 def get(self, request):
43@@ -492,14 +520,7 @@
44 :type filename: string
45 :return: The file is returned in the response content.
46 """
47- filename = request.GET.get("filename", None)
48- if not filename:
49- raise MAASAPIBadRequest("Filename not supplied")
50- try:
51- db_file = FileStorage.objects.get(filename=filename)
52- except FileStorage.DoesNotExist:
53- raise MAASAPINotFound("File not found")
54- return HttpResponse(db_file.data.read(), status=httplib.OK)
55+ return get_file(request)
56
57 @api_exported('add', 'POST')
58 def add(self, request):
59
60=== modified file 'src/maasserver/tests/test_api.py'
61--- src/maasserver/tests/test_api.py 2012-03-21 05:56:36 +0000
62+++ src/maasserver/tests/test_api.py 2012-03-22 10:08:20 +0000
63@@ -836,16 +836,7 @@
64 self.assertEqual(httplib.BAD_REQUEST, response.status_code)
65
66
67-class FileStorageAPITest(APITestCase):
68-
69- def setUp(self):
70- super(FileStorageAPITest, self).setUp()
71- media_root = settings.MEDIA_ROOT
72- self.assertFalse(os.path.exists(media_root), "See media/README")
73- self.addCleanup(shutil.rmtree, media_root, ignore_errors=True)
74- os.mkdir(media_root)
75- self.tmpdir = os.path.join(media_root, "testing")
76- os.mkdir(self.tmpdir)
77+class FileStorageTestMixin:
78
79 def make_file(self, name="foo", contents="test file contents"):
80 """Make a temp file named `name` with contents `contents`.
81@@ -877,6 +868,38 @@
82 params = self._create_API_params(op, filename, fileObj)
83 return self.client.get(self.get_uri('files/'), params)
84
85+
86+class AnonymousFileStorageAPITest(APIv10TestMixin, FileStorageTestMixin,
87+ TestCase):
88+
89+ def setUp(self):
90+ super(AnonymousFileStorageAPITest, self).setUp()
91+ media_root = settings.MEDIA_ROOT
92+ self.assertFalse(os.path.exists(media_root), "See media/README")
93+ self.addCleanup(shutil.rmtree, media_root, ignore_errors=True)
94+ os.mkdir(media_root)
95+ self.tmpdir = os.path.join(media_root, "testing")
96+ os.mkdir(self.tmpdir)
97+
98+ def test_get_works_anonymously(self):
99+ factory.make_file_storage(filename="foofilers", data=b"give me rope")
100+ response = self.make_API_GET_request("get", "foofilers")
101+
102+ self.assertEqual(httplib.OK, response.status_code)
103+ self.assertEqual(b"give me rope", response.content)
104+
105+
106+class FileStorageAPITest(APITestCase, FileStorageTestMixin):
107+
108+ def setUp(self):
109+ super(FileStorageAPITest, self).setUp()
110+ media_root = settings.MEDIA_ROOT
111+ self.assertFalse(os.path.exists(media_root), "See media/README")
112+ self.addCleanup(shutil.rmtree, media_root, ignore_errors=True)
113+ os.mkdir(media_root)
114+ self.tmpdir = os.path.join(media_root, "testing")
115+ os.mkdir(self.tmpdir)
116+
117 def test_add_file_succeeds(self):
118 filepath = self.make_file()
119