Skip to content

Commit

Permalink
Remove inetd implementation of update and add http endpoints for upda…
Browse files Browse the repository at this point in the history
…te and restart to api server
  • Loading branch information
btmorr committed Feb 23, 2018
1 parent 77f6550 commit 8dc7878
Show file tree
Hide file tree
Showing 11 changed files with 141 additions and 111 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ venv/
env/
.env/

build/
api/docs/build/
develop-eggs/
dist/
Expand Down
40 changes: 8 additions & 32 deletions api/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,46 +30,22 @@ publish:
dev:
python opentrons/server/main.py -P 31950 opentrons.server.main:init

dist/update.base64: clean
rm -rf dist/* && \
.PHONY: wheel
wheel:
python setup.py bdist_wheel && \
tar -C dist -cf /tmp/update . && \
echo -e "---OT UPDATE BEGIN---" > dist/update.base64 && \
base64 -i /tmp/update | sed -e "s/.\{50\}/&\n/g" >> dist/update.base64 && \
echo -e "\n---OT UPDATE END---" >> dist/update.base64
ls dist

.PHONY: push
push: dist/update.base64
curl -F 'data=@dist/update.base64' http://\[fd00:0:cafe:fefe::1\]/upload
push: wheel
curl -X POST \
-H "Content-Type: multipart/form-data" \
-F 'whl=@dist/`ls dist | grep whl`' \
http://\[fd00:0:cafe:fefe::1\]/server/update

.PHONY: term
term:
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@fd00:0:cafe:fefe::1

########## [START] RESIN DEVICE ##########

.PHONY: info_device
info_device:
resin device $(OT_DEVICE_ID)

.PHONY: sync_device
sync_device: clean
resin sync $(OT_DEVICE_ID) --source ./ --destination /usr/src/api --progress

.PHONY: blink_device
blink_device:
resin device identify $(OT_DEVICE_ID)

.PHONY: active_devices
active_devices:
resin devices --app OTone | awk 'NR==1 || /true/'

.PHONY: ssh_device
ssh_device:
resin ssh $(OT_DEVICE_ID)

########## [END] RESIN DEVICE ##########

.PHONY: clean
clean:
rm -rf \
Expand Down
65 changes: 61 additions & 4 deletions api/opentrons/server/endpoints.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import os
import json
import logging
import asyncio
import subprocess
from time import sleep
from threading import Thread
from aiohttp import web
from opentrons import robot, __version__
Expand Down Expand Up @@ -147,17 +149,72 @@ async def wifi_status(request):
return web.json_response(connectivity)


async def _install(filename, loop):
proc = await asyncio.create_subprocess_shell(
'pip install --upgrade --force-reinstall --no-deps {}'.format(
filename),
stdout=asyncio.subprocess.PIPE,
loop=loop)

rd = await proc.stdout.read()
res = rd.decode().strip()
print(res)
await proc.wait()
return res


async def update_api(request):
"""
This handler accepts a POST request with Content-Type: multipart/form-data
and a file field in the body named "whl". The file should be a valid Python
wheel to be installed. The received file is install using pip, and then
deleted and a success code is returned.
"""
log.debug('Update request received')
data = await request.post()
filename = data['whl'].filename
log.info('Preparing to install: {}'.format(filename))
content = data['whl'].file.read()

with open(filename, 'wb') as wf:
wf.write(content)

res = await _install(filename, request.loop)
log.debug('Install complete')
try:
os.remove(filename)
except OSError:
pass
log.debug("Result: {}".format(res))
return web.json_response({
'message': res,
'filename': filename
})


async def restart(request):
"""
Returns OK, then waits approximately 3 seconds and restarts container
"""
def wait_and_restart():
log.info('Restarting server')
sleep(3)
os.system('kill 1')
Thread(target=wait_and_restart).start()
return web.json_response({"message": "restarting"})


async def identify(request):
Thread(target=lambda: robot.identify(
int(request.query.get('seconds', '10')))).run()
return web.Response(text='Ok')
int(request.query.get('seconds', '10')))).start()
return web.json_response({"message": "identifying"})


async def turn_on_rail_lights(request):
robot.turn_on_rail_lights()
return web.Response(text='Ok')
return web.json_response({"lights": "on"})


async def turn_off_rail_lights(request):
robot.turn_off_rail_lights()
return web.Response(text='Ok')
return web.json_response({"lights": "off"})
8 changes: 5 additions & 3 deletions api/opentrons/server/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,11 @@ def init(loop=None):
server.app.router.add_get('/wifi/list', endp.wifi_list)
server.app.router.add_post('/wifi/configure', endp.wifi_configure)
server.app.router.add_get('/wifi/status', endp.wifi_status)
server.app.router.add_get('/identify', endp.identify)
server.app.router.add_get('/lights/on', endp.turn_on_rail_lights)
server.app.router.add_get('/lights/off', endp.turn_off_rail_lights)
server.app.router.add_post('/identify', endp.identify)
server.app.router.add_post('/lights/on', endp.turn_on_rail_lights)
server.app.router.add_post('/lights/off', endp.turn_off_rail_lights)
server.app.router.add_post('/server/update', endp.update_api)
server.app.router.add_post('/server/restart', endp.restart)
return server.app


Expand Down
54 changes: 54 additions & 0 deletions api/tests/opentrons/server/test_update_endpoints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import os
import json
import tempfile
from aiohttp import web
from opentrons.server.main import init, log_init
from opentrons.server import endpoints


async def test_restart(virtual_smoothie_env, monkeypatch, loop, test_client):
test_data = {"test": "pass"}

async def mock_restart(request):
return web.json_response(test_data)
monkeypatch.setattr(endpoints, 'restart', mock_restart)

app = init(loop)
cli = await loop.create_task(test_client(app))

expected = json.dumps(test_data)
resp = await cli.post('/server/restart')
text = await resp.text()
assert resp.status == 200
assert text == expected


async def test_update(virtual_smoothie_env, monkeypatch, loop, test_client):
log_init()

msg = "success"
filename = "testy.whl"
tmpdir = tempfile.mkdtemp("files")
with open(os.path.join(tmpdir, filename), 'w') as fd:
fd.write("test")

async def mock_install(filename, loop):
return msg
monkeypatch.setattr(endpoints, '_install', mock_install)

app = init(loop)
cli = await loop.create_task(test_client(app))

data = {'whl': open(os.path.join(tmpdir, filename))}

resp = await cli.post(
'/server/update',
data=data)

expected = json.dumps({
'message': msg,
'filename': filename
})
text = await resp.text()
assert resp.status == 200
assert text == expected
1 change: 0 additions & 1 deletion compute/conf/inetd.conf
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
local-ethernet:ssh stream tcp6 nowait root /usr/sbin/dropbear dropbear -Bi
localhost:http stream tcp nowait root /usr/local/bin/updates.sh updates.sh
26 changes: 9 additions & 17 deletions compute/conf/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@ http {
listen [::]:{OT_SERVER_PORT};
listen 0.0.0.0:{OT_SERVER_PORT};

client_body_in_file_only off;
client_body_buffer_size 128k;
client_max_body_size 5M;

location / {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 1h;
# This gets replaced by actual path during container build
proxy_pass http://unix:{OT_SERVER_UNIX_SOCKET_PATH};
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 1h;
proxy_pass http://unix:{OT_SERVER_UNIX_SOCKET_PATH};
}
}

Expand All @@ -36,16 +39,5 @@ http {
location / {
index index.htm index.html;
}

# Upload form should be submitted to this location
location /upload {
limit_except POST { deny all; }
proxy_pass http://127.0.0.1;
# Allow for large files
client_body_temp_path /tmp/;
client_body_in_file_only off;
client_body_buffer_size 128K;
client_max_body_size 50M;
}
}
}
15 changes: 0 additions & 15 deletions compute/scripts/extract_update.py

This file was deleted.

1 change: 1 addition & 0 deletions compute/scripts/setup.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env bash

python /usr/local/bin/setup_gpio.py

# Keep all IPv6 addresses on an interface down event. If set static
# global addresses with no expiration time are not flushed.
Expand Down
6 changes: 2 additions & 4 deletions compute/scripts/start.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#!/usr/bin/env bash

python /usr/local/bin/setup_gpio.py

radvd --logmethod=stderr_syslog --pidfile=/run/radvd.pid

# mdns announcement
Expand All @@ -10,7 +8,7 @@ announce_mdns.py &
# serve static pages and proxy HTTP services
nginx

# SSH, updates, etc
# enable SSH over ethernet
inetd -e /etc/inetd.conf

# Home robot
Expand All @@ -20,7 +18,7 @@ python -c "from opentrons import robot; robot.connect(); robot.home()"
# Start Jupyter Notebook server
echo "Starting Jupyter Notebook server"
mkdir -p /data/user_storage/opentrons_data/jupyter
jupyter notebook --log-level=DEBUG --allow-root &
jupyter notebook --allow-root &

# Check if config exists, and alert if not found
echo "Checking for deck calibration data..."
Expand Down
35 changes: 0 additions & 35 deletions compute/scripts/updates.sh

This file was deleted.

0 comments on commit 8dc7878

Please sign in to comment.