Case Study: Getting Through a Vendor Upgrade With AfterConfig Guards
This case uses the examples/vendor-app folder that ships in every download archive, so you can run each step yourself. The commands and output below come from a real run of the downloaded aftermarket 0.4.0.
The situation
A team runs a vendor’s app. It reads config.json, and each upgrade replaces that file. The team needs seven changes:
- a longer timeout
- more workers
- JSON logs
- no telemetry
- their own database password
- a bigger connection pool
- a body limit the vendor doesn’t offer yet
They keep those changes in overlay.yaml and never touch the vendor’s file. Most changes carry a guard, the value they expect to find.
Before going live
config check reads the overlay against the vendor’s 1.0 file:
$ aftermarket config check --overlay examples/vendor-app/overlay.yaml
examples/vendor-app/overlay.yaml: 7 change(s) to examples/vendor-app/1.0/config.json (json)
1. line 8: set server.timeout to 120, guarded
2. line 11: set server.workers to 8, guarded
3. line 14: set logging.format to "json", guarded
4. line 17: set telemetry.enabled to false, guarded
5. line 20: set database.password to REDACTED
6. line 21: set database.pool to 25, guarded
7. line 24: set server.max_body to "10MiB", guarded: must be absent
ok
config diff shows what the result will change, field by field:
$ aftermarket config diff --overlay examples/vendor-app/overlay.yaml
~ server.timeout: 30 -> 120
~ server.workers: 4 -> 8
+ server.max_body: "10MiB"
~ logging.format: "text" -> "json"
~ telemetry.enabled: true -> false
~ database.password: REDACTED -> REDACTED
~ database.pool: 10 -> 25
The password never appears, in either direction. Then they apply:
$ aftermarket config apply --overlay examples/vendor-app/overlay.yaml --out config.json
wrote config.json: 7 change(s) applied, 0 already in place
Pointing --out at the vendor’s file is refused:
$ aftermarket config apply --overlay examples/vendor-app/overlay.yaml --out examples/vendor-app/1.0/config.json
aftermarket: the output is the vendor's file itself; AfterConfig never writes the base, so choose another --out
The upgrade
Version 1.1 arrives. Before switching, they run the same overlay against the new file:
$ aftermarket config apply --overlay examples/vendor-app/overlay.yaml --base examples/vendor-app/1.1/config.json --out config.json
examples/vendor-app/overlay.yaml: 2 of 7 change(s) can't be applied to examples/vendor-app/1.1/config.json
line 8: set expects server.timeout to be 30, but the base has 60
line 14: set expects logging.format to be "text", but the base has "json"
nothing was written
The config.json they wrote earlier is still the same, byte for byte. Two of their seven changes rested on vendor defaults that 1.1 changed, and AfterConfig won’t guess what they want now.
Looking at the two lines, the decisions are quick. They still want 120 seconds, so the timeout guard now expects 60. And the vendor logs JSON by default now, so the logging change isn’t needed any more. The overlay for 1.1 differs from the old one in three places:
< base: 1.0/config.json
> base: 1.1/config.json
< - set: {path: server.timeout, value: 120, expect: 30}
> - set: {path: server.timeout, value: 120, expect: 60}
< - set: {path: logging.format, value: json, expect: text}
$ aftermarket config apply --overlay overlay-1.1.yaml --out config.json
wrote config.json: 6 change(s) applied, 0 already in place
The result has their six changes, and everything new in 1.1 that they didn’t touch: a new http2 setting and a new telemetry endpoint, which stays off.
{
"server": {
"port": 8080,
"timeout": 120,
"workers": 8,
"http2": true,
"max_body": "10MiB"
},
"logging": {
"level": "info",
"format": "json"
},
"telemetry": {
"enabled": false,
"endpoint": "https://telemetry.example.com/v2"
},
"database": {
"url": "postgres://[email protected]/app",
"password": "from-our-vault",
"pool": 25
}
}
In CI
The example’s fixture locks the behaviour in. One case checks the result on 1.0, one checks that a changed timeout stops apply, and one checks that a vendor adding max_body itself stops it too:
$ aftermarket config test --overlay examples/vendor-app/overlay.yaml --fixture examples/vendor-app/fixture.yaml
PASS the overlay applies to the 1.0 config
PASS an upgrade that changes the timeout default stops apply
PASS a vendor that adds max_body itself stops apply
3 case(s), 0 failed
The full format is in the AfterConfig reference. The use cases page has a short version of this case.