controller | ||
hack | ||
solvable | ||
.dockerignore | ||
.drone.yml | ||
.gitignore | ||
.pylintrc | ||
config.yml | ||
README.md |
tutorial-framework test
This is an example playground project built via TFW. It is a good starting point to build your own challenges from and will host automated tests in the future.
It also gives home to several useful scripts in the hack
folder to speed up development.
Getting started
TFW consists of 3 repositories:
- baseimage-tutorial-framework – Docker baseimage
- frontend-tutorial-framework – Angular frontend
- test-tutorial-framework (this repo)
See the documentation of each in their README.md
files.
To learn the stuff you need to know about TFW in order to get started you should consult the baseimage-tutorial-framework repo first.
Getting started with creating challenges using the framework – setting up a development environment, building, running and such – is documented here.
Setting up a development environment
Just copy and paste the following command in a terminal:
bash -c "$(curl -fsSL https://git.io/vxBfj)"
Note that your SSH public key must be added to your GitHub user for this to work, or you mush select HTTPS remotes when prompted.
By default your IDE will fail to autocomplete code and will complain about missing dependencies.
To fix this you should install the tfw
pip package in your dev virtualenv:
pip install git+ssh://git@github.com/avatao-content/baseimage-tutorial-framework.git
(SSH)pip install git+https://github.com/avatao-content/baseimage-tutorial-framework.git
(HTTPS)
Dependencies:
- bash
- git
- Docker
- yarn
- Angular CLI
- GNU coreutils
You have trust issues regarding the public key infrastructure? You can request a checksum authenticated version of the installer command from our team!
This will set up a dev environment based on test-tutorial-framework just for you:
- it builds the latest release of the framework Docker baseimage locally
- it pins
solvable/Dockerfile
to use the this image - it includes the latest frontend in
solvable/frontend
with dependencies installed
Building & running
Automated
Our magical bash script hack/tfw.sh
can handle everything for you. Just run it without any arguments to see usage information.
It is advisable to run the frontend locally while developing to avoid really looooong build times. The hack/tfw.sh
script handles this for you automagically.
Challenge-toolbox
You can also use our toolbox to build and run TFW based challenges, just like the regular ones. Note that this will always create a production build with the frontend included.
Doing it manually
In case you must really do it then you can build & run manually.
Note that this is relatively painful and you should use the hack/tfw.sh
script when possible.
Building without frontend – execute from project root:
docker build -t test-tutorial-framework -f solvable/Dockerfile --build-arg BUILD_CONTEXT=solvable --build-arg NOFRONTEND=1 .
This will create a Docker image without the frontend, which you can run locally. For procudtion builds exclude the argument --build-arg NOFRONTEND=1
to include a frontend instance.
Execute the following command to run the image:
docker run --rm -p 8888:8888 -e AVATAO_SECRET=secret test-tutorial-framework
In case of a build without frontend (built with --build-arg NOFRONTEND=1
included) you will need to run yarn start
from the solvable/frontend
directory as well.
This will serve the frontend locally on http://localhost:4200
and take care of proxying.
If you've created a production build (without --build-arg NOFRONTEND=1
) you don't have to run the frontend locally and you can access the challenge on http://localhost:8888
.
Getting our hands dirty
The repository of a tutorial-framework based challenge is quite similar to a regular challenge. The project root should look something like this:
your_repo
├── solvable
│ └── [TFW based Docker image]
├── controller
│ └── [solution checking]
├── metadata
│ └── [challenge descriptions, writeups, etc.]
└── config.yml
The only notable difference is that the solvable
Docker image is a child of our baseimage: solvable/Dockerfile
begins with FROM eu.gcr.io/avatao-challengestore/tutorial-framework
.
From now on we are going to focus on the solvable
image.
Basics of a TFW based challenge
Let us take a closer look on solvable
:
solvable
├── Dockerfile
├── nginx webserver configurations
├── supervisor process manager (init replacement)
├── frontend clone of the frontend-tutorial-framework repo with dependencies installed
└── src example source code
Note that our baseimage requires the nginx
, supervisor
and frontend
folders to be in these exact locations and to be used as described below.
This is a contract your image must comply.
The src
directory contains a simple example of using TFW
nginx
All TFW based challenges expose a single port defined in the TFW_PUBLIC_PORT
envvar which is set to 8888
by default.
This means that in order to listen on more than a single port we must use a reverse proxy.
Any .conf
files in solvable/nginx/
will be automatically included in the nginx configuration.
In case you want to serve a website or service you must proxy it through TFW_PUBLIC_PORT
.
This is really easy: just create a config file in solvable/nginx/
similar to this one:
location /yoururl {
proxy_pass http://127.0.0.1:3333;
}
After this you can access the service running on port 3333
at http://localhost:8888/yoururl
You can learn about configuring nginx in this handy little tutorial.
supervisor
In most Docker conainers there is a single process running (it gets PID 1
).
When working with TFW you can run as many processes as you want to by using supervisord.
Any .conf
files in the solvable/supervisor/
directory will be included in the supervisor configuration.
The programs, you define this way, are easy to manage (starting/stopping/restarting) using the supervisorctl
command line tool or our built-in event handler.
You can even configure your processes to start with the container by including autostart=true
in your configuration file.
To run your own webservice for instance you need to create a config file in solvable/supervisor/
similar to this one:
[program:yourprogram]
user=user
directory=/home/user/example/
command=python3 server.py
autostart=true
This starts the /home/user/example/server.py
script using python3
after your container entered the running state (because of autostart=true
, supervisor does not start programs by default).
You can learn more about configuring supervisor here.
frontend
This is a clone of the frontend-tutorial-framework
repository with dependencies installed in solvable/frontend/node_modules
.
You can modify it to fit your needs, but this requires some Angular knowledge (not much at all).
If all you want to do is starting a simple web application and to send some messages you can mostly skip the Angluar knowledge bit. Refer to the example in this repo.
src
This folder contains the source code of a server running TFW and an other server running our event handlers. Note that this is not a part of the framework by any means, these are just simple examples.
solvable/src
├── tfw_server.py tutorial-framework server
├── event_handler_main.py event handlers implemented in python
└── test_fsm.py example FSM
The core of the framework is the TFWServer
class, which is instanciated in tfw_server.py
.
This class handles the forwarding of the messages from the frontend to the event handlers connecting to it via ZMQ.
It also manages the FSM.
As you can see this file is set up to start with the container in solvable/supervisor/tfw_server.conf
.
event_handler_main.py
contains example usage of our pre-defined event handlers written in Python3.
As you can see they run in a separate process (set up in solvable/supervisor/event_handlers.conf
).
These event handlers could be implemented in any language that has ZMQ bindings.
Note that you don't have to use all our event handlers.
Should you want to avoid using a feature, you can just delete the appropriate event handler from event_handler_main.py
.
It is genarally a good idea to separate these files from the rest of the stuff in solvable
, so it is a good practice to create an src
directory.
Baby steps
When creating your own challenge the process should be the following:
- Use our install script to bootstrap your dev environment
- Create an FSM that describes your challenge
- An example is in
solvable/src/test_fsm.py
- An example is in
- Create a
TFWServer
instance and set it up to run:- Create a server app:
solvable/src/tfw_server.py
- Set it up to run:
solvable/supervisor/tfw_server.conf
- Create a server app:
- Create event handlers connecting to the
TFWServer
handling events you want to process:- Create an event handler server:
solvable/src/event_handler_main.py
- Set it up to run:
solvable/supervisor/event_handlers.conf
- Create an event handler server:
- Modify the frontend in
solvable/frontend
to fit your challenge- This usually involves using our pre-made components
- And perhaps doing some of your own stuff, like:
- Sending messages then handling these in event handlers written in step 4
- Sending triggers to step the FSM
- Including images of cats – http://thecatapi.com