Skip to content

Commit fd48327

Browse files
Mark Weinreuterwiewo
authored andcommitted
RLL WebEditor
The RLL WebEditor allows users to write and visualize their code directly in the browser. The WebEditor extends the existing rll_stack with of the following components: 1. ContainerControl: A new package which starts a Tornado app to start/stop Docker containers and upload files into these containers via a simple API. It manages a set of project images and the WebEditor utilizes the ContainerControl to spawn containers for the editor sessions. The ContainerControl app can run on a different host from the WebEditor and is a standalone app. 2. Editor backend The rll_server has been extended with new handlers and editor session management. Users start a editor session by selecting the project they want to work on and specifying whether they would like to use the text-based Python editor or the graphical Blockly editor. The WebEditor will keep track of the user session and persist the user code in the database. 3. Editor frontend The editor frontend shows a split-screen view with the editor on the left and the visualisation area on the right. The visualisation is powered by Ros3DJS which utilizes THREE.js and creates a WebGL simulation similiar to RVIZ. The frontend communicates via Websockets to the backend which relays the commands and messages into the respective Docker containers. 4. rll_worker network setup. This MR also includes changes in the way the docker networks for the rll_worker are setup/configure. **Important:** These changes have not yet been tested on the actual robots. 5. The way to create the web editor docker containers is still via scripts and should be updated to use the image_creator scripts. The WebEditor requires a fair amount of configuration files. The configuration for supported projects are located in the `config/editor_projects` folder and can be used as a reference for new projects. This is a squashed commit from the weinreuter:py3-webeditor branch.
1 parent 503fb21 commit fd48327

File tree

133 files changed

+89750
-335
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

133 files changed

+89750
-335
lines changed

.ci/after_build_target_workspace.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ apt install -y python3-pip
88
pip2 install -r rll_common/requirements.txt
99
pip2 install -r rll_test/requirements.txt
1010

11+
12+
pip3 install -r rll_common/requirements.txt
1113
pip3 install -r rll_server/requirements.txt
14+
pip3 install -r rll_test/requirements.txt
1215

1316
# Creating docker daemon config file
1417
# Setting log level to fatal to prevent broken pipe during log size test
@@ -32,6 +35,10 @@ service mongodb status
3235
# add rll_common to the PYTHONPATH in order for edit_projects to import files from this module
3336
ROS_PACKAGE_PATH=$(pwd)/rll_common:$(pwd)/rll_server PYTHONPATH=$(pwd)/rll_common/src:${PYTHONPATH} python3 rll_common/scripts/edit_projects.py set_defaults
3437

38+
ROS_PACKAGE_PATH=$(pwd)/rll_common:$(pwd)/rll_server PYTHONPATH=$(pwd)/rll_common/src:$(pwd)/rll_server/src:${PYTHONPATH} python3 rll_server/scripts/multiprocess_server.py &
39+
sleep 10
40+
kill %2
41+
3542
# Checking docker daemon status
3643
docker info
3744

.gitignore

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,10 @@ CVS/*
7474

7575
.idea/
7676
.vscode/
77-
venv/
77+
*venv/
7878
*.log
7979
.mypy_reports/
80-
.mypy_cache/
80+
.mypy_cache/
81+
82+
# Prepared submission archives
83+
template.tar

.gitlab-ci.yml

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ melodic-build-tests:
2424
CATKIN_LINT: "pedantic"
2525
CATKIN_LINT_ARGS: "--ignore unknown_package --ignore unknown_depend"
2626
VERBOSE_TESTS: "true"
27+
IMMEDIATE_TEST_OUTPUT: "true"
2728
# -e = setting ROS_IP variable inside the container
2829
# -v = turning /var/lib/docker into a volume which is necessary for writing to disk while using aufs storage driver
2930
# --privileged is needed for running docker in docker (which we do)
@@ -44,16 +45,13 @@ melodic-build-tests:
4445
- cp rll_worker/config/worker.yaml.sample rll_worker/config/worker.yaml
4546
- test ! -f .pylintrc && wget https://gitlab.ipr.kit.edu/rll/rll_sdk/raw/master/.pylintrc
4647

47-
48-
49-
5048
script: .industrial_ci/gitlab.sh
5149
tags:
5250
- docker
5351

5452
py3_pylint:
5553
before_script:
56-
- apk add --no-cache --update python3 python3-dev py3-pip gcc musl-dev libffi-dev build-base
54+
- apk add --no-cache --update python3 python3-dev py3-pip gcc musl-dev libffi-dev build-base curl-dev
5755
- pip3 install pylint
5856
- pip3 install -r rll_server/requirements.txt
5957
# Install the python modules from their directories (note the trailing slash)
@@ -65,14 +63,15 @@ py3_pylint:
6563
# for directories with an __init__.py you can pass the directory, otherwise specify separate files
6664
# Tornado handlers set class attributes outside of the __init__ function: W0201 disables this check
6765
#
68-
- pylint --rcfile=.pylintrc -d W0201 rll_server/src/rll_server/ rll_server/scripts/*.py
69-
- pylint --rcfile=.pylintrc rll_common/src/rll_common/ rll_common/scripts/*.py
70-
- pylint --rcfile=.pylintrc rll_test/src/rll_test/ rll_test/scripts/*.py
66+
- python3 -m pylint --rcfile=.pylintrc -d W0201 rll_server/src/rll_server/ rll_server/scripts/*.py
67+
- python3 -m pylint --rcfile=.pylintrc rll_common/src/rll_common/ rll_common/scripts/*.py
68+
- python3 -m pylint --rcfile=.pylintrc rll_test/src/rll_test/ rll_test/scripts/*.py
69+
- python3 -m pylint --rcfile=.pylintrc -d W0201 rll_container_control/src/rll_container_control/ rll_container_control/scripts/*.py
7170

7271

7372
py3_flake8:
7473
before_script:
75-
- apk add --no-cache --update python3 python3-dev py3-pip musl-dev libffi-dev build-base
74+
- apk add --no-cache --update python3 python3-dev py3-pip musl-dev libffi-dev build-base curl-dev
7675
- pip3 install flake8
7776
- pip3 install -r rll_server/requirements.txt
7877
- pip3 install rll_common/ rll_server/ rll_test/
@@ -82,14 +81,17 @@ py3_flake8:
8281
- python3 -m flake8 rll_server/
8382
- python3 -m flake8 rll_common/
8483
- python3 -m flake8 rll_test/
84+
- python3 -m flake8 rll_container_control/
8585

8686
mypy:
8787
before_script:
8888
# If you want to generate reports, you need to add the commented out dependencies for lxml
89-
- apk add --no-cache --update python3 python3-dev py3-pip musl-dev libffi-dev build-base # libxml2-dev libxslt-dev
89+
- apk add --no-cache --update python3 python3-dev py3-pip musl-dev libffi-dev build-base curl-dev # libxml2-dev libxslt-dev
9090
- pip3 install mypy # lxml
9191
- pip3 install -r rll_server/requirements.txt
9292
- pip3 install rll_common/ rll_server/ rll_test/ rll_worker/
93+
- pip3 install rll_common/ rll_server/ rll_test/ rll_worker/ rll_container_control/
94+
9395

9496
allow_failure: true
9597
script:
@@ -98,3 +100,4 @@ mypy:
98100
- mypy rll_common/src/ rll_common/scripts/
99101
- mypy rll_test/src/ rll_test/scripts/
100102
- mypy --py2 rll_worker/src/ rll_worker/scripts
103+
- mypy rll_container_control/src/ rll_container_control/scripts/

rll_common/config/logging.yaml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,18 @@ formatters:
99
handlers:
1010
console:
1111
class: logging.StreamHandler
12-
level: DEBUG
12+
level: INFO
1313
stream: "ext://sys.stdout"
1414
formatter: default
1515

1616
file_handler:
17-
class: logging.FileHandler
17+
class: logging.handlers.RotatingFileHandler
1818
level: INFO
1919
filename: "/tmp/{log_name}.log"
2020
formatter: extended
21+
maxBytes: 20971520 # 20MB
22+
backupCount: 14
23+
encoding: utf8
2124

2225
error_console_handler:
2326
class: logging.StreamHandler
@@ -30,4 +33,8 @@ root:
3033
level: DEBUG
3134
propagate: false
3235

36+
loggers:
37+
tornado.access:
38+
level: WARNING
39+
3340
disable_existing_loggers: false

rll_container_control/CMakeLists.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
cmake_minimum_required(VERSION 3.0.2)
2+
project(rll_container_control)
3+
4+
find_package(catkin REQUIRED COMPONENTS
5+
rll_common
6+
)
7+
8+
catkin_python_setup()
9+
catkin_package()
10+
11+
install(DIRECTORY launch DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} )
12+
13+
catkin_install_python(PROGRAMS
14+
scripts/container_control.py
15+
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
16+
17+
#catkin_lint: ignore_once missing_file
18+
install(FILES
19+
config/control.yaml config/logging.yaml
20+
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/config OPTIONAL)

rll_container_control/Readme.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# RLL Container Control
2+
3+
Tornado app to control docker containers (start, stop, restart, upload files).
4+
5+
The container manager has a set of images it can create containers for. The settings for these images is read
6+
from a configuration file. Each container created by the RLLContainerControl is identified by the config identifier for
7+
a docker image and a unique id, e.g. a user id. This allows to create multiple containers with the same settings.
8+
Consequently, to perform an operation you need to pass the image identifier and uid with every request.
9+
10+
## Network configuration
11+
12+
First create a custom bridge network which with the following options:
13+
14+
```shell script
15+
docker network create -d bridge rll-editor -o "com.docker.network.bridge.enable_icc"="0" --subnet="10.42.42.0/24" --gateway="10.42.42.1" -o "com.docker.network.bridge.name"="rll-editor"
16+
```
17+
18+
This network is not internal, i. e. accessible from other computers than the host. Inter-container communication is set to disabled.
19+
The containers will reside in the configured subnet (chose one that does not collide with your network setup). To make the bridge
20+
easier to identify in the system set the internal bridge name to "rll-editor" as well. See the [Docker documentation](https://docs.docker.com/engine/reference/commandline/network_create/)
21+
for more details.
22+
23+
### Limiting connections
24+
25+
Containers within this subnet can access other hosts in the network and the internet! To restrict the container's connection options
26+
some custom iptable rules should be configured. Docker adds a DOCKER-USER chain to the iptables FORWARD chain and it is recommended to put custom rules there.
27+
See the [Docker documentation](https://docs.docker.com/network/iptables/) for more details.
28+
29+
To limit the in- and outgoing connections the following rules are added:
30+
31+
```
32+
export RLL_CONTAINER_IP_RANGE="10.42.42.0/24"
33+
export RLL_SERVER_IP="YOUR_IP_HERE"
34+
export RLL_CONTAINER_CONTROL_IP="YOUR_IP_HERE"
35+
36+
# Connections from the source subnet via the input interface rll-editor are dropped except for the given destination IP
37+
sudo iptables -I DOCKER-USER -s $RLL_CONTAINER_IP_RANGE -i "rll-editor" -j DROP
38+
sudo iptables -I DOCKER-USER -s $RLL_CONTAINER_IP_RANGE -i "rll-editor" -d $RLL_SERVER_IP -p tcp --dport 1025:65535 -j ACCEPT
39+
40+
# Connetions to the destination subnet via the output interface rll-editor are dropped except for the given source IP
41+
sudo iptables -I DOCKER-USER -d $RLL_CONTAINER_IP_RANGE -o "rll-editor" -j DROP
42+
sudo iptables -I DOCKER-USER -d $RLL_CONTAINER_IP_RANGE -o "rll-editor" -s $RLL_SERVER_IP -j ACCEPT
43+
```
44+
**NOTE** These rules are ony a best guess on my part and could be incomplete/wrong!
45+
46+
Connections that originate in the container subnet and are send via the configured bridge ('rll-editor') are dropped except if the are meant to reach the configured destination ip.
47+
Note, that the port range is set to `1025:65535`. This is done because the server opens websocket connections into the containers and uses available random ports to do so.
48+
It might be desirable to limit this range further. For connections targeting the docker network the same restriction applies. Only connections from the server ip are allowed.
49+
50+
There is still one problem however, the containers are still able to reach the docker host since those packets are not forwarded
51+
and the DOCKER-USER chain is not applied. To circumvent connections to the docker host, the following rule should be added as well:
52+
53+
```
54+
# Block connections to the LOCAL docker host (cannot add this to the DOCKER-USER chain since the packets are local and not forwarded -> DOCKER-USER chain is not applied)
55+
sudo iptables -A INPUT -d $RLL_CONTAINER_CONTROL_IP -i "rll-editor" -j DROP
56+
```
57+
58+
### Limiting resources
59+
60+
The containers shouldn't be able to occupy to many resources and starve other users.
61+
Therefore, the available resources for each container are limited. The configuration for each project
62+
allows to specify the amount of CPUs to make available to each container. The range of available CPUs
63+
can also be set in the configuration. Containers are assigned a CPU-set from the pool of available CPUs.
64+
If no more CPUs are available multiple containers share the same CPU.
65+
**Note:** the containers still see all CPUs but can only access the configured CPUs.
66+
67+
The available RAM memory is also configurable via the project settings. Containers are allowed to swap and
68+
have to make do with the assigned memory.
69+
70+
## TODO:
71+
- [ ] prevent concurrent container operation (set flag on OP start, clear on done)
72+
- [ ] Overview-Handler use Ajax requests for restart/stop buttons
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# If a container is inactive for this period of time stop the container
2+
inactivity_seconds: 1800
3+
bridge_url_template: ws://%s:%d/
4+
control_url_template: ws://%s:%d/
5+
template_path: www
6+
cpu_range:
7+
min: 0
8+
max: 32
9+
images:
10+
# the internal name of this image (project name)
11+
rll_planning_project:
12+
# the actual docker image tag
13+
image: "rll-webeditor-rll_planning_project-melodic:latest"
14+
# the base path within the container where files for this project are uploaded
15+
upload_base_path: "/home/rll_user/ws/src/rll_planning_project/"
16+
# restrict the container settings
17+
container_settings:
18+
cpus: 1
19+
disk_size: "2g"
20+
network: "rll-editor"
21+
# Specify which ports are exposed, they will be remapped
22+
ports:
23+
control: 9090
24+
bridge: 9091
25+
26+
rll_tower_of_hanoi:
27+
image: "rll-webeditor_tower_of_hanoi-melodic:latest"
28+
upload_base_path: "/home/rll_user/ws/src/rll_tower_of_hanoi/tower_of_hanoi_project/"
29+
container_settings:
30+
cpus: 2
31+
disk_size: "2g"
32+
network: "rll-editor"
33+
ports:
34+
control: 9090
35+
bridge: 9091
36+
37+
rll_robot_playground_project:
38+
upload_base_path: "/home/rll_user/ws/src/rll_robot_playground_project/"
39+
image: "rll-webeditor-rll_robot_playground_project-melodic:latest"
40+
container_settings:
41+
cpus: 1
42+
disk_size: "2g"
43+
network: "rll-editor"
44+
ports:
45+
control: 9090
46+
bridge: 9091
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
version: 1
2+
3+
formatters:
4+
default:
5+
format: "%(asctime)s %(levelname)s %(name)s: %(message)s"
6+
extended:
7+
format: "%(asctime)s %(levelname)s %(name)s, line:%(lineno)d: %(message)s"
8+
9+
handlers:
10+
console:
11+
class: logging.StreamHandler
12+
level: DEBUG
13+
stream: "ext://sys.stdout"
14+
formatter: default
15+
16+
file_handler:
17+
class: logging.handlers.RotatingFileHandler
18+
level: INFO
19+
filename: "/tmp/{log_name}.log"
20+
formatter: extended
21+
maxBytes: 20971520 # 20MB
22+
backupCount: 14
23+
encoding: utf8
24+
25+
error_console_handler:
26+
class: logging.StreamHandler
27+
stream: "ext://sys.stderr"
28+
level: ERROR
29+
formatter: extended
30+
31+
loggers:
32+
websockets:
33+
level: INFO
34+
handler: [console]
35+
propagate: false
36+
docker:
37+
level: INFO
38+
handler: [console]
39+
propagate: false
40+
urllib3:
41+
level: INFO
42+
handler: [console]
43+
propagate: false
44+
45+
root:
46+
handlers: [file_handler, error_console_handler, console]
47+
level: DEBUG
48+
propagate: false
49+
50+
disable_existing_loggers: false
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0"?>
2+
<launch>
3+
<node name="rll_container_control" pkg="rll_container_control" type="container_control.py" output="screen"/>
4+
</launch>

rll_container_control/package.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0"?>
2+
<package format="2">
3+
<name>rll_container_control</name>
4+
<version>0.0.1</version>
5+
<description>Control and monitor docker containers for the RLL.</description>
6+
7+
<maintainer email="[email protected]">Mark Weinreuter</maintainer>
8+
9+
<license>GPLv3+</license>
10+
11+
<buildtool_depend>catkin</buildtool_depend>
12+
<depend>rll_common</depend>
13+
14+
</package>

0 commit comments

Comments
 (0)