aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Christopher Baklid <[email protected]>2018-05-21 18:02:07 +0200
committerGravatar Christopher Baklid <[email protected]>2018-05-21 18:02:07 +0200
commit964054baf1e02fb017deec9d2f4cd8b65f19944a (patch)
tree6ce672a025c5f8c59109888f2299b4cfdf21be9b
parentInitial commit (diff)
init commit
-rw-r--r--Pipfile18
-rw-r--r--Pipfile.lock29
-rw-r--r--README.md55
-rw-r--r--docker/Dockerfile23
-rw-r--r--runner/config.py8
-rw-r--r--runner/consume.py68
-rw-r--r--runner/publish.py42
-rw-r--r--runner/test.py21
8 files changed, 264 insertions, 0 deletions
diff --git a/Pipfile b/Pipfile
new file mode 100644
index 0000000..bb59655
--- /dev/null
+++ b/Pipfile
@@ -0,0 +1,18 @@
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+pika = "*"
+
+[dev-packages]
+
+[requires]
+python_version = "3.6"
+
+[scripts]
+consume = "python consume.py"
+publish = "python publish.py"
+build = "docker build -t runner:latest -f docker/Dockerfile ."
+docker = "docker run --network=host runner"
diff --git a/Pipfile.lock b/Pipfile.lock
new file mode 100644
index 0000000..00a32d2
--- /dev/null
+++ b/Pipfile.lock
@@ -0,0 +1,29 @@
+{
+ "_meta": {
+ "hash": {
+ "sha256": "7374d59c23f8adb4d87cd3533f6a7e8f95cc7d7e2e1f5bc1c9dcb49a02459bbd"
+ },
+ "pipfile-spec": 6,
+ "requires": {
+ "python_version": "3.6"
+ },
+ "sources": [
+ {
+ "name": "pypi",
+ "url": "https://pypi.org/simple",
+ "verify_ssl": true
+ }
+ ]
+ },
+ "default": {
+ "pika": {
+ "hashes": [
+ "sha256:15f485eb68ec56b5a2673c01d518d16f7c371809ca42c72a2da42d4d8190fa4f",
+ "sha256:ded1cf12810f909099a3a698cc5adf495b73fd2da1d8f669f8b267664653122d"
+ ],
+ "index": "pypi",
+ "version": "==0.11.2"
+ }
+ },
+ "develop": {}
+}
diff --git a/README.md b/README.md
index ead6200..3fed9cf 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,57 @@
# snekbox
Python sandbox runners for executing code in isolation
+
+# Dependencies
+
+| dep | version (or greater) |
+|--------|----------------------|
+| python | 3.6.5 |
+| pip | 10.0.1 |
+| pipenv | 2018.05.18 |
+| docker | 18.03.1-ce |
+
+
+## Setup local test
+
+install python packages
+
+```bash
+pipenv sync
+```
+
+Start a rabbitmq instance and get the container IP
+
+```bash
+docker run --name rmq -d rabbitmq:3.7.5-alpine
+docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' rmq
+# expected output with default setting: 172.17.0.2
+# If not, change the runner/config.py file to match
+```
+
+## Test the code
+
+use two terminals!
+
+```bash
+#terminal 1
+pipenv run python runner/consume.py
+
+#terminal 2
+pipenv run python runner/publish.py
+```
+
+The publish will put a message on the message queue
+and the consumer will pick it up and do stuff
+
+## Build and run the consumer in a container
+
+```bash
+docker build -t snekbox:latest -f docker/Dockerfile .
+
+#terminal 1
+docker run --name snekbox -d snekbox:latest
+docker logs snekbox -f
+
+#terminal 2
+pipenv run python runner/publish.py
+```
diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 0000000..cbf1b49
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,23 @@
+FROM python:3.6-alpine3.7
+
+RUN apk add --update tini
+RUN apk add --update build-base
+
+ENV PIPENV_VENV_IN_PROJECT=1
+ENV PIPENV_IGNORE_VIRTUALENVS=1
+ENV PIPENV_NOSPIN=1
+ENV PIPENV_HIDE_EMOJIS=1
+ENV PYTHONPATH=/runner
+
+RUN pip install pipenv
+
+RUN mkdir -p /runner
+COPY Pipfile /runner
+COPY Pipfile.lock /runner
+COPY runner /runner
+WORKDIR /runner
+
+RUN pipenv sync
+
+ENTRYPOINT ["/sbin/tini", "--"]
+CMD ["pipenv", "run", "consume"]
diff --git a/runner/config.py b/runner/config.py
new file mode 100644
index 0000000..75b3c28
--- /dev/null
+++ b/runner/config.py
@@ -0,0 +1,8 @@
+USERNAME = 'guest'
+PASSWORD = 'guest'
+HOST = '172.17.0.2'
+PORT = 5672
+EXCHANGE = 'exchange'
+EXCHANGE_TYPE = 'direct'
+QUEUE = 'text'
+ROUTING_KEY = 'bacon'
diff --git a/runner/consume.py b/runner/consume.py
new file mode 100644
index 0000000..0e4d79a
--- /dev/null
+++ b/runner/consume.py
@@ -0,0 +1,68 @@
+import pika
+import traceback
+import sys
+from io import StringIO
+
+from config import (
+ USERNAME,
+ PASSWORD,
+ HOST,
+ PORT,
+ EXCHANGE,
+ EXCHANGE_TYPE,
+ QUEUE,
+ ROUTING_KEY,
+)
+
+def execute(snippet):
+ old_stdout = sys.stdout
+ redirected_output = sys.stdout = StringIO()
+ failed = False
+ try:
+ exec(snippet)
+ except Exception as e:
+ failed = e
+ finally:
+ sys.stdout = old_stdout
+
+ if failed:
+ return failed
+ return redirected_output.getvalue()
+
+
+def message_handler(ch, method, properties, body):
+ msg = body.decode('utf-8')
+
+ # Execute code snippets here
+ print(f"incoming: {msg}", flush=True)
+ result = execute(msg)
+ print(result, flush=True)
+
+ ch.basic_ack(delivery_tag = method.delivery_tag)
+
+def rabbitmq_consume():
+ credentials = pika.PlainCredentials(USERNAME, PASSWORD)
+ connection = pika.BlockingConnection(pika.ConnectionParameters(HOST, PORT, '/', credentials))
+
+ channel = connection.channel()
+ channel.queue_declare(queue=QUEUE, durable=False)
+ channel.basic_qos(prefetch_count=1)
+ channel.basic_consume(message_handler, queue=QUEUE)
+
+ try:
+ print(f"""Connecting to
+ host: {HOST}
+ port: {PORT}
+ exchange: {EXCHANGE}
+ queue: {QUEUE}""", flush=True)
+
+ channel.start_consuming()
+
+ except Exception:
+ exc = traceback.format_exc()
+ print(exc, flush=True)
+
+ finally:
+ connection.close()
+
+rabbitmq_consume()
diff --git a/runner/publish.py b/runner/publish.py
new file mode 100644
index 0000000..fc18d03
--- /dev/null
+++ b/runner/publish.py
@@ -0,0 +1,42 @@
+import pika
+from config import (
+ USERNAME,
+ PASSWORD,
+ HOST,
+ PORT,
+ EXCHANGE,
+ EXCHANGE_TYPE,
+ QUEUE,
+ ROUTING_KEY,
+)
+
+def send(message):
+ credentials = pika.PlainCredentials(USERNAME, PASSWORD)
+ connection = pika.BlockingConnection(pika.ConnectionParameters(HOST, PORT, '/', credentials))
+ properties = pika.BasicProperties(content_type='text/plain', delivery_mode=1)
+
+ channel = connection.channel()
+ channel.queue_declare(queue=QUEUE, durable=False)
+ channel.exchange_declare(exchange=EXCHANGE, exchange_type=EXCHANGE_TYPE)
+ channel.queue_bind(exchange=EXCHANGE, queue=QUEUE, routing_key=ROUTING_KEY)
+
+ result = channel.basic_publish(
+ exchange=EXCHANGE,
+ routing_key=ROUTING_KEY,
+ body=message,
+ properties=properties
+ )
+
+ if result:
+ print(f"""Connecting to
+ host: {HOST}
+ port: {PORT}
+ exchange: {EXCHANGE}
+ queue: {QUEUE}""", flush=True)
+ print(f"Sent: '{message}'")
+ else:
+ print("not delivered")
+
+ connection.close()
+
+send('print "bacon is delicious"')
diff --git a/runner/test.py b/runner/test.py
new file mode 100644
index 0000000..08f0441
--- /dev/null
+++ b/runner/test.py
@@ -0,0 +1,21 @@
+import sys
+from io import StringIO
+
+def execute(snippet):
+ old_stdout = sys.stdout
+ redirected_output = sys.stdout = StringIO()
+ try:
+ exec(snippet)
+ except:
+ raise
+ finally:
+ sys.stdout = old_stdout
+
+ return redirected_output.getvalue()
+
+
+code = """
+i = [0,1,2]
+for j in i:
+ print(j)
+"""