Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

persistence: add tenant db #26

Merged
merged 5 commits into from
Jun 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ FROM python:3.5
ADD . /code
WORKDIR /code
RUN pip install -e .[all]
RUN adduser --uid 1000 --disabled-password --gecos '' reanauser && \
chown -R reanauser:reanauser /code
USER reanauser
EXPOSE 5000
ENV FLASK_APP reana_workflow_controller/app.py
CMD ["flask", "run", "--host=0.0.0.0"]
CMD flask users create info@reana.io &&\
flask run --host=0.0.0.0
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ include COPYING
include *.rst
include *.sh
include pytest.ini
prune docs/_build
recursive-include docs *.py
recursive-include docs *.png
recursive-include docs *.rst
Expand Down
12 changes: 6 additions & 6 deletions docs/restapi.rst
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
REST API
========

GET /workflows
--------------
GET /api/workflows
------------------

.. autofunction:: reana_workflow_controller.app.get_workflows
.. autofunction:: reana_workflow_controller.rest.get_workflows


POST /yadage
------------
POST /api/yadage
----------------

.. autofunction:: reana_workflow_controller.app.yadage_endpoint
.. autofunction:: reana_workflow_controller.rest.yadage_endpoint


176 changes: 3 additions & 173 deletions reana_workflow_controller/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,178 +20,8 @@
# granted to it by virtue of its status as an Intergovernmental Organization or
# submit itself to any jurisdiction.

"""Rest API endpoint for workflow management."""
"""REANA Workflow Controller Instance."""

from __future__ import absolute_import
from .factory import create_app

import os
import traceback

from flask import Flask, abort, jsonify, redirect, request

from .fsdb import Organization, get_all_workflows
from .tasks import run_yadage_workflow

app = Flask(__name__)
app.config.from_object('reana_workflow_controller.config')
app.secret_key = "super secret key"

experiment_to_queue = {
'alice': 'alice-queue',
'atlas': 'atlas-queue',
'lhcb': 'lhcb-queue',
'cms': 'cms-queue',
'recast': 'recast-queue'
}


@app.route('/workflows', methods=['GET'])
def get_workflows():
"""Get all workflows.

.. http:get:: /workflows

Returns a JSON list with all the workflows.

**Request**:

.. sourcecode:: http

GET /workflows HTTP/1.1
Content-Type: application/json
Host: localhost:5000

:reqheader Content-Type: application/json

**Responses**:

.. sourcecode:: http

HTTP/1.1 200 OK
Content-Length: 22
Content-Type: application/json

{
"workflows": [
{
"id": "256b25f4-4cfb-4684-b7a8-73872ef455a1",
"organization": "default_org",
"status": "running",
"tenant": "default_tenant"
},
{
"id": "3c9b117c-d40a-49e3-a6de-5f89fcada5a3",
"organization": "default_org",
"status": "finished",
"tenant": "default_tenant"
},
{
"id": "72e3ee4f-9cd3-4dc7-906c-24511d9f5ee3",
"organization": "default_org",
"status": "waiting",
"tenant": "default_tenant"
},
{
"id": "c4c0a1a6-beef-46c7-be04-bf4b3beca5a1",
"organization": "default_org",
"status": "waiting",
"tenant": "default_tenant"
}
]
}

:resheader Content-Type: application/json
:statuscode 200: no error - the list has been returned.

.. sourcecode:: http

HTTP/1.1 500 Internal Error
Content-Length: 22
Content-Type: application/json

{
"msg": "Either organization or tenant doesn't exist."
}

:resheader Content-Type: application/json
:statuscode 500: error - the list couldn't be returned.
"""
workflows = []
tenant = 'default_tenant'
try:
for org in Organization:
workflows.extend(get_all_workflows(org, tenant))

return jsonify({"workflows": workflows}), 200
except Exception as e:
return jsonify({"msg": str(e)}), 500


@app.route('/yadage', methods=['POST'])
def yadage_endpoint():
"""Create a new job.

.. http:post:: /yadage

This resource is expecting JSON data with all the necessary
information to run a yadage workflow.

**Request**:

.. sourcecode:: http

POST /yadage HTTP/1.1
Content-Type: application/json
Host: localhost:5000

{
"experiment": "atlas",
"toplevel": "from-github/testing/scriptflow",
"workflow": "workflow.yml",
"nparallel": "100",
"preset_pars": {}
}

:reqheader Content-Type: application/json
:json body: JSON with the information of the yadage workflow.

**Responses**:

.. sourcecode:: http

HTTP/1.0 200 OK
Content-Length: 80
Content-Type: application/json

{
"msg", "Workflow successfully launched",
"workflow_id": "cdcf48b1-c2f3-4693-8230-b066e088c6ac"
}

:resheader Content-Type: application/json
:statuscode 200: no error - the workflow was created
:statuscode 400: invalid request - problably a malformed JSON
"""
if request.method == 'POST':
try:
if request.json:
queue = experiment_to_queue[request.json['experiment']]
resultobject = run_yadage_workflow.apply_async(
args=[request.json],
queue='yadage-{}'.format(queue)
)
if 'redirect' in request.args:
return redirect('{}/{}'.format(
os.environ['YADAGE_MONITOR_URL']),
resultobject.id)
return jsonify({'msg': 'Workflow successfully launched',
'workflow_id': resultobject.id})

except (KeyError, ValueError):
traceback.print_exc()
abort(400)


if __name__ == '__main__':
app.run(debug=True, port=5000,
host='0.0.0.0')
app = create_app()
64 changes: 64 additions & 0 deletions reana_workflow_controller/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
#
# This file is part of REANA.
# Copyright (C) 2017 CERN.
#
# REANA is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# REANA is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# REANA; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
# Suite 330, Boston, MA 02111-1307, USA.
#
# In applying this license, CERN does not waive the privileges and immunities
# granted to it by virtue of its status as an Intergovernmental Organization or
# submit itself to any jurisdiction.

"""REANA Workflow Controller command line interface."""

import click
from flask.cli import with_appcontext
from sqlalchemy import exc

from . import config
from .factory import db
from .fsdb import create_user_space
from .models import User


@click.group()
def users():
"""Record management commands."""


@users.command('create')
@click.argument('email')
@click.option('-o', '--organization', 'organization',
type=click.Choice(config.ORGANIZATIONS),
default='default')
@click.option('-i', '--id', 'id_',
default='00000000-0000-0000-0000-000000000000')
@click.option('-k', '--key', 'key', default='secretkey')
@with_appcontext
def users_create_default(email, organization, id_, key):
"""Create new user."""
try:
user = User(id_=id_, email=email, api_key=key)
db.choose_organization(organization)
db.session.add(user)
db.session.commit()
click.echo(user.id_)
create_user_space(id_, organization)
except exc.IntegrityError:
raise click.BadParameter(
'User with id={0} already exists.'.format(id_),
param_hint='id_',
)
except Exception as e:
click.echo('Something went wrong: {0}'.format(e))
11 changes: 11 additions & 0 deletions reana_workflow_controller/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,14 @@

SHARED_VOLUME_PATH = os.getenv('SHARED_VOLUME_PATH', '/reana')
"""Path to the mounted REANA shared volume."""

SQLALCHEMY_DATABASE_URI_TEMPLATE = 'sqlite:///{path}'.format(
path=os.path.join(SHARED_VOLUME_PATH, 'default/reana.db'))
"""SQLAlchemy database location"""

ORGANIZATIONS = os.getenv('ORGANIZATIONS').split(',') \
if os.getenv('ORGANIZATIONS') else []
"""Organizations."""

SQLALCHEMY_TRACK_MODIFICATIONS = False
"""Track modifications flag."""
51 changes: 51 additions & 0 deletions reana_workflow_controller/factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
#
# This file is part of REANA.
# Copyright (C) 2017 CERN.
#
# REANA is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# REANA is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# REANA; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
# Suite 330, Boston, MA 02111-1307, USA.
#
# In applying this license, CERN does not waive the privileges and immunities
# granted to it by virtue of its status as an Intergovernmental Organization or
# submit itself to any jurisdiction.

"""Rest API endpoint for workflow management."""

from __future__ import absolute_import

from flask import Flask

from .multiorganization import MultiOrganizationSQLAlchemy

# Initialize DB
db = MultiOrganizationSQLAlchemy()

from .models import User # isort:skip # noqa


def create_app():
"""REANA Workflow Controller application factory."""
app = Flask(__name__)
app.config.from_object('reana_workflow_controller.config')
app.secret_key = "super secret key"

# Initialize flask extensions
db.init_app(app)
# Register API routes
from .rest import restapi_blueprint # noqa
app.register_blueprint(restapi_blueprint, url_prefix='/api')
with app.app_context():
db.initialize_dbs()

return app
Loading