Commit 7de37a66 authored by Stefano Alberto Russo's avatar Stefano Alberto Russo
Browse files

Merge branch 'feature/binder_compatibility' into develop

parents 721536e2 4b335dfd
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -68,7 +68,7 @@ services:
      - TASK_PROXY_HOST=localhost
      - TASK_TUNNEL_HOST=localhost
      - ROSETTA_HOST=localhost
      - REGISTRY_HOST=proxy     # Use same value as ROSETTA_HOST for production or to use "real" computing resurces 
      - REGISTRY_HOST=proxy:5000     # Use same value as ROSETTA_HOST for production or to use "real" computing resurces 
    ports:
      - "8080:8080"
      - "7000-7020:7000-7020"
+120 −0
Original line number Diff line number Diff line
import os
import uuid
import json
import requests
import subprocess
import base64
from django.conf import settings
@@ -1166,13 +1167,132 @@ def sharable_link_handler(request, short_uuid):
    return redirect(redirect_string)


def get_or_create_container_from_repository(repository_url, repository_tag=None): 
    repository_name = '{}/{}'.format(repository_url.split('/')[-2],repository_url.split('/')[-1])
    
    logger.debug('Called get_or_create_container_from_repository with repository_url="{}" and repository_tag="{}"'.format(repository_url,repository_tag))

    # If building:
    #{"message": "Successfully built 5a2089b2c334\n", "phase": "building"}
    #{"message": "Successfully tagged r2dhttps-3a-2f-2fgithub-2ecom-2fnorvig-2fpytudes5e745c3:latest\n", "phase": "building"}
    
    # If reusing:
    #{"message": "Reusing existing image (r2dhttps-3a-2f-2fgithub-2ecom-2fnorvig-2fpytudes5e745c3), not building."}
    
    # Build the Docker container for this repo
    if repository_tag:
        command = 'sudo jupyter-repo2docker --ref {} --user-id 1000 --user-name rosetta --no-run --json-logs {}'.format(repository_tag, repository_url)
    else:
        command = 'sudo jupyter-repo2docker --user-id 1000 --user-name rosetta --no-run --json-logs {}'.format(repository_url)
    out = os_shell(command, capture=True)
    if out.exit_code != 0:
        logger.error(out.stderr)
        raise ErrorMessage('Something went wrong when creating the Dockerfile for repository "{}"'.format(repository_url))   

    # Convert output to lines
    out_lines = out.stderr.split('\n')

    # Get rep2docker image name from output
    last_line_message = json.loads(out_lines[-1])['message']
    if 'Reusing existing image' in last_line_message:
        repo2docker_image_name = last_line_message.split('(')[1].split(')')[0]
    elif 'Successfully tagged' in last_line_message:
        repo2docker_image_name = last_line_message.split(' ')[2]                
    else:
        raise Exception('Cannot build')

    # Set image registry, name and tag, Use "strip()" as sometimes the newline chars might jump in.
    registry = os.environ.get('REGISTRY_HOST','proxy:5000').strip()
    image_name = repository_name.lower().strip()
    image_tag = repo2docker_image_name[-7:].strip() # The last part of the image name generated by repo2docker is the git short hash

    # Re-tag image taking into account that if we are using the proxy as registry we use localhost or it won't work
    if registry == 'proxy:5000':
        push_registry = 'localhost:5000'
    else:
        push_registry = registry
        
    out = os_shell('sudo docker tag {} {}/{}:{}'.format(repo2docker_image_name,push_registry,image_name,image_tag) , capture=True)
    if out.exit_code != 0:
        logger.error(out.stderr)
        raise ErrorMessage('Something went wrong when tagging the container for repository "{}"'.format(repository_url))   

    # Push image to the (local) registry
    out = os_shell('sudo docker push {}/{}:{}'.format(push_registry,image_name,image_tag) , capture=True)
    if out.exit_code != 0:
        logger.error(out.stderr)
        raise ErrorMessage('Something went wrong when pushing the container for repository "{}"'.format(repository_url))   

    # Create the container if not already existent
    try:
        container = Container.objects.get(registry=registry, image_name=image_name, image_tag=image_tag)
    except Container.DoesNotExist:

        # Set default container name and description
        container_name = repository_name
        container_description = 'Built from {}'.format(repository_url)
            
        # Get name repo name and description from GitHub (if repo is there)
        if repository_url.startswith('https://github.com'):
            try:
                response = requests.get('https://api.github.com/repos/{}'.format(repository_name))
                json_content = json.loads(response.content)
                container_name = json_content['name'].title()
                container_description = json_content['description']
                if not container_description.endswith('.'):
                    container_description+='.'
                container_description += ' Built from {}'.format(repository_url)
            except:
                pass

        container = Container.objects.create(user = None,
                                             name = container_name,
                                             description = container_description,
                                             registry = registry,
                                             image_name = image_name,
                                             image_tag  = image_tag,
                                             image_arch = 'amd64',
                                             image_os = 'linux',
                                             interface_port = '8888',
                                             interface_protocol = 'http',
                                             interface_transport = 'tcp/ip',
                                             supports_custom_interface_port = False,
                                             supports_interface_auth = False)
    return container



#=========================
#  New Binder Task
#=========================

@private_view
def new_binder_task(request, repository):

    # Init data
    data={}
    data['user']  = request.user

    # Convert the Git repository as a Docker container
    logger.debug('Got a new Binder task request for repository "%s"', repository)
        
    # Set repository name/tag/url        
    repository_tag = repository.split('/')[-1]
    repository_url = repository.replace('/'+repository_tag, '')

    container = get_or_create_container_from_repository(repository_url, repository_tag)
    
    # Set the container
    data['task_container'] = container

    # List all computing resources 
    data['computings'] = list(Computing.objects.filter(group=None)) + list(Computing.objects.filter(group__user=request.user))
        
    data['step'] = 'two'
    data['next_step'] = 'three'

    # Render the new task page and handle the rest from there
    return render(request, 'new_task.html', {'data': data})



+4 −0
Original line number Diff line number Diff line
@@ -85,6 +85,10 @@ urlpatterns = [
    path('api/v1/base/agent/', core_app_api.agent_api.as_view(), name='agent_api'),
    path('api/v1/filemanager/', core_app_api.FileManagerAPI.as_view(), name='filemanager_api'),

    # Binder compatibility
    path('v2/git/<path:repository>', core_app_views.new_binder_task),


]


+1 −0
Original line number Diff line number Diff line
@@ -8,3 +8,4 @@ sendgrid==5.3.0
mozilla-django-oidc==1.2.4
uwsgi==2.0.19.1
python-magic==0.4.15
jupyter-repo2docker==2022.2.0