Architecture
============
Code Base
---------
There are two top level component of RestKnot. First is ``resknot-api`` which
exposes the REST API to be consumed by the client. The app is built on top of
Flask. If you want to familiarize yourself with the codebase, the best way to start
is the controllers (``api/app/controllers``). ``domain.py`` acts as
an entry point after user registration (``user.py``). It serves the creation of
new zone and default records.
The second component is the ``agent``. It receives a JSON message from the
broker, parses it then sends a command to the Knot server via
``libknot.py``. It's a simple app and the main entry point is ``agent/dnsagent/start.py``.
The Ecosystem
-------------
This part describes how ``resknot-api`` talks to ``resknot-agent`` and other
applications. You can also see the :ref:`deploy:Basic Deployment Architecture` to learn the basic form of RESTKnot
deployment.
.. figure:: /img/architecture.png
:width: 400px
:alt: RESTKnot Architecture
A whole ecosystem.
To learn how each application talks with each other, let's start with a request
from the ``client``. Such as asking for new zone creation. The request is a REST API call
that is exposed by ``restknot-api``. While interacting with ``client``, it also
talks with ``database`` to store and retrieves new zones, user, etc. Then the
``restknot-api`` send a JSON message to ``message broker``.
.. figure:: /img/knot-master-slave.png
:width: 250px
:alt: Knot DNS master-slave
Knot DNS master-slave
In turn, The ``broker`` pushes the message to all ``resknot-agent``
(including the slaves). The ``agent`` parses the message and send a command to
``Knot DNS server`` using ``libknot.py``
For creating a new zone. ``resknot-api`` will send 4 messages. The first message
contains data to create zone config, it will be consumed by both ``master`` and
``slave`` agent.
.. raw:: html
first message
.. code-block:: json
{
"agent": {
"agent_type": [
"master",
"slave"
]
},
"knot": [
{
"cmd": "conf-begin",
"zone": "example.com"
},
{
"cmd": "conf-set",
"section": "zone",
"item": "domain",
"data": "example.com"
},
{
"cmd": "conf-commit",
"zone": "example.com"
}
]
}
.. raw:: html
The second message contains data to create default records (SOA, NS,
CNAME). It's only consumed by ``master``.
.. raw:: html
second message
.. code-block:: json
{
"agent": {
"agent_type": [
"master"
]
},
"knot": [
{
"cmd": "zone-begin",
"zone": "example.com"
},
{
"cmd": "zone-set",
"zone": null,
"owner": "@",
"rtype": "SOA",
"ttl": "3600",
"data": "one.dns.id. hostmaster.dns.id. 20 ..."
},
{
"cmd": "zone-set",
"zone": null,
"owner": "@",
"rtype": "NS",
"ttl": "3600",
"data": "one.dns.id."
},
{
"//": "some messages omitted for brevity"
},
{
"cmd": "zone-commit",
"zone": "example.com"
}
]
}
.. raw:: html
The third message contains data to set additional config such ``serial-policy``,
``notify``, and ``ACL``. The third and fourth message contains similar data,
only it's refined toward ``master`` agent or ``slave`` agent. This is why we
have to set ``RESTKNOT_AGENT_TYPE`` correctly. Otherwise, the zone can not be
created.
.. raw:: html
third message
.. code-block:: json
[
{
"item": "notify",
"data": "slave1",
"cmd": "conf-set",
"section": "zone",
"zone": "example.com"
},
{
"item": "notify",
"data": "slave2",
"cmd": "conf-set",
"section": "zone",
"zone": "example.com"
},
{
"item": "acl",
"data": "slave1",
"cmd": "conf-set",
"section": "zone",
"zone": "example.com"
},
{
"item": "acl",
"data": "slave2",
"cmd": "conf-set",
"section": "zone",
"zone": "example.com"
}
]
.. raw:: html
The fourth message contains similar data as the third, but it's only geared
toward ``slave``. Such ``notify`` keyword being changed to ``master``. These
keyword differences also represented in ``knot.conf`` and
``config.yml``. To learn more, take a look at ``knot.conf.master`` and
``knot.conf.slave`` in ``/deploy/examples/``.
.. raw:: html
fourth message
.. code-block:: json
{
"item":"master",
"data":"master1",
"cmd":"conf-set",
"section":"zone",
"zone":"example.com"
}
.. raw:: html
If we look closely at the figure above. Message broker pushing the message to all
``agent`` (represented by a straight line). In zone creation example, all the zone
config data is sent to all ``agent``. This is due to the fact that ``knot DNS
slaves`` can't create zone config automatically, so the ``agent`` there for it.
After the config is created in the ``slaves``. It starts talking with ``master`` via
``AXFR`` to receive zone's records and updates (represented by dotted line). But
before any zone's config is created, The ``slave`` can't do anything.
If config creation is done automatically in the ``slaves`` via ``AXFR`` maybe we
can get rid of the ``agent`` in the ``slaves`` entirely.