mirror of
https://github.com/avatao-content/test-tutorial-framework
synced 2024-11-14 16:17:17 +00:00
222 lines
10 KiB
Markdown
222 lines
10 KiB
Markdown
# 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](https://github.com/avatao-content/baseimage-tutorial-framework) – Docker baseimage
|
||
- [frontend-tutorial-framework](https://github.com/avatao-content/frontend-tutorial-framework) – Angular frontend
|
||
- [test-tutorial-framework](https://github.com/avatao-content/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](https://github.com/avatao-content/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
|
||
|
||
Dependencies:
|
||
- bash
|
||
- git
|
||
- Docker
|
||
- yarn
|
||
- Angular CLI
|
||
- GNU coreutils
|
||
|
||
Just copy and paste the following command in a terminal:
|
||
|
||
`bash -c "$(curl -fsSL https://git.io/vxBfj)"`
|
||
|
||
Please do not hesitate to contact us with error logs included should this command fail to complete.
|
||
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.
|
||
|
||
This will set up a dev environment based on [test-tutorial-framework](https://github.com/avatao-content/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
|
||
|
||
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)
|
||
|
||
## 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](https://github.com/avatao-content/challenge-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`.
|
||
|
||
### Building the TFW baseimage without test-tutorial-framework
|
||
|
||
You might need to build our baseimage separately in case you've cloned an existing challenge depending on a specific version.
|
||
|
||
To do this simply issue `BASEIMAGE_ONLY=version bash -c "$(curl -fsSL https://git.io/vxBfj)"`, where `version` is a tag or commit of the [baseimage-tutorial-framework](https://github.com/avatao-content/baseimage-tutorial-framework) repository.
|
||
|
||
## 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:
|
||
|
||
```text
|
||
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`:
|
||
|
||
```text
|
||
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:
|
||
```text
|
||
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`
|
||
|
||
It is very important to understand that from now on your application must behave well behind a reverse proxy.
|
||
What this means is all `href`s must point the proxied paths (e.g. links should refer to `/yoururl/register` instead of `/register`) on your HTML pages.
|
||
|
||
You can learn about configuring nginx in [this](https://www.digitalocean.com/community/tutorials/understanding-the-nginx-configuration-file-structure-and-configuration-contexts) 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:
|
||
|
||
```text
|
||
[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](http://supervisord.org/configuration.html).
|
||
|
||
### 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.
|
||
|
||
```text
|
||
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_handler_main.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:
|
||
1. Use our install script to bootstrap your dev environment
|
||
2. Create an FSM that describes your challenge
|
||
- An example is in `solvable/src/test_fsm.py`
|
||
3. 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`
|
||
4. 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_handler_main.conf`
|
||
5. 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
|