2018-12-01 23:43:34 +00:00
|
|
|
\chapter{A Tour of TFW}\label{atouroftfw}
|
|
|
|
|
|
|
|
The bulk of the functionality provided by the Tutorial Framework
|
|
|
|
is implemented in pre-made components that are built upon the architecture
|
|
|
|
described in Chapter~\ref{architecture}.
|
|
|
|
To create tutorials, developers generally rely on and use TFW provided components to
|
|
|
|
implement most of the common functionality and then build more exercise-specific logic
|
|
|
|
based on the TFW architecture themselves.
|
|
|
|
A \emph{very important} point to keep in mind is that most of this
|
|
|
|
exercise-specific logic will be implemented in \textbf{FSM callbacks} and
|
|
|
|
custom \textbf{event handlers}.
|
2018-12-03 18:14:02 +00:00
|
|
|
The whole framework is built in a way to facilitate this process and developers
|
2018-12-02 23:24:06 +00:00
|
|
|
who understand this mindset almost always find it a breeze to create great
|
|
|
|
content using TFW\@.
|
2018-12-01 15:39:05 +00:00
|
|
|
|
|
|
|
The purpose of this chapter is to further detail the built-in components
|
|
|
|
provided by the framework.
|
2018-12-02 23:24:06 +00:00
|
|
|
As previously mentioned before, these components are implemented as event handlers
|
|
|
|
running in the \code{solvable} Docker container which communicate with frontend
|
2018-12-01 15:39:05 +00:00
|
|
|
components written in Angular.
|
2018-12-02 23:24:06 +00:00
|
|
|
Some components might only feature one of these however,
|
|
|
|
for instance the built-in code editor requires a frontend component and an event
|
|
|
|
handler to function properly, while the component responsible for
|
|
|
|
drawing out and managing frontend components implements no
|
|
|
|
event handler, so it purely exists in Angular.
|
2018-12-03 18:14:02 +00:00
|
|
|
Another example of a purely frontend component would be the messages component,
|
2018-12-02 23:24:06 +00:00
|
|
|
which is used to display messages to the user.
|
2018-12-01 15:39:05 +00:00
|
|
|
|
|
|
|
In the Tutorial Framework most of the built-ins define APIs, which are TFW messages
|
|
|
|
that can be used to interact with them.
|
|
|
|
For example, to inject a command into the terminal one would use a message like this:
|
2018-12-02 23:24:06 +00:00
|
|
|
\begin{lstlisting}[captionpos=b,caption={An API message capable of writing to the terminal}]
|
2018-12-01 15:39:05 +00:00
|
|
|
{
|
|
|
|
"key": "shell",
|
|
|
|
"data":
|
|
|
|
{
|
|
|
|
"command": "write",
|
|
|
|
"value": "echo 'Hello TFW World!'\n"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
\end{lstlisting}
|
2018-12-02 23:24:06 +00:00
|
|
|
Notice the ``\code{\n}'' at the end of the command.
|
|
|
|
By including a newline character, we are also capable of executing commands directly
|
|
|
|
in the user's terminal (and the user can see this).
|
2018-12-01 15:39:05 +00:00
|
|
|
Were this newline omitted, the command would only be written to the terminal
|
2018-12-02 23:24:06 +00:00
|
|
|
--- but not automatically executed --- for users to inspect and potentially execute themselves.
|
2018-12-01 15:39:05 +00:00
|
|
|
|
|
|
|
Some components emit or broadcast messages on specific events, for instance the
|
2018-12-02 15:44:31 +00:00
|
|
|
\code{FSMManagingEventHandler} broadcasts the following message on state transitions:
|
2018-12-02 23:24:06 +00:00
|
|
|
\begin{lstlisting}[captionpos=b,caption={An FSM update message}]
|
2018-12-01 15:39:05 +00:00
|
|
|
{
|
|
|
|
"key": "fsm_update",
|
|
|
|
"data" :
|
|
|
|
{
|
|
|
|
"current_state": ...string...,
|
|
|
|
"valid_transitions": ...[array of {"trigger": ...string...} objects]...,
|
|
|
|
"in_accepted_state": ...boolean...,
|
|
|
|
"last_event": ...
|
|
|
|
object like
|
|
|
|
{
|
|
|
|
"from_state": ...string...,
|
|
|
|
"to_state": ...string...,
|
|
|
|
"trigger": ...string...,
|
2018-12-03 18:14:02 +00:00
|
|
|
"timestamp": ...UNIX timestamp...
|
2018-12-01 15:39:05 +00:00
|
|
|
}
|
|
|
|
...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
\end{lstlisting}
|
|
|
|
As you can see this message contains loads of useful information regarding what is
|
2018-12-02 23:24:06 +00:00
|
|
|
exactly happening in the tutorial at a given point in time and can be used by client code
|
2018-12-01 15:39:05 +00:00
|
|
|
to make informed decisions based on this knowledge.
|
|
|
|
|
2018-12-02 23:24:06 +00:00
|
|
|
It is not the purpose of this text to provide a complete API documentation however, so in the
|
2018-12-01 15:39:05 +00:00
|
|
|
following I am only going to explain possibilities provided by given components rather
|
|
|
|
than showcasing actual, real-life API messages.
|
|
|
|
|
2018-12-01 19:24:24 +00:00
|
|
|
\section{Messages Component}
|
|
|
|
|
2018-12-03 15:38:22 +00:00
|
|
|
The framework must allow content creators to communicate with the user,
|
|
|
|
and provide some mechanism to enable ``talking'' to them.
|
2018-12-02 23:24:06 +00:00
|
|
|
This is the responsibility of the \emph{messages} component, which
|
|
|
|
provides a chatbox-like element on the frontend.
|
2018-12-03 18:14:02 +00:00
|
|
|
The simplest form of communication it accommodates it the insertion of text
|
2018-12-01 19:24:24 +00:00
|
|
|
into the chatbox through API messages.
|
2018-12-03 15:38:22 +00:00
|
|
|
This component always expects messages it receives to be in Markdown%
|
|
|
|
\footnote{\href{https://daringfireball.net/projects/markdown/}
|
|
|
|
{https://daringfireball.net/projects/markdown/}} format,
|
|
|
|
so that it is possible to nicely format any text that one might want to display.
|
|
|
|
This is especially important when displaying inline code with text around it,
|
|
|
|
so that it is easier to read for the user.
|
|
|
|
Every message has an optional \emph{originator} field, which serves
|
|
|
|
to remind the user of the purpose of the given message.
|
2018-12-02 23:24:06 +00:00
|
|
|
These messages are also timestamped so that it is easier to navigate through them
|
|
|
|
and look back older messages from the user.
|
2018-12-03 15:38:22 +00:00
|
|
|
If no timestamp is present in the API message, then it will be added on
|
|
|
|
the frontend.
|
|
|
|
This is useful, because this will use system time on the user's machine,
|
|
|
|
and as such time zones will not be an issue (whereas if we suggested adding
|
|
|
|
timestamps to the messages on the backend, content creators would have to
|
|
|
|
deal with conversions between time zones).
|
2018-12-01 19:24:24 +00:00
|
|
|
|
2018-12-02 23:24:06 +00:00
|
|
|
\pic[width=.5\textwidth]{figures/chatbot.png}{The avataobot typing in the messages component}
|
2018-12-01 19:24:24 +00:00
|
|
|
|
|
|
|
A particularly interesting feature of the messages component is that TFW client code
|
2018-12-02 23:24:06 +00:00
|
|
|
can queue a bunch of messages for the component to display one by one, separated by
|
2018-12-01 19:24:24 +00:00
|
|
|
appropriate pauses in time so that the user is capable of conveniently reading through all
|
|
|
|
off them.
|
|
|
|
Similarly to a real chat application, some
|
2018-12-02 23:24:06 +00:00
|
|
|
``jumping dots'' indicate if the bot is still ``typing'' something.
|
2018-12-01 19:24:24 +00:00
|
|
|
The timing of pauses and messages is based on the \emph{WPM} --- or Words Per Minute ---
|
2018-12-02 23:24:06 +00:00
|
|
|
set by developers according to their specific requirements.
|
2018-12-01 19:24:24 +00:00
|
|
|
This creates an experience similar to chatting with someone in real time, as the time
|
2018-12-03 18:14:02 +00:00
|
|
|
it takes for each message to be displayed depends on the length of the previous message.
|
2018-12-02 15:44:31 +00:00
|
|
|
This illusion is made possible through appropriate \code{setTimeout()} calls in
|
2018-12-01 19:24:24 +00:00
|
|
|
TypeScript and some elementary math to calculate the proper delays in milliseconds based on
|
|
|
|
message lengths:
|
|
|
|
|
|
|
|
\[ charactersPerMinute = wordsPerMinute * 5 \]
|
|
|
|
\[ charactersPerSeconds = charactersPerMinute / 60 \]
|
|
|
|
\[ timeoutSeconds = lastMessageLength / charactersPerSeconds \]
|
|
|
|
\[ timeoutMilliseconds = timeoutSeconds * 1000 \]
|
|
|
|
|
2018-12-03 18:14:02 +00:00
|
|
|
The value 5 comes from the fact that on average English words are 5
|
2018-12-02 23:24:06 +00:00
|
|
|
characters long according to some studies.
|
2018-12-03 15:38:22 +00:00
|
|
|
This value could be made configurable in the future, but currently there
|
2018-12-03 18:14:02 +00:00
|
|
|
are no plans to make non-English challenges.
|
2018-12-02 23:24:06 +00:00
|
|
|
|
2018-12-01 19:24:24 +00:00
|
|
|
\section{IDE Component}\label{idecomponent}
|
|
|
|
|
|
|
|
This is the code editor integrated into the frontend of the framework.
|
|
|
|
It allows users to select, display and edit files.
|
2018-12-02 23:24:06 +00:00
|
|
|
Developers can configure which directory of the file system should the editor list files from.
|
2018-12-01 19:24:24 +00:00
|
|
|
The editor features the ``Deploy'' button referred to earlier in this paper, which is
|
2018-12-02 23:24:06 +00:00
|
|
|
capable of restarting processes that might be running code from a file visible in the editor.
|
2018-12-01 19:24:24 +00:00
|
|
|
|
|
|
|
To implement this IDE%
|
|
|
|
\footnote{Integrated development environment}
|
|
|
|
I have integrated the open source Monaco editor developed by Microsoft into the
|
|
|
|
Angular web application TFW uses as a frontend, and added functionality to it
|
|
|
|
that allows the editor to integrate with the framework.
|
2018-12-03 18:14:02 +00:00
|
|
|
This involves communication with an event handler dedicated to this feature,
|
2018-12-01 19:24:24 +00:00
|
|
|
which is capable of reading and writing files to disk, while sending and receiving
|
|
|
|
editor content from the frontend component.
|
|
|
|
The interaction of this event handler and the Monaco editor provides a seamless
|
2018-12-03 18:14:02 +00:00
|
|
|
editing experience, featuring auto-save at configurable intervals, code completion,
|
2018-12-01 19:24:24 +00:00
|
|
|
automatic code coloring for several programming languages and more.
|
|
|
|
|
|
|
|
Perhaps the most ``magical'' feature of this editor is that if any process
|
|
|
|
in the Docker container writes a file that is being displayed in the editor,
|
2018-12-03 18:14:02 +00:00
|
|
|
the contents of that file are automatically refreshed without any user
|
2018-12-01 19:24:24 +00:00
|
|
|
interaction whatsoever.
|
|
|
|
Besides that, if a file is created in the directory the editor is configured
|
2018-12-03 18:14:02 +00:00
|
|
|
to display, that file is automatically displayed on a new tab in the IDE\@.
|
2018-12-01 19:24:24 +00:00
|
|
|
|
|
|
|
This allows for really interesting demo opportunities.
|
2018-12-03 18:14:02 +00:00
|
|
|
Let's say I create a file using the terminal on the frontend by executing the
|
2018-12-02 15:44:31 +00:00
|
|
|
command \code{touch file.txt}. A new tab on the editor automatically
|
2018-12-01 19:24:24 +00:00
|
|
|
appears. If I select it I can confirm that I have successfully created an
|
|
|
|
empty file.
|
2018-12-02 15:44:31 +00:00
|
|
|
After this let's run a \code{while} cycle in the command line which
|
2018-12-03 18:14:02 +00:00
|
|
|
periodically appends some text to \code{file.txt}:
|
2018-12-02 23:24:06 +00:00
|
|
|
\begin{lstlisting}[captionpos=b,caption={Bash while cycle writing to a file periodically},
|
2018-12-01 19:24:24 +00:00
|
|
|
language=bash]
|
|
|
|
while true
|
|
|
|
do
|
|
|
|
echo 'Hey there!' >> file.txt
|
|
|
|
sleep 1
|
|
|
|
done
|
|
|
|
\end{lstlisting}
|
|
|
|
The results speak for themselves:
|
2018-12-02 23:24:06 +00:00
|
|
|
\pic{figures/ide_demo.png}{The editor demo involving automatic file refreshing}
|
2018-12-03 18:14:02 +00:00
|
|
|
As you can see, the file contents are automatically updated as the bash script appends
|
2018-12-01 19:24:24 +00:00
|
|
|
to the file.
|
|
|
|
This feature is implemented by using the inotify API%
|
|
|
|
\footnote{\href{http://man7.org/linux/man-pages/man7/inotify.7.html}
|
|
|
|
{http://man7.org/linux/man-pages/man7/inotify.7.html}}
|
|
|
|
provided by the Linux kernel to monitor file system events involving the directory listed by
|
|
|
|
the editor. The event handler of the editor hooks callbacks to said events which notify the
|
2018-12-02 23:24:06 +00:00
|
|
|
Tutorial Framework to reload the list of files in the directory as well as the contents of
|
|
|
|
the selected file.
|
2018-12-01 19:24:24 +00:00
|
|
|
The code making this feature possible is reused several times in the framework
|
|
|
|
for interesting purposes such as monitoring the logs of processes.
|
|
|
|
|
2018-12-03 15:38:22 +00:00
|
|
|
It is also worth to mention that integrating such file monitoring into the framework
|
|
|
|
is not quite as simple as described above, because one has to deal with many issues like the
|
|
|
|
semi-undeterministic nature of how a single file modification can sometimes result in
|
|
|
|
several inotify events, or implement rate limiting for the whole thing to avoid
|
|
|
|
saturating the messaging system with file content updates triggered by said events
|
|
|
|
being triggered too frequently.
|
|
|
|
|
2018-12-01 19:24:24 +00:00
|
|
|
The editor also allows content creators to completely control it using API messages.
|
2018-12-02 23:24:06 +00:00
|
|
|
This involves the selecting, reading and writing of files as well as changing the
|
2018-12-01 19:24:24 +00:00
|
|
|
selected directory.
|
|
|
|
These features allow content creators to ``guide'' a user through code bases
|
|
|
|
for example, where in each step of a tutorial a file is opened and explained
|
|
|
|
through messages sent to the chatbox of the messages component.
|
|
|
|
|
2018-12-03 15:38:22 +00:00
|
|
|
Developers have to \emph{explicitly} allow directories one by one to be listed by the
|
|
|
|
editor. This is done to avoid access control issues in case the editor is
|
|
|
|
running with more permissions than the user should have%
|
|
|
|
\footnote{Actually this involves extra caution, such as dealing with
|
2018-12-03 15:47:09 +00:00
|
|
|
symlinks in an allowed directory which could point to other, non-allowed locations.}.
|
2018-12-03 15:38:22 +00:00
|
|
|
It is also possible to blacklist file patterns (so that binary files can be
|
|
|
|
excluded for example, as a text editor is not suitable to deal with these).
|
|
|
|
|
2018-12-01 19:24:24 +00:00
|
|
|
\section{Terminal Component}
|
|
|
|
|
|
|
|
This is a full-fledged xterm terminal emulator running right in the user's browser.
|
|
|
|
I have partially derived this from my early work I have briefly mentioned
|
|
|
|
in~\ref{intro:emergence}, but it required lot's of new functionality to work the way
|
|
|
|
it does today.
|
|
|
|
The difference this time is that I had to integrate it into an Angular component.
|
|
|
|
I have used the awesome xterm.js%
|
|
|
|
\footnote{\href{https://xtermjs.org}{https://xtermjs.org}}
|
|
|
|
terminal emulator to do so.
|
|
|
|
|
|
|
|
This component has a tiny server process which is managed by a TFW event handler.
|
|
|
|
This small server is responsible for spawning bash sessions and
|
2018-12-03 18:14:02 +00:00
|
|
|
UNIX pseudoterminals (or \code{pty}s) in the \code{solvable} Docker
|
2018-12-01 19:24:24 +00:00
|
|
|
container.
|
2018-12-02 15:44:31 +00:00
|
|
|
It is also responsible for connecting the master end of the \code{pty} to the
|
2018-12-03 15:38:22 +00:00
|
|
|
emulator running in the browser and the slave end to the bash session it has
|
2018-12-01 19:24:24 +00:00
|
|
|
spawned.
|
|
|
|
This way users are able to work in the shell displayed on the frontend just like
|
|
|
|
they would on their home machines, which allows for great tutorials
|
|
|
|
explaining topics that involve the usage of a shell.
|
2018-12-03 18:14:02 +00:00
|
|
|
Note that this allows us to cover an extremely wide variety of topics using TFW\@:
|
|
|
|
from compiling shared libraries for development, to using cryptographic FUSE file systems
|
2018-12-01 19:24:24 +00:00
|
|
|
for enhanced privacy%
|
|
|
|
\footnote{\href{https://github.com/rfjakob/gocryptfs}{https://github.com/rfjakob/gocryptfs}},
|
2018-12-03 15:38:22 +00:00
|
|
|
or automating cloud infrastructure using Ansible%
|
2018-12-01 19:24:24 +00:00
|
|
|
\footnote{\href{https://www.ansible.com}{https://www.ansible.com}}.
|
|
|
|
|
2018-12-03 15:38:22 +00:00
|
|
|
This component exposes several functions through TFW API messages, such as injecting commands
|
2018-12-01 19:24:24 +00:00
|
|
|
to the terminal, reading command history and registering callbacks that are invoked when
|
2018-12-03 15:38:22 +00:00
|
|
|
the user executes anything in the terminal.
|
|
|
|
This allows content developers to implement functionality such as advancing the
|
|
|
|
tutorial when a certain command was invoked, or detect common mistakes in using certain
|
|
|
|
tools, such as warning users when they try to use an outdated cipher when
|
|
|
|
encrypting a file using \code{openssl}, or if they generate an RSA key with a small key size.
|
2018-12-01 19:24:24 +00:00
|
|
|
|
2018-12-03 15:38:22 +00:00
|
|
|
\pic{figures/terminal.png}{The frontend terminal of TFW running top}
|
2018-12-01 19:24:24 +00:00
|
|
|
|
2018-12-01 23:43:34 +00:00
|
|
|
The implementation of reading command history is quite an exotic one.
|
2018-12-02 15:02:56 +00:00
|
|
|
The framework needs to be able to detect if the user has executed any command in the
|
|
|
|
container using an interactive bash session.
|
|
|
|
This is not an easy thing to accomplish without relying on some sort of heavyweight
|
2018-12-01 23:43:34 +00:00
|
|
|
monitoring solution such as Sysdig%
|
|
|
|
\footnote{\href{https://sysdig.comq}{https://sysdig.com}}.
|
2018-12-03 18:14:02 +00:00
|
|
|
I deemed most similar systems a huge overkill to implement this functionality, and their
|
2018-12-03 15:38:22 +00:00
|
|
|
memory footprints are not something we could afford here%
|
|
|
|
\footnote{These containers will be spawned on a per-user basis, so we must be as
|
2018-12-03 15:47:09 +00:00
|
|
|
conservative with memory as possible.}.
|
2018-12-02 15:44:31 +00:00
|
|
|
Another way would be to use \code{pam_tty_audit.so} in the PAM%
|
2018-12-01 23:43:34 +00:00
|
|
|
\footnote{Linux Pluggable Authentication Modules:
|
|
|
|
\href{http://man7.org/linux/man-pages/man3/pam.3.html}
|
|
|
|
{http://man7.org/linux/man-pages/man3/pam.3.html}}
|
2018-12-02 15:02:56 +00:00
|
|
|
configurations responsible for logins, as this allows for various TTY auditing functions,
|
2018-12-03 15:38:22 +00:00
|
|
|
but I have found an even simpler approach to solving this problem in the end.
|
|
|
|
It is possible to set up the user's environment in
|
|
|
|
such a way during the build of the image, that I can enforce and determine the
|
|
|
|
location of the bash \code{HISTFILE}%
|
2018-12-01 23:43:34 +00:00
|
|
|
\footnote{This environment variable contains the path to the file bash writes command
|
2018-12-03 15:47:09 +00:00
|
|
|
history to.}
|
2018-12-01 23:43:34 +00:00
|
|
|
of the user.
|
2018-12-03 15:38:22 +00:00
|
|
|
By combining this with the inotify system built into TFW,
|
|
|
|
the framework can monitor changes made to this file and read the commands executed
|
2018-12-01 23:43:34 +00:00
|
|
|
by the user from it.
|
2018-12-02 15:02:56 +00:00
|
|
|
It is important to keep in mind that the user is able to ``sabotage'' this method%
|
2018-12-03 15:47:09 +00:00
|
|
|
\footnote{By unsetting the \code{HISTFILE} envvar for example.},
|
2018-12-02 15:02:56 +00:00
|
|
|
but that should not be an issue as this is not a feature that is intended to be
|
|
|
|
used in competitive environments (and if the users of a tutorial intentionally
|
|
|
|
break the system under themselves, well, good for them).
|
2018-12-01 23:43:34 +00:00
|
|
|
|
2018-12-03 18:14:02 +00:00
|
|
|
Another advantage of this method is that this can be applied to any interactive
|
|
|
|
application that supports logging commands executed in them in some way or another.
|
2018-12-03 15:38:22 +00:00
|
|
|
A good example would be GDB%
|
|
|
|
\footnote{\href{https://www.gnu.org/software/gdb/}{https://www.gnu.org/software/gdb/}},
|
|
|
|
which supports an option called \code{set trace-commands on}. This option flushes
|
|
|
|
command history to a file after every executed command.
|
|
|
|
This feature can be combined with the file monitoring capabilities of the framework, and now
|
|
|
|
we can even detect commands executed inside GDB by the user.
|
|
|
|
This is a good example of the flexibility provided by this solution. Feature requests
|
|
|
|
like ``I'd like to create a tutorial about <insert software here>'' are quite common, and
|
|
|
|
supporting them is really easy using this extensible system.
|
|
|
|
|
2018-12-01 19:24:24 +00:00
|
|
|
\section{Console Component}
|
|
|
|
|
|
|
|
This component is a simple textbox that can be used to display anything to the user,
|
|
|
|
from the results of unit test to the output of processes.
|
|
|
|
The console has no event handler: it is a purely frontend component which exposes a simple
|
|
|
|
API through TFW messages to write and read it's contents.
|
|
|
|
|
|
|
|
It works great when combined with the process management capabilities of the framework:
|
2018-12-03 18:14:02 +00:00
|
|
|
if configured to do so it can display the output of processes like web servers in real time.
|
2018-12-03 15:38:22 +00:00
|
|
|
When using this next to the TFW frontend editor, it allows for a development
|
2018-12-01 19:24:24 +00:00
|
|
|
experience similar to working in an IDE on your laptop.
|
|
|
|
|
2018-12-03 15:38:22 +00:00
|
|
|
Similarly to other developer tools, I chose to display this component inside the terminal
|
|
|
|
window, so that the user can switch between the two in order to conserve space using tabs.
|
|
|
|
It is also possible to configure which one should be displayed by default,
|
|
|
|
as well as switching between them mid-tutorial using API messages.
|
|
|
|
|
|
|
|
\pic{figures/console_and_editor.png}{The console displaying live process logs next to the TFW editor}
|
2018-12-01 19:24:24 +00:00
|
|
|
|
2018-12-02 15:02:56 +00:00
|
|
|
\section{Process Management}\label{processmanagement}
|
2018-12-01 19:24:24 +00:00
|
|
|
|
|
|
|
The framework includes an event handler capable of managing processes running inside
|
2018-12-02 15:44:31 +00:00
|
|
|
the \code{solvable} Docker container.
|
2018-12-03 18:14:02 +00:00
|
|
|
The capabilities of this component include the starting, stopping and restarting of processes,
|
2018-12-03 15:38:22 +00:00
|
|
|
as well as emitting the standard out or standard error logs belonging to them, even
|
|
|
|
in real-time (by broadcasting TFW messages).
|
2018-12-03 18:14:02 +00:00
|
|
|
This logging feature allows for interesting possibilities such as the handling
|
2018-12-03 15:38:22 +00:00
|
|
|
of live process output, or just requesting the logs belonging to a certain application when
|
|
|
|
some sort of event has occurred (such as on errors).
|
|
|
|
This component also can be interacted with using TFW API messages.
|
2018-12-02 23:24:06 +00:00
|
|
|
The ``Deploy'' button on the code editor uses this component to restart
|
2018-12-03 15:38:22 +00:00
|
|
|
processes, and the console component also uses this event handler to display
|
|
|
|
real-time logs.
|
2018-12-01 19:24:24 +00:00
|
|
|
|
|
|
|
The Tutorial Framework uses supervisor%
|
|
|
|
\footnote{\href{http://supervisord.org}{http://supervisord.org}}
|
|
|
|
to run multiple processes inside a Docker container
|
|
|
|
(whereas usually Docker containers run a single process only).
|
2018-12-03 15:38:22 +00:00
|
|
|
This is going to be explained further in Chapter~\ref{usingtfw}.
|
2018-12-01 19:24:24 +00:00
|
|
|
|
|
|
|
It is also possible to find out what files does a process write logs to.
|
|
|
|
Combining this with the inotify capabilities of TFW explained
|
|
|
|
briefly in~\ref{idecomponent}, it becomes possible to implement live log monitoring
|
|
|
|
in the framework.
|
|
|
|
The features involving the use of inotify were among the most difficult ones implement,
|
2018-12-03 15:38:22 +00:00
|
|
|
since the sheer number of almost impossible to debug issues that such
|
2018-12-01 19:24:24 +00:00
|
|
|
a complex system could come with.
|
|
|
|
|
2018-12-03 15:38:22 +00:00
|
|
|
I'll briefly explain such a bug, which I've found to be immensely exciting.
|
|
|
|
To understand this, it is necessary to signify, that the inotify API supplied by
|
|
|
|
the Linux kernel is not capable
|
|
|
|
of monitoring a single file, it is only able to watch whole directories.
|
|
|
|
I was unaware of this fact before running into this issue, as the Python
|
|
|
|
bindings I was using did not warn me about supplying a filename on top of
|
|
|
|
the directory path, and just stripped down the filename silently%
|
|
|
|
\footnote{In software development it is considered a bad practice to do such things
|
|
|
|
implicitly. It is better to fail loud and clear instead of trying to figure out
|
|
|
|
what the user meant to do.}.
|
2018-12-01 19:24:24 +00:00
|
|
|
During the initial development of this feature all processes inside the
|
2018-12-02 15:44:31 +00:00
|
|
|
\code{solvable} Docker container were writing their logs to files
|
2018-12-01 19:24:24 +00:00
|
|
|
in the FHS%
|
|
|
|
\footnote{The File Hierarchy Standard is a standard maintained by the Linux foundation,
|
|
|
|
defining a common directory structure for compliant systems. See
|
|
|
|
\href{https://wiki.linuxfoundation.org/lsb/fhs}{https://wiki.linuxfoundation.org/lsb/fhs}}
|
2018-12-02 15:44:31 +00:00
|
|
|
location \code{/tmp/}.
|
2018-12-01 19:24:24 +00:00
|
|
|
All logs coming from the container itself were also logged to this location.
|
2018-12-02 15:44:31 +00:00
|
|
|
This had caused an infinite recursion: when a process would write to \code{/tmp/}
|
2018-12-01 19:24:24 +00:00
|
|
|
inotify would invoke a process that would also log to that location causing the kernel to
|
2018-12-03 18:14:02 +00:00
|
|
|
emit more inotify events, which in turn would cause more and more new processes to spawn
|
2018-12-02 15:44:31 +00:00
|
|
|
and write to \code{/tmp/}, causing the whole procedure to repeat again and again.
|
2018-12-03 15:38:22 +00:00
|
|
|
This continued until my machine would start to run out of memory and begin swapping
|
2018-12-01 19:24:24 +00:00
|
|
|
pages to disk%
|
|
|
|
\footnote{When a modern operating system runs out of physical RAM, it is going to swap
|
2018-12-03 15:47:09 +00:00
|
|
|
virtual memory pages to disk so it can continue to operate --- slowly.}
|
2018-12-01 19:24:24 +00:00
|
|
|
like crazy, causing the whole system to spiral downwards
|
|
|
|
in a spectacular fashion until the whole thing managed to crash.
|
2018-12-03 15:38:22 +00:00
|
|
|
It was an event of such rare and chaotic beauty, that I often fondly recall it to this day.
|
|
|
|
After my first encounter with the bug I decided to have lunch instead.
|
2018-12-01 19:24:24 +00:00
|
|
|
Of course it would take me several hours to identify the exact causes behind this
|
2018-12-03 15:38:22 +00:00
|
|
|
fascinating phenomenon, but those were \emph{very} fun hours at least.
|
2018-12-01 23:43:34 +00:00
|
|
|
|
|
|
|
\section{FSM Management}
|
|
|
|
|
2018-12-02 15:44:31 +00:00
|
|
|
I have already mentioned the event handler called \code{FSMManagingEventHandler},
|
2018-12-03 18:14:02 +00:00
|
|
|
which is responsible for managing the framework FSM\@.
|
2018-12-03 15:38:22 +00:00
|
|
|
For completeness I chose to include it in this chapter as well.
|
2018-12-01 23:43:34 +00:00
|
|
|
The API it exposes through TFW messages allows client code to attempt stepping the
|
|
|
|
state machine.
|
|
|
|
As previously explained this is something that is considered to be a \emph{privileged}
|
|
|
|
operation in case authentication is enabled, causing the event handler to accept
|
|
|
|
digitally signed messages only.
|
|
|
|
In the event of a successful FSM step, this component is going to broadcast
|
|
|
|
a message showcased as an example in the beginning of Chapter~\ref{atouroftfw}.
|
|
|
|
|
2018-12-02 15:02:56 +00:00
|
|
|
\section{Web Component}
|
|
|
|
|
|
|
|
The web component allows developers to configure what web application should
|
|
|
|
be displayed on the frontend.
|
|
|
|
There are two options for doing this:
|
|
|
|
\begin{enumerate}
|
|
|
|
\item Supply the URL of a web application served from the
|
|
|
|
Docker container to be displayed
|
|
|
|
\item Implement an Angular component on the frontend and display it
|
|
|
|
\end{enumerate}
|
|
|
|
|
|
|
|
In most cases, when developers make tutorials on Python and Java topics for
|
|
|
|
example, they will implement a web application in the respective programming
|
|
|
|
language the exercise is about and most developers have no experience in
|
|
|
|
Angular development.
|
|
|
|
This is why more often then not the fist option is chosen.
|
|
|
|
This feature is implemented by embedding a standard HTML iframe%
|
|
|
|
\footnote{\href{https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe}
|
|
|
|
{https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe}}
|
|
|
|
inside the frontend, which is controllable by TFW
|
|
|
|
(as an example it can be reloaded or navigated from the framework).
|
|
|
|
|
|
|
|
Watchful readers might now be thinking about how does ``controlled by the framework''
|
|
|
|
thing work with the Same Origin Policy%
|
|
|
|
\footnote{\href{https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy}
|
|
|
|
{https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin\_policy}}
|
|
|
|
being in effect?
|
2018-12-03 18:14:02 +00:00
|
|
|
The answer is that developers must use a \emph{relative URL}, that is an URL relative
|
2018-12-03 15:38:22 +00:00
|
|
|
to the entry point of the TFW frontend itself.
|
2018-12-02 15:02:56 +00:00
|
|
|
To allow serving several web applications from a single port the framework
|
|
|
|
supports optional reverse-proxy configurations through the nginx%
|
|
|
|
\footnote{\href{http://nginx.org}{http://nginx.org}} web server ran by the framework.
|
2018-12-03 15:38:22 +00:00
|
|
|
More on this in a Chapter~\ref{usingtfw}.
|
2018-12-02 15:02:56 +00:00
|
|
|
|
|
|
|
\section{Various Frontend Features}
|
|
|
|
|
2018-12-03 15:38:22 +00:00
|
|
|
The Angular frontend of the framework features several different layouts.
|
2018-12-03 18:14:02 +00:00
|
|
|
These layouts are useful to accommodate different workflows for users,
|
|
|
|
such as the previous example of editing code and being able to view the
|
2018-12-02 15:02:56 +00:00
|
|
|
result of said code in real time next to the editor.
|
|
|
|
Another example would be editing Ansible playbooks in the file editor,
|
|
|
|
and then trying to run them in the terminal.
|
|
|
|
There are also almost full screen views for each component that makes sense
|
2018-12-03 15:38:22 +00:00
|
|
|
to be used like that, for instance the code editor can be used to conveniently
|
|
|
|
edit larger files this way.
|
2018-12-02 15:02:56 +00:00
|
|
|
|
2018-12-03 15:38:22 +00:00
|
|
|
The frontend was designed in a way to be fully responsive in window sizes
|
2018-12-03 18:14:02 +00:00
|
|
|
that still keep the whole thing usable (i.e.\ it would not be practical to start
|
2018-12-03 15:38:22 +00:00
|
|
|
solving TFW tutorials on a smart phone, simply because of size limits, so such small screens are
|
2018-12-02 15:02:56 +00:00
|
|
|
not supported, but the frontend still behaves as expected on small laptops or bigger tablets).
|
2018-12-03 18:14:02 +00:00
|
|
|
This is not an easy thing to implement and maintain due to the lots of small
|
|
|
|
incompatibilities between browsers given the complexity of the frontend.
|
2018-12-02 15:02:56 +00:00
|
|
|
|
|
|
|
Just remember that a few years ago the clearfix%
|
|
|
|
\footnote{\href{https://stackoverflow.com/questions/8554043/what-is-a-clearfix}
|
|
|
|
{https://stackoverflow.com/questions/8554043/what-is-a-clearfix}}
|
|
|
|
hack was the industry standard in creating CSS layouts.
|
|
|
|
The situation has improved \emph{a lot} since then with flexboxes
|
|
|
|
and grid layouts despite the sheer chaos that is generally involved in web
|
2018-12-03 18:14:02 +00:00
|
|
|
standardization efforts, but CSS especially%
|
2018-12-02 15:02:56 +00:00
|
|
|
\footnote{\href{https://developer.mozilla.org/en-US/docs/Web/CSS/CSS3}
|
|
|
|
{https://developer.mozilla.org/en-US/docs/Web/CSS/CSS3}}.
|
|
|
|
|
|
|
|
The framework frontend is built on grid layout and flexboxes%
|
|
|
|
\footnote{\href{https://developer.mozilla.org/en-US/docs/Web/CSS/CSS\_Grid\_Layout}
|
|
|
|
{https://developer.mozilla.org/en-US/docs/Web/CSS/CSS\_Grid\_Layout}},
|
|
|
|
which gives us the best hopes of being able to maintain it down the line.
|
|
|
|
It would involve unimaginable horrors to support this multi-layout
|
2018-12-03 15:38:22 +00:00
|
|
|
frontend on older browsers without flexboxes and grid, so I have
|
|
|
|
decided to avoid spending development time on these and make sure that
|
|
|
|
it works like a charm in reasonably modern browsers.
|
2018-12-02 15:02:56 +00:00
|
|
|
Arguably this is a good thing, as people should keep their browsers up to date to
|
|
|
|
follow frequent security patches anyway, so let this serve as a reminder to
|
|
|
|
developers looking to get into IT security that the first step is to
|
|
|
|
keep your software up to date.
|
|
|
|
|
2018-12-03 15:38:22 +00:00
|
|
|
\pic{figures/tfw_grid.png}{The grid layout of the TFW frontend showcased from developer tools}
|
|
|
|
|
|
|
|
There are several additional APIs exposed by the frontend,
|
|
|
|
which include the changing of layouts, selecting the terminal or console
|
2018-12-02 15:02:56 +00:00
|
|
|
component to be displayed, the possibility of dynamically modifying
|
2018-12-03 18:14:02 +00:00
|
|
|
frontend configuration values (such as the frequency of auto-saving the files in the editor)
|
2018-12-01 23:43:34 +00:00
|
|
|
and more.
|
2018-12-03 15:38:22 +00:00
|
|
|
|
2018-12-03 18:14:02 +00:00
|
|
|
To accommodate communication with the TFW server, the frontend of the framework
|
2018-12-03 15:38:22 +00:00
|
|
|
comes with some library code which can be used to send and receive TFW messages.
|
|
|
|
This code is mostly WebSockets combined with RxJS%
|
|
|
|
\footnote{\href{https://rxjs-dev.firebaseapp.com}{https://rxjs-dev.firebaseapp.com}},
|
|
|
|
which makes it easier to write completely asynchronous, callback based code.
|
|
|
|
The observables%
|
|
|
|
\footnote{\href{http://reactivex.io/documentation/observable.html}
|
|
|
|
{http://reactivex.io/documentation/observable.html}}
|
|
|
|
provided by RxJS are used all around the TFW frontend, as our library code
|
|
|
|
exposes the operation of receiving data from WebSockets as observables.
|
|
|
|
Client code can subscribe to these observables using callbacks,
|
|
|
|
which will be invoked when the observable emits a new value
|
|
|
|
(i.e.\ when a new message was received on the WebSocket).
|
|
|
|
When using Angular it is generally a good idea to get familiar with reactive programming,
|
|
|
|
because it is very easy to get lost in the callback hell without it.
|