Changing configuration while it runs¶
Doppel does not need restarting to change what it serves. There are three ways in, and they suit different situations.
| Way in | Reaches | Use it when |
|---|---|---|
doppel config reload |
The control socket | You edited the file, or another instance changed the database |
POST /api/v1/config/reload |
The admin listener | The same, from something that only has HTTP |
POST /api/v1/proxies and friends |
The admin listener | You want to change one proxy without touching a document |
All three end in the same place: a whole new configuration is loaded, validated and compiled, and only then swapped in. A failure anywhere leaves the running one untouched, and requests in flight finish against the configuration they started with.
Editing the file and reloading¶
$EDITOR main.yaml
doppel config reload --socket /tmp/doppel.sock
reloaded: revision 7a3f2c1908e4bb52, 3 proxies
If the edit was bad, nothing changes and every violation is listed:
reload rejected: CONFIG_INVALID
proxies[0].latency.min: min must be <= max
admin.access.create: `create` must not be public: ...
Some sections cannot take effect without a restart, because their listener is already bound. Those are reported rather than silently ignored:
reloaded: revision 91b0..., 3 proxies
note: these sections changed but only take effect after a restart: server, admin
A change to admin.tokens or admin.access does take effect -- what needs a
restart is the host, the port and whether the listener runs at all.
Issuing a token¶
The admin API needs a token, and the first one has to come from somewhere. The control socket is that somewhere: it is reachable by anyone who can read the socket file, which is the same trust boundary as editing the configuration.
doppel token add --name ci --group admin
token `ci` issued to group `admin`, revision 4a19...
it is in force now, and this is the only time it is shown:
3f2504e0-4f89-41d3-9a0c-0305e82c3301
The value is generated, written into the stored configuration, and brought into force before the command answers -- so it works on the next request rather than after someone remembers to reload.
Capture it without a parser:
TOKEN=$(doppel token add --name ci --group admin | tail -1)
--group defaults to user, which carries no write access under the default
access block. A token meant for administration needs --group admin spelled
out.
Changing one proxy over HTTP¶
curl -s -H "X-Proxy-Authorization: Bearer $TOKEN" \
localhost:8081/api/v1/proxies
{"proxies":[{"revision":"afecb8ebcfbd4471","proxy":{"name":"backend", ...}}]}
Each proxy carries its own revision, also returned in ETag. Send it back to
make the write conditional:
curl -s -X PUT \
-H "X-Proxy-Authorization: Bearer $TOKEN" \
-H 'If-Match: "afecb8ebcfbd4471"' \
-H 'Content-Type: application/json' \
-d '{"name":"backend","type":"http","url":"https://api.example.com/v2/","resolve":{"type":"default"}}' \
localhost:8081/api/v1/proxies/backend
A 409 means someone else changed that proxy since you read it. Read it again,
reapply your change, and retry -- do not strip the header.
The revision is per proxy, so two people editing two different proxies do not collide.
Uploading a template at runtime¶
A mock naming template: greeting.json.j2 reads that file at request time, so
it can be uploaded to a running process:
curl -s -X PUT \
-H "X-Proxy-Authorization: Bearer $TOKEN" \
--data-binary @greeting.json.j2 \
localhost:8081/api/v1/proxies/backend/templates/greeting.json.j2
No reload is needed -- the next matching request renders the new file.
Only files a proxy's configuration actually names can be uploaded. An upload for a name no mock declares is refused, so the templates directory cannot become a place to put arbitrary files.
Two instances, one database¶
With --store postgres, both instances read the same configuration and agree
on its revision. What they do not get is automatic propagation: each keeps
serving what it compiled until told to reload.
# On the instance making the change
doppel token add --name ci --group admin
# On every other instance
doppel config reload
A rolling change is a write followed by a reload on each instance. Nothing
coordinates them, and nothing stops two instances running different revisions
in between; GET /api/v1/status on each reports which one it is holding.
See Storing configuration in PostgreSQL.
Checking what is running¶
curl -s localhost:8081/api/v1/status
{
"uptime_seconds": 412,
"revision": "598781ecf030d385",
"proxies": [
{"name": "production", "upstream": "http://127.0.0.1:45799/", "resolve": "default", "mocks": 0},
{"name": "offline", "upstream": "http://127.0.0.1:45799/", "resolve": "header:X-Backend", "mocks": 1}
]
}
proxies is the list, not a count, and each entry says how it is reached --
which is the quickest way to confirm a resolution header is spelled the way you
think it is. Credentials in an upstream URL are redacted here.
/api/v1/status needs no token by default, so it is usable as a health check and as a
way to confirm a rolling reload actually reached every instance.