Resolve "support multiple channels" (#32)
Closes #32 (closed)
This MR adds functionality for supporting an arbitrary (and constantly growing) number of channels on a single signalboost server. Due to upatread limitations beyond our control, it is impossible to run multiple daemonized instances of signal-cli each listening for messages for different phone numbers on the same "computer." [1]
To get around this, we choose to take a liberal interpretation of the word "computer." Every time we wish to allocate a new signalboost channel for a new phone number, we dynamically allocate a new docker container and run an isolated version of signal-cli, dbus, and our app inside of it. This likely won't hold up at massive scale, but since we are not at "massive scale" yet (and "premature optimization is the root of all evil"), this seems good enough to buy us time to deploy the app and gather feedback from users while we develop a more performant solution (assuming we ever need to).
With that in mind, the commits in this Merge Request do the following:
-
extract an orchestrator service that can:
- run in its own docker container containing one instance each of dbus daemon, signal-cli, and the orchestrator service node app itself (where each process is controlled by a supervisord process started in the docker entrypoint)
- access shared app, db, and keystore data via mounted volumes
- expose API calls that provision new phone numbers and activate new channels:
- "provisioning" means purchasing form twilio and registering with signal
- when a new channel is "activated", the orchestrator dynamically allocates a new container for a dispatcher service
- there is only one channel per phone number and only one dispatcher service container per channel
-
extract a dispatcher service that:
- also runs in its own docker container with access to shared app/db/keystore data via volumes
- also contains dbus, signal-cli, and a node-app controlled by supervisord
- does not expose an API
- has the sole purpose of dispatching messages and commands that are recieved on the channel/ phone number for which it was provisiioned
-
provide the docker-compose files, supervisord configurations, and bash scripts necessary to run the app and its test harness in the new, dockerized environment
-
adds a
channel
module to the dispatcher service with logic for:- activating a new channel and allocating the dispatcher container for it
- initializing all phone numbers and channels on deplly/startup (ie: finding phone numbers and channels that are persisted in the database and registering and activating them, respectively -- and idempotently)
-
add a
boost
cli tool to allow sysadmins to easily provision phone numbers and activate channels from their local machine (and documents it in the README) -
provide a custom logger that prepends container/service info to logs
-
add retry logic for db and dbus connections so container/process spinup won't halt on failed intial connection attempt
-
add integration tests for the above functionality (our first)
[1] Short version: signal-cli in daemon mode is a long-running process that is initialized with one
and only one phone number. The DBus interface that it uses to send and receive messages can have
one and only one process that owns it. Thus, we may freely start up a signal-cli process for
+15555555555, but when we try to start up a second for +14444444444, DBus throws an error, because
the interface we want to use is already connected to the process corresponding to +15555555555.
We cannot fix this without introducing significant breaking changes to signal-cli, or introducing
an entire new multinumber
mode that may or may not be accepted upstream. In the instance of efficiency,
we chose a clever workaround (for now).