aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Doan <andy.doan@linaro.org>2015-08-03 11:42:40 -0500
committerAndy Doan <andy.doan@linaro.org>2015-08-24 12:53:39 -0500
commit231df005bc3a4f62636bf24ea9954792043d0c84 (patch)
tree77d91ca423aaa31f4c583d95c171851512459842
parentb2fae238aea6f6bf5dafeabdcf8504107477daa2 (diff)
s3: publish api support
The apiv3 takes advantage of the fact that S3 can generate temporary URLs that our API client (linaro-cp) can upload files through. It should help reduce the load on our l-l-p server. Change-Id: Ibd3c11b7151392c5db7f107c8a6dcd4acb10a6c1
-rw-r--r--license_protected_downloads/api/v3.py42
-rw-r--r--license_protected_downloads/tests/test_api_v3.py59
-rw-r--r--urls.py2
3 files changed, 103 insertions, 0 deletions
diff --git a/license_protected_downloads/api/v3.py b/license_protected_downloads/api/v3.py
index b84a446..24a0bc3 100644
--- a/license_protected_downloads/api/v3.py
+++ b/license_protected_downloads/api/v3.py
@@ -1,4 +1,46 @@
+from django.conf import settings
+from django.http import HttpResponse
+from django.views.decorators.csrf import csrf_exempt
+
+from license_protected_downloads.artifact.s3 import S3Artifact
+from license_protected_downloads.api.v1 import (
+ HttpResponseError,
+)
from license_protected_downloads.api import v2
+from license_protected_downloads.models import (
+ APILog,
+)
+
# no changes required for tokens in v3
token = v2.token
+
+
+class PublishResource(v2.PublishResource):
+ def __init__(self, request, path):
+ super(PublishResource, self).__init__(request, path)
+
+ def POST(self):
+ b = S3Artifact.get_bucket()
+ if not b:
+ raise HttpResponseError('S3 is not enabled', 403)
+
+ k = b.new_key(settings.S3_PREFIX_PATH + self.path)
+ if k.exists():
+ APILog.mark(self.request, 'FILE_OVERWRITE_DENIED')
+ raise HttpResponseError('File already exists', 403)
+
+ headers = {}
+ mtype = self.request.POST.get('Content-Type', None)
+ if mtype:
+ headers['Content-Type'] = mtype
+
+ resp = HttpResponse(status=201)
+ resp['Location'] = k.generate_url(60, method='PUT', headers=headers)
+ APILog.mark(self.request, 'FILE_UPLOAD', self.api_key)
+ return resp
+
+
+@csrf_exempt
+def publish(request, path):
+ return PublishResource(request, path).handle()
diff --git a/license_protected_downloads/tests/test_api_v3.py b/license_protected_downloads/tests/test_api_v3.py
index e3bb40c..9c7ca9c 100644
--- a/license_protected_downloads/tests/test_api_v3.py
+++ b/license_protected_downloads/tests/test_api_v3.py
@@ -1,13 +1,23 @@
+import unittest
+import urllib2
import datetime
import json
+from django.conf import settings
from django.test import Client, TestCase
+from license_protected_downloads.artifact.s3 import S3Artifact
from license_protected_downloads.models import (
APIKeyStore,
APIToken,
)
+import requests
+
+
+_orig_s3_prefix = getattr(settings, 'S3_PREFIX_PATH', None)
+_s3_enabled = _orig_s3_prefix is not None
+
class APIv3TokenTests(TestCase):
'''A subset of token tests since this *should* just be v2 token code'''
@@ -36,3 +46,52 @@ class APIv3TokenTests(TestCase):
resp = self.client.post(
'/api/v3/token/', data=data, HTTP_AUTHTOKEN=self.api_key.key)
self.assertEqual(201, resp.status_code)
+
+
+@unittest.skipIf(_s3_enabled is False, 's3 not configured')
+class APIv3PublishTests(TestCase):
+ def setUp(self):
+ self.client = Client(enforce_csrf_checks=True)
+ self.api_key = APIKeyStore.objects.create(key='foo', public=True)
+
+ settings.S3_PREFIX_PATH = settings.S3_PREFIX_PATH[:-1] + '-test-pub/'
+ self.bucket = S3Artifact.get_bucket()
+
+ # make sure nothing was left from an old run
+ keys = self.bucket.list(settings.S3_PREFIX_PATH)
+ self.bucket.delete_keys(keys)
+
+ def tearDown(self):
+ settings.S3_PREFIX_PATH = _orig_s3_prefix
+
+ def _post_file(self, url, token, content, resp_code=201):
+ response = self.client.post(url, HTTP_AUTHTOKEN=token)
+ self.assertEqual(response.status_code, resp_code)
+ if resp_code == 201:
+ url = response['Location']
+ r = requests.put(url, data=content)
+ self.assertEqual(200, r.status_code)
+
+ def test_publish_simple(self):
+ content = 'foo bar'
+ token = APIToken.objects.create(key=self.api_key).token
+ self._post_file('/api/v3/publish/a/b', token, content)
+
+ # we'll be missing a build-info so:
+ self.assertEqual(403, self.client.get('/a/b').status_code)
+
+ # add a build-info and see if the file works
+ info = 'Format-Version: 0.5\n\nFiles-Pattern: *\nLicense-Type: open\n'
+ self._post_file('/api/v3/publish/a/BUILD-INFO.txt', token, info)
+
+ resp = self.client.get('/a/b')
+ self.assertEqual(302, resp.status_code)
+ resp = urllib2.urlopen(resp['Location'])
+ self.assertEqual(content, resp.read())
+
+ def test_publish_no_overwrite(self):
+ content = 'foo bar'
+ token = APIToken.objects.create(key=self.api_key).token
+ k = self.bucket.get_key(settings.S3_PREFIX_PATH + 'a', validate=False)
+ k.set_contents_from_string(content)
+ self._post_file('/api/v3/publish/a', token, 'wont overwrite', 403)
diff --git a/urls.py b/urls.py
index dfd14c9..14d818f 100644
--- a/urls.py
+++ b/urls.py
@@ -67,6 +67,8 @@ urlpatterns = patterns(
url(r'^api/v3/token/(?P<token>.*)$',
'license_protected_downloads.api.v3.token'),
+ url(r'^api/v3/publish/(?P<path>.*)$',
+ 'license_protected_downloads.api.v3.publish'),
# Catch-all. We always return a file (or try to) if it exists.
# This handler does that.