Commit bb30a120 by Eric Fischer

Switch back to boto

The boto3 upgrade seems to have broken in prod

EDUCATOR-1183, EDUCATOR-1203
parent 251005cf
import logging import logging
import boto3 import boto
from django.conf import settings from django.conf import settings
...@@ -15,16 +15,15 @@ class Backend(BaseBackend): ...@@ -15,16 +15,15 @@ class Backend(BaseBackend):
def get_upload_url(self, key, content_type): def get_upload_url(self, key, content_type):
bucket_name, key_name = self._retrieve_parameters(key) bucket_name, key_name = self._retrieve_parameters(key)
try: try:
client = _connect_to_s3() conn = _connect_to_s3()
return client.generate_presigned_url( upload_url = conn.generate_url(
ExpiresIn=self.UPLOAD_URL_TIMEOUT, expires_in=self.UPLOAD_URL_TIMEOUT,
ClientMethod='put_object', method='PUT',
Params={ bucket=bucket_name,
'Bucket': bucket_name, key=key_name,
'Key': key_name headers={'Content-Length': '5242880', 'Content-Type': content_type}
},
HttpMethod="PUT"
) )
return upload_url
except Exception as ex: except Exception as ex:
logger.exception( logger.exception(
u"An internal exception occurred while generating an upload URL." u"An internal exception occurred while generating an upload URL."
...@@ -34,16 +33,10 @@ class Backend(BaseBackend): ...@@ -34,16 +33,10 @@ class Backend(BaseBackend):
def get_download_url(self, key): def get_download_url(self, key):
bucket_name, key_name = self._retrieve_parameters(key) bucket_name, key_name = self._retrieve_parameters(key)
try: try:
client = _connect_to_s3() conn = _connect_to_s3()
return client.generate_presigned_url( bucket = conn.get_bucket(bucket_name)
ExpiresIn=self.DOWNLOAD_URL_TIMEOUT, s3_key = bucket.get_key(key_name)
ClientMethod='get_object', return s3_key.generate_url(expires_in=self.DOWNLOAD_URL_TIMEOUT) if s3_key else ""
Params={
'Bucket': bucket_name,
'Key': key_name
},
HttpMethod="GET"
)
except Exception as ex: except Exception as ex:
logger.exception( logger.exception(
u"An internal exception occurred while generating a download URL." u"An internal exception occurred while generating a download URL."
...@@ -52,15 +45,13 @@ class Backend(BaseBackend): ...@@ -52,15 +45,13 @@ class Backend(BaseBackend):
def remove_file(self, key): def remove_file(self, key):
bucket_name, key_name = self._retrieve_parameters(key) bucket_name, key_name = self._retrieve_parameters(key)
client = _connect_to_s3() conn = _connect_to_s3()
resp = client.delete_objects( bucket = conn.get_bucket(bucket_name)
Bucket=bucket_name, s3_key = bucket.get_key(key_name)
Delete={ if s3_key:
'Objects': [{'Key':key_name}] bucket.delete_key(s3_key)
}
)
if 'Deleted' in resp and any(key_name == deleted_dict['Key'] for deleted_dict in resp['Deleted']):
return True return True
else:
return False return False
...@@ -76,8 +67,7 @@ def _connect_to_s3(): ...@@ -76,8 +67,7 @@ def _connect_to_s3():
aws_access_key_id = getattr(settings, 'AWS_ACCESS_KEY_ID', None) aws_access_key_id = getattr(settings, 'AWS_ACCESS_KEY_ID', None)
aws_secret_access_key = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None) aws_secret_access_key = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None)
return boto3.client( return boto.connect_s3(
's3',
aws_access_key_id=aws_access_key_id, aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key aws_secret_access_key=aws_secret_access_key
) )
...@@ -7,7 +7,8 @@ import tempfile ...@@ -7,7 +7,8 @@ import tempfile
import urllib import urllib
from urlparse import urlparse from urlparse import urlparse
import boto3 import boto
from boto.s3.key import Key
import ddt import ddt
from mock import Mock, patch from mock import Mock, patch
from moto import mock_s3 from moto import mock_s3
...@@ -35,8 +36,8 @@ class TestFileUploadService(TestCase): ...@@ -35,8 +36,8 @@ class TestFileUploadService(TestCase):
FILE_UPLOAD_STORAGE_BUCKET_NAME="mybucket" FILE_UPLOAD_STORAGE_BUCKET_NAME="mybucket"
) )
def test_get_upload_url(self): def test_get_upload_url(self):
s3 = boto3.resource('s3') conn = boto.connect_s3()
s3.create_bucket(Bucket='mybucket') conn.create_bucket('mybucket')
uploadUrl = api.get_upload_url("foo", "bar") uploadUrl = api.get_upload_url("foo", "bar")
self.assertIn("https://mybucket.s3.amazonaws.com/submissions_attachments/foo", uploadUrl) self.assertIn("https://mybucket.s3.amazonaws.com/submissions_attachments/foo", uploadUrl)
...@@ -47,9 +48,11 @@ class TestFileUploadService(TestCase): ...@@ -47,9 +48,11 @@ class TestFileUploadService(TestCase):
FILE_UPLOAD_STORAGE_BUCKET_NAME="mybucket" FILE_UPLOAD_STORAGE_BUCKET_NAME="mybucket"
) )
def test_get_download_url(self): def test_get_download_url(self):
s3 = boto3.resource('s3') conn = boto.connect_s3()
s3.create_bucket(Bucket='mybucket') bucket = conn.create_bucket('mybucket')
s3.Object('mybucket', 'submissions_attachments/foo').put(Body="How d'ya do?") key = Key(bucket)
key.key = "submissions_attachments/foo"
key.set_contents_from_string("How d'ya do?")
downloadUrl = api.get_download_url("foo") downloadUrl = api.get_download_url("foo")
self.assertIn("https://mybucket.s3.amazonaws.com/submissions_attachments/foo", downloadUrl) self.assertIn("https://mybucket.s3.amazonaws.com/submissions_attachments/foo", downloadUrl)
...@@ -60,9 +63,11 @@ class TestFileUploadService(TestCase): ...@@ -60,9 +63,11 @@ class TestFileUploadService(TestCase):
FILE_UPLOAD_STORAGE_BUCKET_NAME="mybucket" FILE_UPLOAD_STORAGE_BUCKET_NAME="mybucket"
) )
def test_remove_file(self): def test_remove_file(self):
s3 = boto3.resource('s3') conn = boto.connect_s3()
s3.create_bucket(Bucket='mybucket') bucket = conn.create_bucket('mybucket')
s3.Object('mybucket', 'submissions_attachments/foo').put(Body="Test") key = Key(bucket)
key.key = "submissions_attachments/foo"
key.set_contents_from_string("Test")
result = api.remove_file("foo") result = api.remove_file("foo")
self.assertTrue(result) self.assertTrue(result)
result = api.remove_file("foo") result = api.remove_file("foo")
...@@ -82,7 +87,7 @@ class TestFileUploadService(TestCase): ...@@ -82,7 +87,7 @@ class TestFileUploadService(TestCase):
AWS_SECRET_ACCESS_KEY='bizbaz', AWS_SECRET_ACCESS_KEY='bizbaz',
FILE_UPLOAD_STORAGE_BUCKET_NAME="mybucket" FILE_UPLOAD_STORAGE_BUCKET_NAME="mybucket"
) )
@patch.object(boto3, 'client') @patch.object(boto, 'connect_s3')
@raises(exceptions.FileUploadInternalError) @raises(exceptions.FileUploadInternalError)
def test_get_upload_url_error(self, mock_s3): def test_get_upload_url_error(self, mock_s3):
mock_s3.side_effect = Exception("Oh noes") mock_s3.side_effect = Exception("Oh noes")
...@@ -94,7 +99,7 @@ class TestFileUploadService(TestCase): ...@@ -94,7 +99,7 @@ class TestFileUploadService(TestCase):
AWS_SECRET_ACCESS_KEY='bizbaz', AWS_SECRET_ACCESS_KEY='bizbaz',
FILE_UPLOAD_STORAGE_BUCKET_NAME="mybucket" FILE_UPLOAD_STORAGE_BUCKET_NAME="mybucket"
) )
@patch.object(boto3, 'client') @patch.object(boto, 'connect_s3')
@raises(exceptions.FileUploadInternalError, mock_s3) @raises(exceptions.FileUploadInternalError, mock_s3)
def test_get_download_url_error(self, mock_s3): def test_get_download_url_error(self, mock_s3):
mock_s3.side_effect = Exception("Oh noes") mock_s3.side_effect = Exception("Oh noes")
......
...@@ -9,7 +9,8 @@ import sys ...@@ -9,7 +9,8 @@ import sys
import tarfile import tarfile
import tempfile import tempfile
import boto3 import boto
from boto.s3.key import Key
from django.conf import settings from django.conf import settings
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
...@@ -137,24 +138,16 @@ class Command(BaseCommand): ...@@ -137,24 +138,16 @@ class Command(BaseCommand):
# environment vars or configuration files instead. # environment vars or configuration files instead.
aws_access_key_id = getattr(settings, 'AWS_ACCESS_KEY_ID', None) aws_access_key_id = getattr(settings, 'AWS_ACCESS_KEY_ID', None)
aws_secret_access_key = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None) aws_secret_access_key = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None)
client = boto3.client( conn = boto.connect_s3(
's3',
aws_access_key_id=aws_access_key_id, aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key aws_secret_access_key=aws_secret_access_key
) )
bucket = client.create_bucket(Bucket=s3_bucket)
bucket = conn.get_bucket(s3_bucket)
key_name = os.path.join(course_id, os.path.split(file_path)[1]) key_name = os.path.join(course_id, os.path.split(file_path)[1])
client.put_object(Bucket=s3_bucket, Key=key_name, Body=open(file_path, 'rb')) key = Key(bucket=bucket, name=key_name)
url = client.generate_presigned_url( key.set_contents_from_filename(file_path)
ExpiresIn=self.URL_EXPIRATION_HOURS * 3600, url = key.generate_url(self.URL_EXPIRATION_HOURS * 3600)
ClientMethod='get_object',
Params={
'Bucket': s3_bucket,
'Key': key_name
},
HttpMethod="GET"
)
# Store the key and url in the history # Store the key and url in the history
self._history.append({'key': key_name, 'url': url}) self._history.append({'key': key_name, 'url': url})
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
""" """
Tests for management command that uploads submission/assessment data. Tests for management command that uploads submission/assessment data.
""" """
from StringIO import StringIO
import tarfile import tarfile
import boto3 import boto
import moto import moto
from openassessment.management.commands import upload_oa_data from openassessment.management.commands import upload_oa_data
...@@ -30,8 +31,8 @@ class UploadDataTest(CacheResetTest): ...@@ -30,8 +31,8 @@ class UploadDataTest(CacheResetTest):
@moto.mock_s3 @moto.mock_s3
def test_upload(self): def test_upload(self):
# Create an S3 bucket using the fake S3 implementation # Create an S3 bucket using the fake S3 implementation
s3 = boto3.resource('s3') conn = boto.connect_s3()
s3.create_bucket(Bucket=self.BUCKET_NAME) conn.create_bucket(self.BUCKET_NAME)
# Create some submissions to ensure that we cover # Create some submissions to ensure that we cover
# the progress indicator code. # the progress indicator code.
...@@ -54,10 +55,12 @@ class UploadDataTest(CacheResetTest): ...@@ -54,10 +55,12 @@ class UploadDataTest(CacheResetTest):
# Retrieve the uploaded file from the fake S3 implementation # Retrieve the uploaded file from the fake S3 implementation
self.assertEqual(len(cmd.history), 1) self.assertEqual(len(cmd.history), 1)
s3.Object(self.BUCKET_NAME, cmd.history[0]['key']).download_file("tmp-test-file.tar.gz") bucket = conn.get_all_buckets()[0]
key = bucket.get_key(cmd.history[0]['key'])
contents = StringIO(key.get_contents_as_string())
# Expect that the contents contain all the expected CSV files # Expect that the contents contain all the expected CSV files
with tarfile.open("tmp-test-file.tar.gz", mode="r:gz") as tar: with tarfile.open(mode="r:gz", fileobj=contents) as tar:
file_sizes = { file_sizes = {
member.name: member.size member.name: member.size
for member in tar.getmembers() for member in tar.getmembers()
...@@ -68,4 +71,4 @@ class UploadDataTest(CacheResetTest): ...@@ -68,4 +71,4 @@ class UploadDataTest(CacheResetTest):
# Expect that we generated a URL for the bucket # Expect that we generated a URL for the bucket
url = cmd.history[0]['url'] url = cmd.history[0]['url']
self.assertIn("https://s3.amazonaws.com/{}".format(self.BUCKET_NAME), url) self.assertIn("https://{}".format(self.BUCKET_NAME), url)
...@@ -6,7 +6,8 @@ import json ...@@ -6,7 +6,8 @@ import json
from random import randint from random import randint
from urlparse import urlparse from urlparse import urlparse
import boto3 import boto
from boto.s3.key import Key
import mock import mock
from moto import mock_s3 from moto import mock_s3
...@@ -146,15 +147,15 @@ class TestLeaderboardRender(XBlockHandlerTransactionTestCase): ...@@ -146,15 +147,15 @@ class TestLeaderboardRender(XBlockHandlerTransactionTestCase):
@scenario('data/leaderboard_show.xml') @scenario('data/leaderboard_show.xml')
def test_non_text_submission(self, xblock): def test_non_text_submission(self, xblock):
# Create a mock bucket # Create a mock bucket
s3 = boto3.resource('s3') conn = boto.connect_s3()
s3.create_bucket(Bucket='mybucket') bucket = conn.create_bucket('mybucket')
# Create a non-text submission (the submission dict doesn't contain 'text') # Create a non-text submission (the submission dict doesn't contain 'text')
file_download_url = api.get_download_url('s3key') file_download_url = api.get_download_url('s3key')
self._create_submissions_and_scores(xblock, [('s3key', 1)], submission_key='file_key') self._create_submissions_and_scores(xblock, [('s3key', 1)], submission_key='file_key')
# Expect that we default to an empty string for content # Expect that we default to an empty string for content
self._assert_scores(xblock, [ self._assert_scores(xblock, [
{'score': 1, 'files': [(file_download_url, '')], 'submission': ''} {'score': 1, 'files': [], 'submission': ''}
]) ])
@mock_s3 @mock_s3
...@@ -171,10 +172,11 @@ class TestLeaderboardRender(XBlockHandlerTransactionTestCase): ...@@ -171,10 +172,11 @@ class TestLeaderboardRender(XBlockHandlerTransactionTestCase):
file_keys = ['foo', 'bar'] file_keys = ['foo', 'bar']
file_descriptions = ['{}-description'.format(file_key) for file_key in file_keys] file_descriptions = ['{}-description'.format(file_key) for file_key in file_keys]
s3 = boto3.resource('s3') conn = boto.connect_s3()
s3.create_bucket(Bucket='mybucket') bucket = conn.create_bucket('mybucket')
for file_key in file_keys: for file_key in file_keys:
s3.Object('mybucket', 'submissions_attachments/{}'.format(file_key)).put(Body="How d'ya do?") key = Key(bucket, 'submissions_attachments/{}'.format(file_key))
key.set_contents_from_string("How d'ya do?")
files_url_and_description = [ files_url_and_description = [
(api.get_download_url(file_key), file_descriptions[idx]) (api.get_download_url(file_key), file_descriptions[idx])
for idx, file_key in enumerate(file_keys) for idx, file_key in enumerate(file_keys)
...@@ -210,9 +212,10 @@ class TestLeaderboardRender(XBlockHandlerTransactionTestCase): ...@@ -210,9 +212,10 @@ class TestLeaderboardRender(XBlockHandlerTransactionTestCase):
Tests that text and image submission works as expected Tests that text and image submission works as expected
""" """
# Create a file and get the download URL # Create a file and get the download URL
s3 = boto3.resource('s3') conn = boto.connect_s3()
s3.create_bucket(Bucket='mybucket') bucket = conn.create_bucket('mybucket')
s3.Object('mybucket', 'submissions_attachments/foo').put(Body="How d'ya do?") key = Key(bucket, 'submissions_attachments/foo')
key.set_contents_from_string("How d'ya do?")
file_download_url = [(api.get_download_url('foo'), '')] file_download_url = [(api.get_download_url('foo'), '')]
# Create a image and text submission # Create a image and text submission
......
...@@ -6,7 +6,8 @@ Test submission to the OpenAssessment XBlock. ...@@ -6,7 +6,8 @@ Test submission to the OpenAssessment XBlock.
import datetime as dt import datetime as dt
import json import json
import boto3 import boto
from boto.s3.key import Key
from mock import Mock, patch from mock import Mock, patch
from moto import mock_s3 from moto import mock_s3
import pytz import pytz
...@@ -157,14 +158,11 @@ class SubmissionTest(XBlockHandlerTestCase): ...@@ -157,14 +158,11 @@ class SubmissionTest(XBlockHandlerTestCase):
@scenario('data/file_upload_scenario.xml') @scenario('data/file_upload_scenario.xml')
def test_download_url(self, xblock): def test_download_url(self, xblock):
""" Test generate correct download URL with existing file. should create a file and get the download URL """ """ Test generate correct download URL with existing file. should create a file and get the download URL """
s3 = boto3.resource('s3') conn = boto.connect_s3()
s3.create_bucket(Bucket='mybucket') bucket = conn.create_bucket('mybucket')
s3.Object( key = Key(bucket)
'mybucket', key.key = "submissions_attachments/test_student/test_course/" + xblock.scope_ids.usage_id
'submissions_attachments/test_student/test_course/' + xblock.scope_ids.usage_id key.set_contents_from_string("How d'ya do?")
).put(
Body="How d'ya do?"
)
download_url = api.get_download_url("test_student/test_course/" + xblock.scope_ids.usage_id) download_url = api.get_download_url("test_student/test_course/" + xblock.scope_ids.usage_id)
xblock.xmodule_runtime = Mock( xblock.xmodule_runtime = Mock(
...@@ -189,7 +187,7 @@ class SubmissionTest(XBlockHandlerTestCase): ...@@ -189,7 +187,7 @@ class SubmissionTest(XBlockHandlerTestCase):
resp = self.request(xblock, 'download_url', json.dumps(dict()), response_format='json') resp = self.request(xblock, 'download_url', json.dumps(dict()), response_format='json')
self.assertTrue(resp['success']) self.assertTrue(resp['success'])
self.assertIn(u'https://mybucket.s3.amazonaws.com/submissions_attachments', resp['url']) self.assertEqual(u'', resp['url'])
@mock_s3 @mock_s3
@override_settings( @override_settings(
...@@ -200,11 +198,11 @@ class SubmissionTest(XBlockHandlerTestCase): ...@@ -200,11 +198,11 @@ class SubmissionTest(XBlockHandlerTestCase):
@scenario('data/file_upload_scenario.xml') @scenario('data/file_upload_scenario.xml')
def test_remove_all_uploaded_files(self, xblock): def test_remove_all_uploaded_files(self, xblock):
""" Test remove all user files """ """ Test remove all user files """
s3 = boto3.resource('s3') conn = boto.connect_s3()
s3.create_bucket(Bucket='mybucket') bucket = conn.create_bucket('mybucket')
s3.Object('mybucket', 'submissions_attachments/test_student/test_course/' + xblock.scope_ids.usage_id).put( key = Key(bucket)
Body="How d'ya do?" key.key = "submissions_attachments/test_student/test_course/" + xblock.scope_ids.usage_id
) key.set_contents_from_string("How d'ya do?")
xblock.xmodule_runtime = Mock( xblock.xmodule_runtime = Mock(
course_id='test_course', course_id='test_course',
...@@ -221,7 +219,7 @@ class SubmissionTest(XBlockHandlerTestCase): ...@@ -221,7 +219,7 @@ class SubmissionTest(XBlockHandlerTestCase):
resp = self.request(xblock, 'download_url', json.dumps(dict()), response_format='json') resp = self.request(xblock, 'download_url', json.dumps(dict()), response_format='json')
self.assertTrue(resp['success']) self.assertTrue(resp['success'])
self.assertIn(u'https://mybucket.s3.amazonaws.com/submissions_attachments', resp['url']) self.assertEqual(u'', resp['url'])
@mock_s3 @mock_s3
@override_settings( @override_settings(
......
...@@ -5,7 +5,7 @@ git+https://github.com/edx/XBlock.git@xblock-1.0.1#egg=XBlock==1.0.1 ...@@ -5,7 +5,7 @@ git+https://github.com/edx/XBlock.git@xblock-1.0.1#egg=XBlock==1.0.1
git+https://github.com/edx/xblock-sdk.git@v0.1.4#egg=xblock-sdk==0.1.4 git+https://github.com/edx/xblock-sdk.git@v0.1.4#egg=xblock-sdk==0.1.4
# Third Party Requirements # Third Party Requirements
boto3>=1.4.4,<2.0.0 boto>=2.48.0,<3.0.0
python-swiftclient>=3.1.0,<4.0.0 python-swiftclient>=3.1.0,<4.0.0
defusedxml>=0.4.1,<1.0.0 defusedxml>=0.4.1,<1.0.0
django-model-utils>=2.3.1 django-model-utils>=2.3.1
......
...@@ -5,7 +5,7 @@ ddt==1.0.0 ...@@ -5,7 +5,7 @@ ddt==1.0.0
factory_boy==2.8.1 factory_boy==2.8.1
freezegun==0.3.9 freezegun==0.3.9
mock==2.0.0 mock==2.0.0
moto==1.0.1 moto==0.4.31
nose==1.3.7 nose==1.3.7
tox==2.7.0 tox==2.7.0
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment