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

1.0.0 #23

Merged
merged 11 commits into from
Feb 19, 2019
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
2 changes: 1 addition & 1 deletion default_file/builder.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/usr/bin/env bash
python3 builder.py pl.json built_pl.json 2> stderr.log
python3 builder.py pl.json processed.json 2> stderr.log
2 changes: 1 addition & 1 deletion default_file/grader.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/usr/bin/env bash
python3 grader.py built_pl.json answers.json evaluated_pl.json feedback.html 2> stderr.log
python3 grader.py pl.json answers.json processed.json feedback.html 2> stderr.log
5 changes: 5 additions & 0 deletions manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,9 @@
"forget to activate a virtual environment?"
)
raise

if sys.argv[1] == "runserver":
sys.argv.append("--noreload")
print("--noreload enforced. You will have to restart the server to apply changes.")

execute_from_command_line(sys.argv)
5 changes: 0 additions & 5 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,3 @@ docker
pytz
requests
timeout-decorator

# The following should be in the dockerfile of exercices
sympy
matplotlib
psycopg2
115 changes: 46 additions & 69 deletions sandbox/container.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
import os
import shutil
import tarfile
import threading
import time

Expand All @@ -12,10 +13,10 @@

CONTAINERS = None


lock = threading.Lock()



def create_container(name):
return docker.from_env().containers.run(
settings.DOCKER_IMAGE,
Expand All @@ -26,7 +27,8 @@ def create_container(name):
cpuset_cpus=settings.DOCKER_CPUSET_CPUS,
mem_limit=settings.DOCKER_MEM_LIMIT,
memswap_limit=settings.DOCKER_MEMSWAP_LIMIT,
name=name,
network_mode="none",
network_disabled=True,
volumes={
os.path.join(settings.DOCKER_VOLUME_HOST, name): {
"bind": settings.DOCKER_VOLUME_CONTAINER,
Expand All @@ -42,6 +44,11 @@ class ContainerWrapper:


def __init__(self, name, index, available=True):
path = os.path.join(settings.DOCKER_VOLUME_HOST, name)
if os.path.isdir(path):
shutil.rmtree(path, ignore_errors=True)
os.makedirs(path)

self.name = name
self.container = create_container(name)
self.index = index
Expand All @@ -52,30 +59,6 @@ def __init__(self, name, index, available=True):
self._get_default_file()


def _reset(self):
"""Reset a given container by ensuring it's killed and overwriting it's instance with a new
one."""
global CONTAINERS

try:
try:
self.container.kill()
except docker.errors.DockerException:
pass

docker.from_env().containers.prune()

cw = ContainerWrapper("c%d" % self.index, self.index)
with lock:
CONTAINERS[self.index] = cw

logger.info(
"Successfully restarted container '%s' of id '%d'" % (self.name, self.index))
except docker.errors.DockerException:
logger.exception(
"Error while restarting container '%s' of id '%d'" % (self.name, self.index))


def _get_default_file(self):
"""Copy every files and directory in DOCKER_DEFAULT_FILES into container environement."""
for item in os.listdir(settings.DOCKER_DEFAULT_FILES):
Expand All @@ -87,23 +70,39 @@ def _get_default_file(self):
shutil.copy2(s, d)


@property
def need_reset(self):
"""Return True if the container need to be reset, False otherwise."""
return self.container.status not in ["running", "restarting", "created"] or self.to_delete
def extract_env(self, envid, suffix, prefix="", test=False):
"""Retrieve the environment from the docker and write it to:
[settings.MEDIA_ROOT]/[prefix][env_id][suffix][ext]

"test_" is added before [prefix] if test is True
An integer (up to 100) can be added before [ext] if the path already exists."""
base = os.path.join(settings.MEDIA_ROOT,
("test_" if test else "") + prefix + envid + suffix)
path = base + ".tgz"

for i in range(1, 100):
if os.path.exists(path):
path = base + str(i) + ".tgz"

with tarfile.open(path, "w|gz") as tar:
for name in os.listdir(self.envpath):
tar.add(os.path.join(self.envpath, name), arcname=name)


@staticmethod
def acquire():
"""Return the first available container, None if none were available."""
global CONTAINERS

with lock:
cw = next((c for c in CONTAINERS if c.available), None)
if cw is not None:
CONTAINERS[cw.index].available = False
CONTAINERS[cw.index].used_since = time.time()
logger.info("Acquiring container '%s' of id '%d'" % (cw.name, cw.index))
lock.acquire()

cw = next((c for c in CONTAINERS if c.available), None)
if cw is not None:
CONTAINERS[cw.index].available = False
CONTAINERS[cw.index].used_since = time.time()
logger.info("Acquiring container '%s' of id '%d'" % (cw.name, cw.index))

lock.release()

return cw

Expand All @@ -122,40 +121,22 @@ def release(self):
with lock:
CONTAINERS[self.index].available = True
logger.info("Releasing container '%s' of id '%d'" % (self.name, self.index))


@classmethod
def refresh_containers(cls):
"""Check that each container are either running, restarting or being created. Reset them if
this is not the case."""
global CONTAINERS

with lock:
for i in range(settings.DOCKER_COUNT):
try:
CONTAINERS[i].reload()
except docker.errors.DockerException:
CONTAINERS[i].to_delete = True

for c in CONTAINERS:
if not c.need_reset:
continue

logger.info("Restarting container '%s' of id '%d'" % (c.name, c.index))
CONTAINERS[c.index].available = False
threading.Thread(target=c._reset).start()



def initialise_container():
"""Called by settings.py to initialize containers at server launch."""
global CONTAINERS

lock.acquire()

time.sleep(0.5)
# Kill stopped container created from DOCKER_IMAGE
logger.info("Purging existing containers using image : %s." % settings.DOCKER_IMAGE)
logger.info("Purging existing stopped containers using image : %s." % settings.DOCKER_IMAGE)
logger.info("Killed stopped container : %s." % str(docker.from_env().containers.prune()))
# Kill running container created from DOCKER_IMAGE

# Deleting running container created from DOCKER_IMAGE
CONTAINERS = []
for c in docker.from_env().containers.list({"ancestor": settings.DOCKER_IMAGE}):
logger.info("Killing container %s." % repr(c))
c.kill()
Expand All @@ -166,15 +147,11 @@ def initialise_container():
if os.path.isdir(settings.DOCKER_VOLUME_HOST):
shutil.rmtree(settings.DOCKER_VOLUME_HOST)

logger.info("Creating new containers environment.")
[os.makedirs(os.path.join(settings.DOCKER_VOLUME_HOST, "c%d" % i)) for i in
range(settings.DOCKER_COUNT)]

# Create containers.
logger.info("Initializing containers.")
with lock:
CONTAINERS = []
for i in range(settings.DOCKER_COUNT):
CONTAINERS.append(ContainerWrapper("c%d" % i, i))
logger.info("Container %d/%d initialized." % (i, settings.DOCKER_COUNT))
for i in range(settings.DOCKER_COUNT):
CONTAINERS.append(ContainerWrapper("c%d" % i, i))
logger.info("Container %d/%d initialized." % (i, settings.DOCKER_COUNT))
logger.info("Containers initialized.")

lock.release()
75 changes: 50 additions & 25 deletions sandbox/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,74 @@ MAINTAINER Quentin COUMES <qcoumes@etud.u-pem.fr>

ENV DEBIAN_FRONTEND noninteractive


# Installing base requirements
RUN apt-get update
RUN apt-get install -y --no-install-recommends\
sudo\
gnupg2\
libasound2\
libasound2-data\
locales\
wget\
build-essential\
libssl-dev\
openssl\
unzip\
python3


# Installing pip
RUN apt-get install -y --no-install-recommends \
sudo \
gnupg2 \
libasound2 \
libasound2-data \
locales \
wget \
ca-certificates \
build-essential \
libssl-dev \
openssl \
unzip \
python3 \
git \
ocaml-nox \
ocaml \
perl \
perl-doc


# Installing pip3 and python's requirements
RUN wget --no-check-certificate https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py
RUN python3 /tmp/get-pip.py
RUN pip3 install jinja2 jsonpickle sympy matplotlib

# Adding sftp lib folder to pythonpath
ENV PYTHONPATH /var/lib/upem:$PYTHONPATH
RUN pip3 install wheel
RUN pip3 install \
jinja2 \
jsonpickle \
sympy \
matplotlib \
psycopg2


# Installing Java 11
RUN wget --no-check-certificate \
RUN wget \
-P /tmp \
--header "Cookie: oraclelicense=accept-securebackup-cookie"\
https://download.oracle.com/otn-pub/java/jdk/11.0.2+7/f51449fcd52f4d52b93a989c5c56ed3c/jdk-11.0.2_linux-x64_bin.deb
RUN dpkg -i jdk-11.0.2_linux-x64_bin.deb
RUN dpkg -i /tmp/jdk-11.0.2_linux-x64_bin.deb
RUN update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk-11.0.2/bin/java 2
RUN update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk-11.0.2/bin/javac 2
RUN mkdir /utils
RUN wget --no-check-certificate \
https://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone/1.4.0-M1/junit-platform-console-standalone-1.4.0-M1.jar\
RUN wget \
https://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone/1.4.0-M1/junit-platform-console-standalone-1.4.0-M1.jar \
-O- > /utils/junit-platform-console-standalone.jar
RUN export JAVA_HOME=/usr/lib/jvm/jdk-11.0.2/


# Installing PL-Java
RUN wget https://github.com/forax/first-language/releases/download/1.0.2/pl-java-jdk-11.tar.gz -P /tmp
RUN tar -xvzf /tmp/pl-java-jdk-11.tar.gz -C /utils/
RUN ln -s /utils/pl-java/bin/pl-java /usr/bin/pl-java
RUN ln -s /utils/pl-java/bin/pl-javac /usr/bin/pl-javac


# Cleaning /tmp
RUN rm -Rf /tmp/*



ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
ENV LC_TYPE en_US.UTF-8
RUN echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
RUN locale-gen "en_US.UTF-8" &&\
dpkg-reconfigure locales
RUN locale-gen "en_US.UTF-8" && dpkg-reconfigure locales

WORKDIR /home/docker

Expand Down
Loading