diff --git a/services/capability/MANIFEST.in b/services/capability/MANIFEST.in new file mode 100644 index 0000000000000000000000000000000000000000..5e84c8812c023a08f88d019026c11f34ebcde103 --- /dev/null +++ b/services/capability/MANIFEST.in @@ -0,0 +1,5 @@ +include *.txt *.ini *.cfg *.rst *.wsgi *.sh *.spec *.md *.py +recursive-include services *.ico *.png *.svg *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml *.ttf *.woff *.woff2 +include MANIFEST.in +include README.txt +include workflow/* diff --git a/services/capability/README.md b/services/capability/README.md index fad90cd376db7b0709b134c6e0e55e84d9134a53..e0dc6f31b9f24903492a0c38a73e95f8b4d94082 100644 --- a/services/capability/README.md +++ b/services/capability/README.md @@ -1 +1,15 @@ -# Workspaces Capability Service +# Capability: The Workspaces Capability Service + +To use, first run the Alembic migrations in /schema: + + env CAPO_PROFILE=local alembic upgrade head +Then run: + `python setup.py develop` + +Then you should be able to run this with: + + env CAPO_PROFILE=local pserve --reload development.ini + +Once there, try the following URLs: + +- http://0.0.0.0:3456/capabilities diff --git a/services/capability/capability.wsgi b/services/capability/capability.wsgi new file mode 100644 index 0000000000000000000000000000000000000000..480a8b8b37ead924c75df26f74ef11832bba44b3 --- /dev/null +++ b/services/capability/capability.wsgi @@ -0,0 +1,22 @@ +# workflow.wsgi +from pyramid.paster import get_app, setup_logging +import os + +import socket + +fqdn = socket.getfqdn() +if "shipman" in fqdn: + os.environ['CAPO_PROFILE'] = 'dsoc-prod' +elif "wirth" in fqdn: + os.environ['CAPO_PROFILE'] = 'dsoc-dev' +elif "hamilton" in fqdn: + os.environ['CAPO_PROFILE'] = 'dsoc-test' +else: + os.environ['CAPO_PROFILE'] = 'local' + +# os.environ['CAPO_PROFILE'] = 'products' + +ini_path = os.path.dirname(__file__) + '/production.ini' + +setup_logging(ini_path) +application = get_app(ini_path, 'main') diff --git a/services/capability/development.ini b/services/capability/development.ini new file mode 100644 index 0000000000000000000000000000000000000000..41d5e207f30a8d008bec566559bd0b40cfa679e1 --- /dev/null +++ b/services/capability/development.ini @@ -0,0 +1,49 @@ +[app:main] +use = egg:ssa-capability +pyramid.includes = + pyramid_debugtoolbar + pyramid_tm +pyramid.reload_all = true + +session.cookie_expires = true +session.auto = true + +[server:main] +use = egg:waitress#main +listen = 0.0.0.0:3456 + + +[loggers] +keys = root, workflow + +[handlers] +keys = console, filelog + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_workflow] +level = DEBUG +handlers = +qualname = workflow + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[handler_filelog] +class = logging.handlers.TimedRotatingFileHandler +args = ('%(here)s/workflow.log','midnight',1,3) +level = INFO +formatter = generic +# When using the TimedRotatingFileHandler, we probably should set up the rollover time +# and other aspects of the log to strike the balance between disk usage and log history + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s diff --git a/services/capability/production.ini b/services/capability/production.ini new file mode 100644 index 0000000000000000000000000000000000000000..aacbb031a1b079c39f5091f9f06ff51770ba64dc --- /dev/null +++ b/services/capability/production.ini @@ -0,0 +1,22 @@ +### +# app configuration +# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html +### + +[app:main] +use = egg:ssa-capability + +session.cookie_expires = true +session.auto = true + +pyramid.reload_templates = false +pyramid.debug_authorization = false +pyramid.debug_notfound = false +pyramid.debug_routematch = false +pyramid.default_locale_name = en +pyramid.includes = pyramid_tm + + +[server:main] +use = egg:waitress#main +listen = 0.0.0.0:6543 diff --git a/services/capability/setup.py b/services/capability/setup.py index ed5bf57ae0fff973c049846324aed4ecef631aeb..9576cb2ecc84ebe90e9a5675e5c46de7a67e1aaf 100644 --- a/services/capability/setup.py +++ b/services/capability/setup.py @@ -1,35 +1,104 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- +"""A setuptools based setup module. -from pathlib import Path -from setuptools import setup +See: +https://packaging.python.org/en/latest/distributing.html +https://github.com/pypa/sampleproject +""" +# To use a consistent encoding +from codecs import open +from os import path + +# Always prefer setuptools over distutils +from setuptools import setup, find_packages + +# For matching the version string. +import re + +this_module = 'capability' +here = path.abspath(path.dirname(__file__)) + +# Get the long description from the README file +with open(path.join(here, 'README.md'), encoding='utf-8') as f: + long_description = f.read() + + +def read(*parts): + with open(path.join(here, *parts), 'r') as fp: + return fp.read() + + +def find_version(*file_paths): + version_file = read(*file_paths) + version_match = re.search(r"^___version___ = ['\"]([^'\"]*)['\"]", + version_file, re.M) + if version_match: + return version_match.group(1) + raise RuntimeError("Unable to find version string.") -VERSION = open('src/capability/_version.py').readlines()[-1].split()[-1].strip("\"'") -README = Path('README.md').read_text() requires = [ + 'pycapo', + 'pyramid', + 'pyramid_beaker', + 'pyramid_debugtoolbar', + 'pyramid_tm', + 'requests', + 'ssa-schema', + 'sqlalchemy', + 'waitress', 'ssa-workspaces', + 'zope.sqlalchemy' ] -# tests_require = [] setup( - name='ssa-' + Path().absolute().name, - version=VERSION, - description='The Workspaces capability service', - long_description=README, - author='NRAO SSA Team', - author_email='dms-ssa@nrao.edu', - url='TBD', - license="GPL", - install_requires=requires, - # tests_require=tests_require, - keywords=[], - packages=['capability'], - package_dir={'':'src'}, + name='ssa-' + this_module, + + # Versions should comply with PEP440. For a discussion on single-sourcing + # the version across setup.py and the project code, see + # https://packaging.python.org/en/latest/single_source_version.html + version=find_version('src/' + this_module, '_version.py'), + + description='Capability: the Workspaces Capability Service', + long_description=long_description, + + # Author details + author='Science Support and Archive', + author_email='ssa-announcements@nrao.edu', + + # Choose your license + license='GPL', + + # See https://pypi.python.org/pypi?%3Aaction=list_classifiers classifiers=[ - 'Programming Language :: Python :: 3.8' + # How mature is this project? Common values are + # 3 - Alpha + # 4 - Beta + # 5 - Production/Stable + 'Development Status :: 4 - Beta', + + # Indicate who your project is intended for + 'Intended Audience :: Developers', + 'Topic :: Software Development :: Build Tools', + + # Pick your license as you wish (should match "license" above) + 'License :: OSI Approved :: GPL License', + + # Specify the Python versions you support here. In particular, ensure + # that you indicate whether you support Python 2, Python 3 or both. + 'Programming Language :: Python :: 3.6', ], + + install_requires=requires, + extras_require={ + 'dev': [ + 'pyramid_debugtoolbar', + ], + }, + package_dir={'': 'src'}, + packages=find_packages(), entry_points={ - 'console_scripts': ['cap_launcher = capability.capability_launcher:main'] + 'paste.app_factory': [ + 'main = capability.server:main' + ], }, ) diff --git a/services/capability/src/capability/server.py b/services/capability/src/capability/server.py new file mode 100644 index 0000000000000000000000000000000000000000..f6cc204facb2df0642ec7d7a1578e6c4ead956cf --- /dev/null +++ b/services/capability/src/capability/server.py @@ -0,0 +1,68 @@ +import zope.sqlalchemy +from pyramid.config import Configurator +from pyramid.renderers import JSONP +from pyramid.view import view_config, view_defaults +from pyramid_beaker import session_factory_from_settings +from workspaces.services import WorkflowInfo, WorkflowService, get_session_factory, get_engine, CapabilityInfo, \ + CapabilityService + + +# --------------------------------------------------------- +# +# F R A M E W O R K S E T U P +# +# --------------------------------------------------------- + + +def get_tm_session(session_factory, transaction_manager): + """ + Enable Zope's transaction manager on our session + :param session_factory: + :param transaction_manager: + :return: + """ + dbsession = session_factory() + zope.sqlalchemy.register(dbsession, transaction_manager=transaction_manager) + return dbsession + + +# --------------------------------------------------------- +# +# M A I N E N T R Y P O I N T +# +# --------------------------------------------------------- + + +def main(global_config, **settings): + with Configurator(settings=settings) as config: + session_factory = session_factory_from_settings(settings) + config.set_session_factory(session_factory) + config.add_renderer('jsonp', JSONP(param_name='callback')) + + settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager' + + # use pyramid_tm to hook the transaction lifecycle to the request + config.include('pyramid_tm') + + # use pyramid_retry to retry a request when transient exceptions occur + config.include('pyramid_retry') + + session_factory = get_session_factory(get_engine()) + config.registry['dbsession_factory'] = session_factory + + # make workflow_info available for use in Pyramid + config.add_request_method( + # r.tm is the transaction manager used by pyramid_tm + lambda request: CapabilityInfo(get_tm_session(session_factory, request.tm)), + 'info', + reify=True + ) + # make workflow_service available for use in Pyramid + config.add_request_method(lambda r: CapabilityService(r.info), 'capabilities', reify=True) + + # add some routes + # config.add_route('workflows', '/workflows') + + config.include('pyramid_beaker') + config.scan('.') + return config.make_wsgi_app() diff --git a/services/workflow/development.ini b/services/workflow/development.ini index 005980125efd6aa0ffd4627d40140cf53e7c14cf..9f23a0f81745a0d687383178850abcca3e8d14ab 100644 --- a/services/workflow/development.ini +++ b/services/workflow/development.ini @@ -1,5 +1,5 @@ [app:main] -use = egg:workflow +use = egg:ssa-workflow pyramid.includes = pyramid_debugtoolbar pyramid_tm diff --git a/services/workflow/production.ini b/services/workflow/production.ini index d1430a9b8c25dff1eeacadaebf6bd3869bb15e5d..1dc1e17b2fdb2a2b53c9614ac896fb588d7bd8b2 100644 --- a/services/workflow/production.ini +++ b/services/workflow/production.ini @@ -4,7 +4,7 @@ ### [app:main] -use = egg:workflow +use = egg:ssa-workflow session.cookie_expires = true session.auto = true diff --git a/services/workflow/setup.py b/services/workflow/setup.py index 2d6903af4d7ae9b574a764a9da31193060abc997..90e6c715e908309fb9c5eb4593bba69684d44621 100644 --- a/services/workflow/setup.py +++ b/services/workflow/setup.py @@ -62,8 +62,8 @@ setup( long_description=long_description, # Author details - author='Daniel Lyons', - author_email='dlyons@nrao.edu', + author='Science Support and Archive', + author_email='ssa-announcements@nrao.edu', # Choose your license license='GPL', diff --git a/services/workflow/workflow.wsgi b/services/workflow/workflow.wsgi index d0452ee043312de9e5f3d2a1a85b1b5dced7392d..480a8b8b37ead924c75df26f74ef11832bba44b3 100644 --- a/services/workflow/workflow.wsgi +++ b/services/workflow/workflow.wsgi @@ -5,11 +5,10 @@ import os import socket fqdn = socket.getfqdn() -if "mcilroy" in fqdn: +if "shipman" in fqdn: os.environ['CAPO_PROFILE'] = 'dsoc-prod' elif "wirth" in fqdn: - os.environ['CAPO_PROFILE'] = 'nmtest' - #os.environ['CAPO_PROFILE'] = 'dsoc-dev' + os.environ['CAPO_PROFILE'] = 'dsoc-dev' elif "hamilton" in fqdn: os.environ['CAPO_PROFILE'] = 'dsoc-test' else: