Running your application
Databases per tenant
Let your running application ask Brewless for a Serverless SQL Database per tenant of its own, signed with a key only the two hold.
On this page
An application that gives every customer of its own a database of its own has to make that database while it runs, when a customer signs up. On a Serverless SQL Database at Scaleway, CREATE DATABASE over SQL is never allowed: each database is a resource of its own, with its own address, made through the Scaleway API. Your application has no key for that API. So it asks Brewless, and Brewless makes the database in your Scaleway project.
In brewless.yml
environments:
production:
database: shop-main
databases:
prefix: shop-
The environment's own database has to be a Serverless SQL Database: a Managed Database is refused. The prefix starts with a lowercase letter, has lowercase letters, digits and dashes, ends in a dash and is at most 32 long. A dash and not an underscore: Scaleway takes only letters, digits and dashes in the name of a Serverless SQL Database, and the database inside has the same name. Every database the application asks for has a name that starts with it.
The next deploy gives the application what it needs to ask (see below). The variables go next to the environment's own variables, so this needs those kept through Brewless (push them once with brewless env:push).
databases: false takes the prefix away: the application can ask for nothing more. The databases it made stay at Scaleway and stay recorded. Another prefix is refused while databases of the one that stands are recorded, because they would no longer be reachable by name.
What the application gets
Every container of the environment gets three variables, the one that serves visitors too, because an application may make a customer's database in a web request:
| Variable | What it is |
|---|---|
BREWLESS_API_URL |
The address of your organisation at Brewless, such as https://acme.brewless.eu |
BREWLESS_ENVIRONMENT |
The id of the environment |
BREWLESS_DATABASES_KEY |
A random key of 32 bytes, base64, made once per environment |
The key is kept in your Secret Manager like the other keys of the environment. Brewless keeps it too, encrypted, to check the signature of a request. It is never shown or logged.
A new database signs in exactly like the environment's own: with DB_USERNAME and DB_PASSWORD, the identity Brewless made for the environment, which may use every Serverless SQL Database of the project. Only the host differs. Making the tables in it is your application's work; Brewless never connects to it.
With Laravel, brewless/laravel 0.7.1 or later (0.7.0 refuses a name with a dash) reads the three variables and signs for you:
use Brewless\Laravel\Databases\DatabaseClient;
$database = resolve(DatabaseClient::class)->create('shop-'.$tenant->id);
$database = resolve(DatabaseClient::class)->waitUntilReady($database->name);
// $database->host and $database->port, with DB_USERNAME and DB_PASSWORD
The requests
Each request carries three headers:
| Header | Value |
|---|---|
X-Brewless-Environment |
BREWLESS_ENVIRONMENT |
X-Brewless-Timestamp |
The time in whole seconds since 1970 |
X-Brewless-Signature |
The HMAC-SHA256 below, in hexadecimal |
The signature is over four lines: the method, the path, the time and the SHA-256 of the body, with the key from BREWLESS_DATABASES_KEY as it stands in the variable (not decoded from base64):
hash_hmac('sha256', $method."\n".$path."\n".$timestamp."\n".hash('sha256', $body), $key);
The path starts with /api/; the body is empty for every request below. A request more than five minutes off, with a wrong signature, or for an environment without databases is answered 401 with the same sentence, whatever was wrong.
| Request | Answer |
|---|---|
PUT /api/runtime/databases/{name} |
Makes the database when it does not exist. 202 while it is being made, 200 once it is there |
GET /api/runtime/databases/{name} |
Where it stands, 200 |
DELETE /api/runtime/databases/{name} |
Removes it at Scaleway now, with everything in it. 202 |
PUT and GET answer with {"name", "status", "host", "port"}. The status is creating, ready, deleting or error; host and port are null until Scaleway names them. DELETE answers with {"name", "status": "deleting"}. Asking for the same name again makes nothing twice.
Limits
- The name has lowercase letters, digits and dashes, at most 63, and starts with the prefix. An underscore is refused with
422before Scaleway is asked, since Scaleway would refuse it. In SQL, quote the name ("shop-1"): PostgreSQL reads a bare dash as a minus. When Scaleway refuses a name for good, Brewless keeps no record of it, so the name and the prefix stay free. It is never the name of the environment's own database, and a database of that name that Brewless did not make for this environment is never taken over: that is refused with422. GETandDELETEonly reach databases Brewless made through this route for this environment; anything else is404. The environment's own database can never be removed this way.- A new database scales between the same vCPUs as the environment's own database, as it was set up.
- Each environment may make 120 requests a minute. When Scaleway cannot be reached or refuses, the answer is
503: ask again. - Every database made and removed is in the audit trail, with the name and the environment. Each is a resource of the environment, so the console lists it and an export names it.