-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
29 changed files
with
733 additions
and
173 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from portia_api.utils.download import ProjectArchiver | ||
|
||
|
||
class BaseDeploy(object): | ||
def __init__(self, project): | ||
self.project = project | ||
self.storage = project.storage | ||
self.config = self._get_config() | ||
self.config.version = self.project.version | ||
|
||
def build_archive(self): | ||
return ProjectArchiver(self.storage, project=self.project).archive( | ||
egg_info=True) | ||
|
||
def _get_config(self): | ||
raise NotImplementedError | ||
|
||
def deploy(self, target=None): | ||
raise NotImplementedError | ||
|
||
def schedule(self, spider, args=None, settings=None, target=None): | ||
raise NotImplementedError |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import os | ||
import textwrap | ||
import zipfile | ||
|
||
from datetime import datetime | ||
from distutils.dist import DistributionMetadata | ||
from io import StringIO | ||
|
||
|
||
class EggInfo(object): | ||
def __init__(self, project, archive): | ||
self.project = project | ||
self.archive = archive | ||
self.tstamp = datetime.now().timetuple()[:6] | ||
|
||
def write(self): | ||
self._write_file('PKG-INFO', self.build_pkg_info()) | ||
self._write_file('SOURCES.txt', self.build_sources()) | ||
self._write_file('dependency_links.txt', self.build_dependency()) | ||
self._write_file('entry_points.txt', self.build_entry_points()) | ||
self._write_file('top_level.txt', self.build_top_level()) | ||
self._write_file('zip-safe', self.build_zip_safe()) | ||
|
||
def _write_file(self, filename, contents): | ||
filepath = os.path.join('EGG-INFO', filename) | ||
fileinfo = zipfile.ZipInfo(filepath, self.tstamp) | ||
fileinfo.external_attr = 0o666 << 16 | ||
self.archive.writestr(fileinfo, contents, zipfile.ZIP_DEFLATED) | ||
|
||
def build_pkg_info(self): | ||
meta = DistributionMetadata() | ||
meta.name = self.project.name | ||
meta.version = self.project.version | ||
file = StringIO() | ||
meta.write_pkg_file(file) | ||
file.seek(0) | ||
return file.read() | ||
|
||
def build_sources(self): | ||
return '\n'.join(sorted(f.filename for f in self.archive.filelist)) | ||
|
||
def build_top_level(self): | ||
return '\n'.join(sorted({ | ||
fn.split('/', 1)[0] for fn in ( | ||
fn for fn in ( | ||
f.filename for f in self.archive.filelist)) | ||
if fn.endswith('.py') | ||
})) | ||
|
||
def build_dependency(self): | ||
return '\n' | ||
|
||
def build_entry_points(self): | ||
return textwrap.dedent("""\ | ||
[scrapy] | ||
settings = spiders.settings | ||
""") | ||
|
||
def build_zip_safe(self): | ||
return '' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import json | ||
import os | ||
|
||
from six import StringIO | ||
from urllib.parse import urljoin | ||
|
||
from django.conf import settings as app_settings | ||
from rest_framework import status | ||
from shub import exceptions | ||
from shub.config import ShubConfig | ||
from shub.schedule import schedule_spider | ||
from shub.utils import make_deploy_request | ||
from portia_api.jsonapi.exceptions import JsonApiGeneralException | ||
from storage.projecttemplates import templates | ||
|
||
from .base import BaseDeploy | ||
|
||
|
||
class ScrapinghubDeploy(BaseDeploy): | ||
SHUB_DOCS_URL = 'https://shub.readthedocs.io/en/stable/configuration.html' | ||
EXCEPTIONS = ( | ||
exceptions.InvalidAuthException, # EX_NOPERM | ||
exceptions.RemoteErrorException, # EX_PROTOCOL | ||
) | ||
STATUS_CODES = { | ||
os.EX_UNAVAILABLE: status.HTTP_404_NOT_FOUND, | ||
os.EX_PROTOCOL: status.HTTP_503_SERVICE_UNAVAILABLE, | ||
} | ||
|
||
def _get_config(self): | ||
conf = ShubConfig() | ||
conf.load(StringIO(json.dumps(self._default_config()))) | ||
if 'SHUB_APIKEY' in os.environ: | ||
conf.apikeys['default'] = os.environ['SHUB_APIKEY'] | ||
try: | ||
conf.load(self.storage.open('scrapinghub.yml')) | ||
except OSError: | ||
raise ('Need a `scrapinghub.yml` file to identify which project ' | ||
'to deploy to. Find more information at: {}'.format( | ||
self.SHUB_DOCS_URL | ||
)) | ||
return conf | ||
|
||
def _default_config(self): | ||
config = { | ||
'stack': 'scrapy:1.5-py-latest', | ||
} | ||
if getattr(app_settings, 'SCRAPINGHUB_APIKEY', None): | ||
config['apikeys'] = { | ||
'default': app_settings.SCRAPINGHUB_APIKEY, | ||
} | ||
return config | ||
|
||
def deploy(self, target='default'): | ||
try: | ||
conf = self.config.get_target_conf(target) | ||
archive = self.build_archive() | ||
data = { | ||
'project': conf.project_id, | ||
'version': self.project.version, | ||
'stack': conf.stack | ||
} | ||
files = [('egg', archive)] | ||
if conf.requirements_file: | ||
try: | ||
file = self.storage.open(conf.requirements_file) | ||
except OSError: | ||
file = StringIO(templates['REQUIREMENTS']) | ||
files.append(('requirements', file)) | ||
make_deploy_request( | ||
urljoin(conf.endpoint, 'scrapyd/addversion.json'), | ||
data, files, (conf.apikey, ''), False, False) | ||
except self.EXCEPTIONS as e: | ||
raise JsonApiGeneralException( | ||
e.format_message(), | ||
self.STATUS_CODES.get(getattr(e, 'exit_code', None), 500), | ||
) | ||
return { | ||
'message': 'Your deploy completed successfully', | ||
} | ||
|
||
def schedule(self, spider, args=None, settings=None, target='default'): | ||
try: | ||
conf = self.config.get_target_conf(target) | ||
schedule_spider( | ||
conf.project_id, conf.endpoint, conf.apikey, spider, | ||
arguments=args or (), settings=settings or ()) | ||
except self.EXCEPTIONS as e: | ||
raise JsonApiGeneralException( | ||
e.format_message(), | ||
self.STATUS_CODES.get(getattr(e, 'exit_code', None), 500), | ||
) |
Oops, something went wrong.