Skip to content

Commit

Permalink
feat: added renku service with cache and datasets
Browse files Browse the repository at this point in the history
  • Loading branch information
jsam committed Nov 22, 2019
1 parent 86fedaf commit 2e33e6f
Show file tree
Hide file tree
Showing 36 changed files with 2,859 additions and 83 deletions.
4 changes: 4 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_DATABASE=0
REDIS_PASSWORD=
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,5 @@ target/
renku-*.bottle.json
renku-*.bottle.tar.gz
renku.rb

.env
2 changes: 1 addition & 1 deletion Dockerfile → Dockerfile.cli
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.6-alpine as base
FROM python:3.7-alpine as base

RUN apk add --no-cache git && \
pip install --no-cache --upgrade pip
Expand Down
18 changes: 18 additions & 0 deletions Dockerfile.svc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM python:3.7-alpine

RUN apk add --update --no-cache alpine-sdk g++ gcc linux-headers libxslt-dev python3-dev build-base openssl-dev libffi-dev git && \
pip install --no-cache --upgrade pip setuptools pipenv requirements-builder

RUN apk add --no-cache --allow-untrusted \
--repository http://dl-cdn.alpinelinux.org/alpine/latest-stable/community \
--repository http://dl-cdn.alpinelinux.org/alpine/latest-stable/main \
--repository http://nl.alpinelinux.org/alpine/edge/community \
git-lfs && \
git lfs install

COPY . /code/renku
WORKDIR /code/renku
RUN requirements-builder -e all --level=pypi setup.py > requirements.txt && pip install -r requirements.txt && pip install -e . && pip install gunicorn


ENTRYPOINT ["gunicorn", "renku.service.entrypoint:app", "-b", "0.0.0.0:8080"]
6 changes: 5 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@
# limitations under the License.

# Check manifest will not automatically add these two files:
include renku/service/.env-example
include .dockerignore
include .editorconfig
include .tx/config
include *.md
prune docs/_build
recursive-include renku *.po *.pot *.mo

recursive-include renku *.py
# added by check_manifest.py
include *.py
include *.yml
include *.rst
include *.sh
include *.txt
Expand All @@ -39,6 +41,7 @@ include babel.ini
include brew.py
include pytest.ini
include snap/snapcraft.yaml
recursive-include renku *.json
recursive-include .github CODEOWNERS
recursive-include .travis *.sh
recursive-include docs *.bat
Expand All @@ -59,3 +62,4 @@ recursive-include renku *.yml
recursive-include renku Dockerfile
recursive-include tests *.py *.gz *.yml
prune .github
prune .env
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,6 @@ brew-commit-bottle: *.bottle.json

brew-release:
open "https://github.com/SwissDataScienceCenter/renku-python/releases/new?tag=v$(shell brew info --json=v1 renku | jq -r '.[0].versions.stable')"

service-container:
docker build -f Dockerfile.svc -t renku-svc .
198 changes: 124 additions & 74 deletions Pipfile.lock

Large diffs are not rendered by default.

176 changes: 176 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,19 @@
import tempfile
import time
import urllib
import uuid
from pathlib import Path

import fakeredis
import pytest
import responses
import yaml
from click.testing import CliRunner

from renku.core.utils.contexts import chdir
from renku.service.cache import ServiceCache
from renku.service.entrypoint import create_app


@pytest.fixture(scope='module')
def renku_path(tmpdir_factory):
Expand Down Expand Up @@ -510,3 +516,173 @@ def remote_project(data_repository, directory_tree):
assert 0 == result.exit_code

yield runner, project_path


@pytest.fixture(scope='function')
def dummy_datapack():
"""Creates dummy data folder."""
temp_dir = tempfile.TemporaryDirectory()

data_file_txt = Path(temp_dir.name) / Path('file.txt')
data_file_txt.write_text('my awesome data')

data_file_csv = Path(temp_dir.name) / Path('file.csv')
data_file_csv.write_text('more,awesome,data')

yield temp_dir


@pytest.fixture(scope='function')
def datapack_zip(dummy_datapack):
"""Returns dummy data folder as a zip archive."""
workspace_dir = tempfile.TemporaryDirectory()
with chdir(workspace_dir.name):
shutil.make_archive('datapack', 'zip', dummy_datapack.name)

yield Path(workspace_dir.name) / 'datapack.zip'


@pytest.fixture(scope='function')
def datapack_tar(dummy_datapack):
"""Returns dummy data folder as a tar archive."""
workspace_dir = tempfile.TemporaryDirectory()
with chdir(workspace_dir.name):
shutil.make_archive('datapack', 'tar', dummy_datapack.name)

yield Path(workspace_dir.name) / 'datapack.tar'


@pytest.fixture(scope='function')
def mock_redis(monkeypatch):
"""Monkey patch service cache with mocked redis."""
with monkeypatch.context() as m:
m.setattr(ServiceCache, 'cache', fakeredis.FakeRedis())
yield


@pytest.fixture(scope='function')
def svc_client(mock_redis):
"""Renku service client."""
flask_app = create_app()

testing_client = flask_app.test_client()
testing_client.testing = True

ctx = flask_app.app_context()
ctx.push()

yield testing_client

ctx.pop()


@pytest.fixture(scope='function')
def svc_client_with_repo(svc_client, mock_redis):
"""Renku service remote repository."""
remote_url = 'https://renkulab.io/gitlab/contact/integration-tests.git'
headers = {
'Content-Type': 'application/json',
'accept': 'application/json',
'Authorization': 'Bearer b4b4de0eda0f471ab82702bd5c367fa7',
}

payload = {
'git_url': remote_url,
'git_username': 'contact',
'git_access_token': 'EcfPJvEqjJepyu6XyqKZ',
}

response = svc_client.post(
'/cache/project-clone',
data=json.dumps(payload),
headers=headers,
)

assert response
assert 'result' in response.json
assert 'error' not in response.json
project_id = response.json['result']['project_id']
assert isinstance(uuid.UUID(project_id), uuid.UUID)

yield svc_client, headers, project_id


@pytest.fixture(
params=[
{
'url': '/cache/files-list',
'allowed_method': 'GET',
'headers': {
'Content-Type': 'application/json',
'accept': 'application/json',
}
},
{
'url': '/cache/files-upload',
'allowed_method': 'POST',
'headers': {}
},
{
'url': '/cache/project-clone',
'allowed_method': 'POST',
'headers': {
'Content-Type': 'application/json',
'accept': 'application/json',
}
},
{
'url': '/cache/project-list',
'allowed_method': 'GET',
'headers': {
'Content-Type': 'application/json',
'accept': 'application/json',
}
},
{
'url': '/datasets/add',
'allowed_method': 'POST',
'headers': {
'Content-Type': 'application/json',
'accept': 'application/json',
}
},
{
'url': '/datasets/create',
'allowed_method': 'POST',
'headers': {
'Content-Type': 'application/json',
'accept': 'application/json',
}
},
{
'url': '/datasets/files-list',
'allowed_method': 'GET',
'headers': {
'Content-Type': 'application/json',
'accept': 'application/json',
}
},
{
'url': '/datasets/list',
'allowed_method': 'GET',
'headers': {
'Content-Type': 'application/json',
'accept': 'application/json',
}
},
]
)
def service_allowed_endpoint(request, svc_client, mock_redis):
"""Ensure allowed methods and correct headers."""
methods = {
'GET': svc_client.get,
'POST': svc_client.post,
'HEAD': svc_client.head,
'PUT': svc_client.put,
'DELETE': svc_client.delete,
'OPTIONS': svc_client.options,
'TRACE': svc_client.trace,
'PATCH': svc_client.patch,
}

yield methods, request.param, svc_client
13 changes: 13 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: '3'

services:
redis:
image: redis:5.0.3-alpine
ports:
- "6379:6379"

renku-svc:
image: renku-svc:latest
env_file: .env
ports:
- "8080:8080"
2 changes: 1 addition & 1 deletion renku/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
option_use_external_storage
from renku.core.commands.version import check_version, print_version
from renku.core.management.client import LocalClient
from renku.core.management.config import ConfigManagerMixin, RENKU_HOME
from renku.core.management.config import RENKU_HOME, ConfigManagerMixin
from renku.core.management.repository import default_path

#: Monkeypatch Click application.
Expand Down
22 changes: 18 additions & 4 deletions renku/core/commands/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import yaml

from renku.core.management import LocalClient
from renku.core.management.config import RENKU_HOME
from renku.core.management.repository import default_path

from .git import get_git_isolation

Expand Down Expand Up @@ -63,8 +65,17 @@ def pass_local_client(
)

def new_func(*args, **kwargs):
ctx = click.get_current_context()
client = ctx.ensure_object(LocalClient)
ctx = click.get_current_context(silent=True)
if not ctx:
client = LocalClient(
path=default_path(),
renku_home=RENKU_HOME,
use_external_storage=True,
)
ctx = click.Context(click.Command(method))
else:
client = ctx.ensure_object(LocalClient)

stack = contextlib.ExitStack()

# Handle --isolation option:
Expand All @@ -85,8 +96,11 @@ def new_func(*args, **kwargs):
if lock or (lock is None and commit):
stack.enter_context(client.lock)

with stack:
result = ctx.invoke(method, client, *args, **kwargs)
result = None
if ctx:
with stack:
result = ctx.invoke(method, client, *args, **kwargs)

return result

return functools.update_wrapper(new_func, method)
5 changes: 4 additions & 1 deletion renku/core/commands/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,12 @@ def add_file(
destination='',
ref=None,
with_metadata=None,
urlscontext=contextlib.nullcontext
urlscontext=contextlib.nullcontext,
use_external_storage=False
):
"""Add data file to a dataset."""
client.use_external_storage = use_external_storage

add_to_dataset(
client, urls, name, link, force, create, sources, destination, ref,
with_metadata, urlscontext
Expand Down
7 changes: 6 additions & 1 deletion renku/core/management/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,18 @@ def default_path():
return '.'


def path_converter(path):
"""Converter for path in PathMixin."""
return Path(path).resolve()


@attr.s
class PathMixin:
"""Define a default path attribute."""

path = attr.ib(
default=default_path,
converter=lambda arg: Path(arg).resolve().absolute(),
converter=path_converter,
)

@path.validator
Expand Down
3 changes: 3 additions & 0 deletions renku/core/utils/contexts.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
@contextlib.contextmanager
def chdir(path):
"""Change the current working directory."""
if isinstance(path, Path):
path = str(path)

cwd = os.getcwd()
os.chdir(path)
try:
Expand Down
6 changes: 6 additions & 0 deletions renku/service/.env-example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_DATABASE=0
REDIS_PASSWORD=

CACHE_DIR=
Loading

0 comments on commit 2e33e6f

Please sign in to comment.