Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • ssa/workspaces
1 result
Show changes
Showing
with 599 additions and 143 deletions
# This is nrao:schema
# The purpose of this is just to run the alembic migrations against the database in a docker-compose environment
FROM nrao:workspaces-base
WORKDIR /code/schema
ENV CAPO_PROFILE docker
ENV PATH /opt/conda/envs/data/bin:$PATH
ENV CONDA_DEFAULT_ENV data
ENTRYPOINT sleep 3 && alembic upgrade head
"""convert capability request argument to JSON
Revision ID: 25add553c317
Revises: e50583fc2a8e
Create Date: 2021-03-04 16:55:41.512834
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "25add553c317"
down_revision = "e50583fc2a8e"
branch_labels = None
depends_on = None
def upgrade():
op.alter_column(
"capability_requests",
"parameters",
type_=sa.JSON,
postgresql_using="parameters::json",
)
op.alter_column(
"capability_versions",
"parameters",
type_=sa.JSON,
postgresql_using="parameters::json",
)
def downgrade():
op.alter_column("capability_requests", "parameters", type_=sa.String)
op.alter_column("capability_versions", "parameters", type_=sa.String)
"""update workflow definition for test_download
Revision ID: 50ff97fe0c2a
Revises: 25add553c317
Create Date: 2021-03-04 16:55:57.373412
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "50ff97fe0c2a"
down_revision = "25add553c317"
branch_labels = None
depends_on = None
def upgrade():
op.execute(
"UPDATE capabilities "
"SET capability_steps = 'prepare-and-run-workflow test_download\nawait-workflow' "
"WHERE capability_name = 'test_download'"
)
op.execute("DELETE FROM workflow_templates WHERE workflow_name = 'test_download'")
op.execute(
"INSERT INTO workflow_templates (workflow_name, filename, content) "
"VALUES ('test_download', 'test_download.condor', "
"E'executable = test_download.sh\narguments = {{product_locator}}"
"\nerror = test_download.err\nlog = test_download.log\n\n\nqueue')"
)
op.execute(
"INSERT INTO workflow_templates (workflow_name, filename, content) VALUES "
"('test_download', 'test_download.sh', E'#!/bin/sh\n\ndatafetcher --product-locator $1\ndeliver -r .\n')"
)
def downgrade():
pass
......@@ -5,9 +5,8 @@ Revises: 44d5bbbf2615
Create Date: 2020-10-07 23:01:25.765027
"""
from alembic import op
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "57c38b5f012e"
......@@ -20,19 +19,17 @@ def upgrade():
print("creating capabilities")
op.create_table(
"capabilities",
sa.Column("capability_id", sa.Integer, primary_key=True),
sa.Column("capability_name", sa.String),
sa.Column("capability_name", sa.String, primary_key=True),
sa.Column("capability_steps", sa.String),
sa.Column("max_jobs", sa.Integer),
sa.Column("enabled", sa.Boolean, default=True, server_default="true"),
)
op.create_table(
"capability_requests",
sa.Column("capability_request_id", sa.Integer, primary_key=True),
sa.Column("state", sa.String),
sa.Column(
"capability_id", sa.Integer, sa.ForeignKey("capabilities.capability_id")
),
sa.Column("capability_name", sa.String, sa.ForeignKey("capabilities.capability_name")),
sa.Column("parameters", sa.String),
)
......@@ -62,7 +59,7 @@ def upgrade():
sa.ForeignKey("workflow_requests.workflow_request_id"),
),
sa.ForeignKeyConstraint(
["capability_request_id", "capability_version_number"],
("capability_request_id", "capability_version_number"),
[
"capability_versions.capability_request_id",
"capability_versions.version_number",
......
......@@ -18,7 +18,7 @@ depends_on = None
def upgrade():
op.execute(
"INSERT INTO capabilities (capability_name, capability_steps, max_jobs) "
"VALUES ('test_download', 'prepare-and-run-workflow deliver -r,-l\nawait-workflow', 2)"
"VALUES ('test_download', 'prepare-and-run-workflow deliver [-r, -l]\nawait-workflow', 2)"
)
op.execute("INSERT INTO workflows (workflow_name) VALUES ('test_download')")
op.execute(
......
......@@ -3,17 +3,22 @@ FROM marconi.aoc.nrao.edu/ops/base:workspaces
# Get capability code into the image
WORKDIR /code
COPY ./services/capability ./
COPY ./shared ./src/shared
COPY ./apps/cli ./src/apps/cli
COPY ./testing ./src/testing
WORKDIR /packages/
COPY ./shared ./shared
COPY ./apps/cli ./apps/cli
COPY ./testing ./testing
# Python library installation
WORKDIR /code
RUN pip install -r requirements.txt \
&& python setup.py develop
# Set Capo
ARG capo_env=dev
ARG capo_env=dsoc-dev
ENV CAPO_PROFILE $capo_env
CMD ["pserve", "--reload", "development.ini"]
# This is nrao:capability
FROM marconi.aoc.nrao.edu/ops/base:workspaces
WORKDIR /code/
# Get capability code into the image
WORKDIR /code
COPY ./services/capability ./
COPY ./shared ./src/shared
COPY ./apps/cli ./src/apps/cli
COPY ./testing ./src/testing
WORKDIR /packages/
COPY ./shared ./shared
COPY ./apps/cli ./apps/cli
COPY ./testing ./testing
# Python library installation
WORKDIR /code/
RUN pip install -r requirements.txt \
&& python setup.py develop
......
[paths]
source =
./services/capability/
/code/
\ No newline at end of file
......@@ -14,9 +14,7 @@ skip_arg="--skip-empty"
# Install testing requirements
init () {
cd src/testing || exit
pip install -r requirements.txt
cd /code || exit
pip install -r /packages/testing/requirements.txt
# if exists, remove old .coverage file
if [[ -e .coverage ]]; then
......@@ -28,27 +26,31 @@ init () {
run_tests () {
# Look for lines in requirements.txt starting with "-e"
# to find paths to packages containings tests to be executed.
pkgs=($(sed -n "s|-e ./||p" requirements.txt | tr "\n" " "))
pkgs=($(sed -n "s|-e ..||p" requirements.txt | grep -v testing | tr "\n" " "))
pkgs+=("$path_to_test")
for pkg in "${pkgs[@]}"
do
cd "$pkg" || exit
if [ "$1" == "c" ]; then
pytestWithCoverage
elif [[ $# -eq 0 ]]; then
if [[ $# -eq 0 ]]; then
pytest
else
pytestWithCoverage $1
fi
cd /code/ || exit
done
}
pytestWithCoverage () {
coverage run --parallel-mode -m pytest
cd /code/ && coverage combine --append "$pkg" && cd "$pkg"
coverage run --parallel-mode --branch -m pytest
if [ "$1" == "b" ]; then
cd /code/ && coverage combine --rcfile="/code/bin/config/.coveragerc" --append "$pkg" && cd "$pkg"
else
cd /code/ && coverage combine --append "$pkg" && cd "$pkg"
fi
}
while getopts "cr:o:" OPTION
while getopts "cbr:o:" OPTION
do
case $OPTION in
c)
......@@ -56,6 +58,10 @@ do
run_tests "$OPTION"
coverage report "$skip_arg"
;;
b)
init
run_tests "$OPTION"
;;
r)
echo "The generating coverage report as: $OPTARG"
report_output=$OPTARG
......
from pyramid.config import Configurator
def includeme(config: Configurator):
"""
Function that gets included in server.py:main(); calls all route adding functions
:param config: Pyramid server config object
"""
default_routes(config)
capability_routes(config)
capability_request_routes(config)
def default_routes(config: Configurator):
"""
Uncategorized server routes
:param config: Pyramid server config object
"""
config.add_route("home", "/")
def capability_routes(config: Configurator):
"""
Server routes related to capabilities
:param config: Pyramid server config object
"""
capability_url = "capability/{capability_name}"
# GET
config.add_route(name="view_capability", pattern=f"{capability_url}", request_method="GET")
# POST
config.add_route(
name="create_capability",
pattern="capability/create",
request_method="POST",
)
config.add_route(
name="edit_capability",
pattern=f"{capability_url}",
request_method="POST",
)
config.add_route(
name="enable_capability", pattern=f"{capability_url}/enable", request_method="POST"
)
config.add_route(
name="disable_capability", pattern=f"{capability_url}/disable", request_method="POST"
)
def capability_request_routes(config: Configurator):
"""
Server routes related to capability requests
:param config: Pyramid server config object
"""
request_url = "capability/{capability_name}/request/{request_id}"
# GET
config.add_route("view_capability_request", f"{request_url}", request_method="GET")
# POST
config.add_route(
"create_capability_request",
"capability/{capability_name}/request/create",
request_method="POST",
)
config.add_route(
"edit_capability_request",
f"{request_url}",
request_method="POST",
)
config.add_route("submit_capability_request", f"{request_url}/submit", request_method="POST")
config.add_route("cancel_capability_request", f"{request_url}/cancel", request_method="POST")
# DELETE
config.add_route("delete_capability_request", f"{request_url}", request_method="DELETE")
......@@ -3,8 +3,9 @@ from pyramid.config import Configurator
from pyramid.events import NewRequest
from pyramid.renderers import JSONP
from pyramid.request import Request
from pyramid.view import view_config, view_defaults
from pyramid_beaker import session_factory_from_settings
from pyramid.response import Response
from pyramid_beaker import BeakerSessionFactoryConfig, session_factory_from_settings
from transaction import TransactionManager
from workspaces.capability.services.capability_info import CapabilityInfo
from workspaces.capability.services.capability_service import CapabilityService
......@@ -13,8 +14,22 @@ from workspaces.workflow.services.workflow_service import WorkflowServiceRESTCli
# Copied from here: https://stackoverflow.com/questions/21107057/pyramid-cors-for-ajax-requests
def add_cors_headers_response_callback(event):
def cors_headers(request, response):
def add_cors_headers_response_callback(event: NewRequest):
"""
Event handler that adds CORS HTTP headers to responses from this server; allows external servers
(the front end) to send requests and not have them bounce off
More information about CORS: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
:param event: Server event
"""
def cors_headers(request: Request, response: Response):
"""
Callback function that adds CORS headers to a response
:param response: Server response
"""
response.headers.update(
{
"Access-Control-Allow-Origin": "*",
......@@ -36,33 +51,39 @@ def add_cors_headers_response_callback(event):
# ---------------------------------------------------------
def lookup_request(request):
return request.capability_info.lookup_capability_request(
request.matchdict["request_id"]
)
@view_defaults(route_name="capability_request", renderer="json")
class CapabilityRestService:
def __init__(self, request: Request):
self.request = request
@view_config(request_method="POST", renderer="json")
def create(self):
# create a capability request for this ... request
req = self.request.json_body
request = self.request.capabilities.create_request(req["capability"], req["args"])
return request
def add_services(config: Configurator, session_factory: BeakerSessionFactoryConfig) -> Configurator:
"""
Add capability info, capability service, and workflow service to Pyramid request configuration
@view_config(
request_method="POST", route_name="submit_capability_request", renderer="json"
:param config: Pyramid Configurator object
:param session_factory: Pyramid Beaker session factory
:return: Updated Configurator
"""
# make capability_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)),
"capability_info",
reify=True,
)
# make workflow_info available for use in Pyramid
config.add_request_method(
# r.tm is the transaction manager used by pyramid_tm
lambda request: WorkflowServiceRESTClient(),
"workflow_service",
reify=True,
)
# make capability_service available for use in Pyramid
config.add_request_method(
lambda request: CapabilityService(request.capability_info, request.workflow_service),
"capability_service",
reify=True,
)
def submit(self):
# 1. Submit the request to the service
execution = self.request.capabilities.run_capability(self.request.context)
return config
# 2. Return something we can listen for
return execution
def lookup_request(request):
return request.capability_info.lookup_capability_request(request.matchdict["request_id"])
# ---------------------------------------------------------
......@@ -72,7 +93,9 @@ class CapabilityRestService:
# ---------------------------------------------------------
def get_tm_session(session_factory, transaction_manager):
def get_tm_session(
session_factory: BeakerSessionFactoryConfig, transaction_manager: TransactionManager
):
"""
Enable Zope's transaction manager on our session
:param session_factory:
......@@ -93,7 +116,9 @@ def get_tm_session(session_factory, transaction_manager):
def main(global_config, **settings):
with Configurator(settings=settings) as config:
# Helpers
config.add_subscriber(add_cors_headers_response_callback, NewRequest)
session_factory = session_factory_from_settings(settings)
config.set_session_factory(session_factory)
config.add_renderer("jsonp", JSONP(param_name="callback"))
......@@ -109,35 +134,12 @@ def main(global_config, **settings):
session_factory = get_session_factory(get_engine())
config.registry["dbsession_factory"] = session_factory
# make capability_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)),
"capability_info",
reify=True,
)
# make workflow_info available for use in Pyramid
config.add_request_method(
# r.tm is the transaction manager used by pyramid_tm
lambda request: WorkflowServiceRESTClient(),
"workflow_service",
reify=True,
)
# make capability_service available for use in Pyramid
config.add_request_method(
lambda r: CapabilityService(r.capability_info, r.workflow_service),
"capabilities",
reify=True,
)
# add some routes
config.add_route("capability_request", "/capability/request")
config.add_route(
"submit_capability_request",
"/capability/request/{request_id}/submit",
factory=lookup_request,
)
# Add services to config
config = add_services(config, session_factory)
config.include("pyramid_beaker")
# Include routes from routes file
config.include(".routes")
# config.scan(".views")
config.scan(".")
return config.make_wsgi_app()
"""
.. codeauthor:: Nathan Hertz <nhertz@nrao.edu>
File containing definitions for one half of the capability side of the Workspaces REST API,
concerning capabilities themselves
"""
from pyramid.httpexceptions import (
HTTPBadRequest,
HTTPExpectationFailed,
HTTPNotFound,
HTTPPreconditionFailed,
)
from pyramid.request import Request
from pyramid.response import Response
from pyramid.view import view_config
from workspaces.capability.schema import Capability
@view_config(route_name="view_capability", renderer="json")
def view_capability(request: Request) -> Response:
"""
Pyramid view that accepts a request to view a capability and responds with the requested capability's
info, if it exists
URL: capability/{capability_name}
:param request: GET request
:return: Response with JSON-formatted capability info if found
or 404 response (HTTPNotFound)
"""
capability = request.capability_info.lookup_capability(request.matchdict["capability_name"])
if capability:
return Response(json_body=capability.__json__())
else:
return HTTPNotFound(detail=f"Capability {request.matchdict['capability_name']} not found.")
@view_config(route_name="create_capability", renderer="json")
def create_capability(request: Request) -> Response:
"""
Pyramid view that accepts a request to create a capability
URL: capability/create
:param request: POST request, expecting JSON parameters ["capability_name", "steps", "max_jobs"]
optionally, accepts a boolean "enabled" parameter
:return: Response with JSON-formatted capability info of newly created capability
or 400 response (HTTPBadRequest) if expected parameters not given
or 412 response (HTTPPreconditionFailed) if capability with given name already exists
"""
expected_params = ["capability_name", "steps", "max_jobs"]
params = request.json_body
if not all([expected in params for expected in expected_params]):
# JSON params do not contain all expected params
params_not_given_msg = (
f"Expected JSON parameters {expected_params}. Received only {params}."
)
return HTTPBadRequest(detail=params_not_given_msg)
elif request.capability_info.lookup_capability(params["capability_name"]):
# Capability with given name already exists
already_exists_msg = (
f"Capability {params['capability_name']} already exists.",
f"To instead edit existing capability, use capability/{params['capability_name']}/edit.",
)
return HTTPPreconditionFailed(detail=already_exists_msg)
else:
new_capability = Capability(
name=params["capability_name"],
steps=params["steps"],
max_jobs=params["max_jobs"],
enabled=params.get("enabled", True),
)
request.capability_info.save_entity(new_capability)
return Response(json_body=new_capability.__json__())
@view_config(route_name="edit_capability", renderer="json")
def edit_capability(request: Request) -> Response:
"""
Pyramid view that accepts a request to edit a capability
URL: capability/{capability_name}/edit
:param request: POST request, expecting JSON parameters ["capability_name", "steps", "max_jobs"]
:return: Response with JSON-formatted capability info of newly created capability
or 400 response (HTTPBadRequest) if expected parameters not given
or 412 response (HTTPPreconditionFailed) if capability with given name does not exist
or 417 response (HTTPExpectationFailed) if the capability was unable to be edited
TODO: In the future, we should check if there are any active requests for the capability being edited and
disallow its editing until they are finished
"""
expected_params = ["steps", "max_jobs"]
capability_name = request.matchdict["capability_name"]
params = request.json_body
if not any([expected in params for expected in expected_params]):
# JSON params do not contain any expected params
params_not_given_msg = (
f"Expected one or more JSON parameters {expected_params}. Received only {params}."
)
return HTTPBadRequest(detail=params_not_given_msg)
elif not request.capability_info.lookup_capability(capability_name):
# Capability with given name does not exist
does_not_exist_msg = (
f"Capability {capability_name} does not exist.",
f"To instead create a new capability, use capability/create with the same parameters.",
)
return HTTPPreconditionFailed(detail=does_not_exist_msg)
else:
steps = params.get("steps", None)
max_jobs = params.get("max_jobs", None)
success = request.capability_info.edit_capability(capability_name, steps, max_jobs)
if success:
return Response(body="Capability successfully edited!")
else:
return HTTPExpectationFailed(detail=f"Unable to edit capability {capability_name}.")
@view_config(route_name="enable_capability", renderer="json")
def enable_capability(request: Request) -> Response:
"""
Pyramid view that enables a capability
:param request: POST request
:return: HTTP 200 response
or 412 response (HTTPPreconditionFailed) if capability with given name does not exist
or 417 response (HTTPExpectationFailed) if the capability was unable to be enabled
"""
capability_name = request.matchdict["capability_name"]
if not request.capability_info.lookup_capability(capability_name):
# Capability with given name does not exist
does_not_exist_msg = f"Capability {capability_name} does not exist. Cannot enable."
return HTTPPreconditionFailed(detail=does_not_exist_msg)
else:
success = request.capability_info.edit_capability(capability_name, enabled=True)
if success:
return Response(body="Capability successfully enabled!")
else:
return HTTPExpectationFailed(detail=f"Unable to enable capability {capability_name}.")
@view_config(route_name="disable_capability", renderer="json")
def disable_capability(request: Request) -> Response:
"""
Pyramid view that disables a capability
:param request: POST request
:return: HTTP 200 response
or 412 response (HTTPPreconditionFailed) if capability with given name does not exist
or 417 response (HTTPExpectationFailed) if the capability was unable to be disabled
"""
capability_name = request.matchdict["capability_name"]
if not request.capability_info.lookup_capability(capability_name):
# Capability with given name does not exist
does_not_exist_msg = f"Capability {capability_name} does not exist. Cannot disable."
return HTTPPreconditionFailed(detail=does_not_exist_msg)
else:
success = request.capability_info.edit_capability(capability_name, enabled=False)
if success:
return Response(body="Capability successfully disabled!")
else:
return HTTPExpectationFailed(detail=f"Unable to disable capability {capability_name}.")
"""
.. codeauthor:: Nathan Hertz <nhertz@nrao.edu>
File containing definitions for the other half of the capability side of the Workspaces REST API,
concerning capability requests
"""
from pyramid.httpexceptions import HTTPBadRequest, HTTPNotFound, HTTPPreconditionFailed
from pyramid.request import Request
from pyramid.response import Response
from pyramid.view import view_config
@view_config(route_name="view_capability_request", renderer="json")
def view_capability_request(request: Request) -> Response:
"""
Pyramid view that accepts a request to view a capability request and responds with the request's info, if it exists
URL: capability/{capability_name}/request/{request_id}
:param request: GET request
:return: 200 OK response with JSON-formatted capability request info if found
or 404 response (HTTPNotFound)
"""
capability_request = request.capability_info.lookup_capability_request(
request.matchdict["request_id"]
)
if capability_request:
return Response(json_body=capability_request.__json__())
else:
not_found_msg = (
f"Capability request for capability {request.matchdict['capability_name']}",
f"with ID {request.matchdict['request_id']} not found.",
)
return HTTPNotFound(detail=not_found_msg)
@view_config(route_name="create_capability_request", renderer="json")
def create_capability_request(request: Request) -> Response:
"""
Pyramid view that accepts a request to create a capability request
URL: capability/{capability_name}/request/create
:param request: POST request, expecting JSON parameter "parameters"
:return: 200 OK response with JSON-formatted info of newly created capability request
or 400 response (HTTPBadRequest) if expected parameters not given
or 412 response (HTTPPreconditionFailed) if capability with given name does not exist and thus cannot be
requested
"""
expected_params = ["parameters"]
capability_name = request.matchdict["capability_name"]
params = request.json_body
if not all([expected in params for expected in expected_params]):
# JSON params do not contain all expected params
params_not_given_msg = (
f"Expected JSON parameters {expected_params}. Received only {params}."
)
return HTTPBadRequest(detail=params_not_given_msg)
elif not request.capability_info.lookup_capability(capability_name):
# Capability with given name does not exist; can't create a request for it
does_not_exist_msg = (
f"Capability {capability_name} does not exist. Cannot create request.",
)
return HTTPPreconditionFailed(detail=does_not_exist_msg)
else:
# TODO: Implement future products
new_capability_request = request.capability_service.create_request(
capability_name, parameters=params["parameters"], products=None
)
return Response(json_body=new_capability_request.__json__())
@view_config(route_name="edit_capability_request", renderer="json")
def edit_capability_request(request: Request) -> Response:
"""
Pyramid view that accepts a request to edit a capability request
URL: capability/{capability_name}/request/{request_id}/edit
:param request: POST request
:return Bad Request response, since it's not yet implemented
TODO: Implement once CapabilityVersions are supported
"""
return HTTPBadRequest(detail="Editing capability requests is not yet implemented.")
@view_config(route_name="submit_capability_request", renderer="json")
def submit_capability_request(request: Request) -> Response:
"""
Pyramid view that accepts a request to submit a capability request
URL: capability/{capability_name}/request/{request_id}/submit
:param request: POST request
:return: 200 OK response
or 412 response (HTTPPreconditionFailed) if capability request with given ID does not exist and thus cannot be
submitted
"""
capability_name = request.matchdict["capability_name"]
request_id = request.matchdict["request_id"]
capability_request = request.capability_info.lookup_capability_request(request_id)
if not capability_request:
# Capability request not found
does_not_exist_msg = f"Capability request for {capability_name} with ID {request_id} does not exist. Cannot submit."
return HTTPPreconditionFailed(detail=does_not_exist_msg)
else:
execution = request.capability_service.run_capability(capability_request)
return Response(json_body=execution.__json__())
@view_config(route_name="delete_capability_request", renderer="json")
def delete_capability_request(request: Request) -> Response:
"""
Pyramid view that accepts a request to delete a capability request from the face of the archive
URL: capability/{capability_name}/request/{request_id}
:param request: DELETE request
:return: 200 OK response
or 412 response (HTTPPreconditionFailed) if capability request with given ID does not exist and thus cannot be
deleted
or 400 response (HTTPBadRequest) if capability request with given ID cannot be deleted
"""
capability_name = request.matchdict["capability_name"]
request_id = request.matchdict["request_id"]
capability_request = request.capability_info.lookup_capability_request(request_id)
if not capability_request:
# Capability request not found
does_not_exist_msg = f"Capability request for {capability_name} with ID {request_id} does not exist. Cannot delete."
return HTTPPreconditionFailed(detail=does_not_exist_msg)
else:
rows_deleted = request.capability_info.delete_capability_request(request_id)
if rows_deleted == -1:
bad_request_msg = (
f"Capability request for {capability_name} with ID {request_id} cannot be deleted;",
f"it has existing executions.",
)
return HTTPBadRequest(detail=bad_request_msg)
return Response(
body=f"Capability request for {capability_name} with ID {request_id} successfully deleted."
)
......@@ -4,6 +4,7 @@ pyramid.includes =
pyramid_debugtoolbar
pyramid_tm
pyramid.reload_all = true
pyramid.debug_routematch = true
session.cookie_expires = true
session.auto = true
......@@ -12,7 +13,6 @@ session.auto = true
use = egg:waitress#main
listen = 0.0.0.0:3457
[loggers]
keys = root, capability
......
# This file is intended to support the Dockerfile.base, not for actual development
# DO NOT MODIFY THIS FILE, THIS IS FOR DOCKER ONLY!
-e ./src/shared/schema
-e ./src/shared/channels
-e ./src/shared/workspaces
-e ./src/apps/cli/utilities/wf_monitor
-e ../packages/shared/schema
-e ../packages/shared/channels
-e ../packages/shared/workspaces
-e ../packages/apps/cli/utilities/wf_monitor
-e ../packages/testing
behave == 1.2.6
pycapo == 0.3.0
......@@ -14,6 +15,6 @@ pyramid_debugtoolbar == 4.5
pyramid_retry == 2.1.1
pyramid_tm == 2.2.1
requests == 2.23
sqlalchemy == 1.3
sqlalchemy == 1.3.23
waitress == 1.4
zope.sqlalchemy == 1.1
......@@ -14,94 +14,82 @@ from setuptools import setup, find_packages
# For matching the version string.
import re
this_module = 'capability'
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:
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:
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)
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.")
requires = [
'pycapo',
'pyramid',
'pyramid_beaker',
'pyramid_debugtoolbar',
'pyramid_tm',
'requests',
'ssa-schema',
'sqlalchemy',
'waitress',
'ssa-workspaces',
'zope.sqlalchemy'
"pycapo",
"pyramid",
"pyramid_beaker",
"pyramid_debugtoolbar",
"pyramid_tm",
"requests",
"ssa-schema",
"sqlalchemy",
"waitress",
"ssa-workspaces",
"zope.sqlalchemy",
]
setup(
name='ssa-' + this_module,
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',
version=find_version(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',
author="Science Support and Archive",
author_email="ssa-announcements@nrao.edu",
# Choose your license
license='GPL',
license="GPL",
# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
classifiers=[
# How mature is this project? Common values are
# 3 - Alpha
# 4 - Beta
# 5 - Production/Stable
'Development Status :: 4 - Beta',
"Development Status :: 4 - Beta",
# Indicate who your project is intended for
'Intended Audience :: Developers',
'Topic :: Software Development :: Build Tools',
"Intended Audience :: Developers",
"Topic :: Software Development :: Build Tools",
# Pick your license as you wish (should match "license" above)
'License :: OSI Approved :: GPL License',
"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',
"Programming Language :: Python :: 3.6",
],
install_requires=requires,
tests_require=["ssa-testing"],
extras_require={
'dev': [
'pyramid_debugtoolbar',
"dev": [
"pyramid_debugtoolbar",
],
},
package_dir={'': 'src'},
packages=find_packages(),
entry_points={
'paste.app_factory': [
'main = capability.server:main'
],
'console_scripts': [
'launch_capability = capability.capability_launcher:main'
]
"paste.app_factory": ["main = capability.server:main"],
"console_scripts": ["launch_capability = capability.capability_launcher:main"],
},
)