diff --git a/.github/workflows/golang-test-linux.yml b/.github/workflows/golang-test-linux.yml index 450c44aea..20f53bddf 100644 --- a/.github/workflows/golang-test-linux.yml +++ b/.github/workflows/golang-test-linux.yml @@ -426,8 +426,11 @@ jobs: if: matrix.store == 'mysql' run: docker pull mlsmaycon/warmed-mysql:8 + - name: Generate current sync wire fixtures + run: go run ./management/server/testdata/sync_request_wire/generate.go + - name: Test - run: | + run: | CGO_ENABLED=1 GOARCH=${{ matrix.arch }} \ NETBIRD_STORE_ENGINE=${{ matrix.store }} \ CI=true \ diff --git a/management/server/testdata/sync_request_wire/.gitignore b/management/server/testdata/sync_request_wire/.gitignore new file mode 100644 index 000000000..349af4ad6 --- /dev/null +++ b/management/server/testdata/sync_request_wire/.gitignore @@ -0,0 +1,2 @@ +current.bin +android_current.bin diff --git a/management/server/testdata/sync_request_wire/README.md b/management/server/testdata/sync_request_wire/README.md index e6bd84d7d..e99cbd353 100644 --- a/management/server/testdata/sync_request_wire/README.md +++ b/management/server/testdata/sync_request_wire/README.md @@ -1,28 +1,31 @@ # SyncRequest wire-format fixtures -These files are the frozen byte-for-byte contents of the `SyncRequest` proto a -netbird client of each listed version would put on the wire. `server_sync_legacy_wire_test.go` +These files are the byte-for-byte contents of the `SyncRequest` proto a netbird +client of each listed version would put on the wire. `sync_legacy_wire_test.go` decodes each file, wraps it in the current `EncryptedMessage` envelope and replays it through the in-process gRPC server to prove that the peer-sync fast path does not break historical clients. File | Client era | Notes -----|-----------|------ -`v0_20_0.bin` | v0.20.x | `message SyncRequest {}` — no fields on the wire. Main Sync loop in v0.20 gracefully skips nil `NetworkMap`, so the fixture is expected to get a full map (empty Sync payload → cache miss → slow path). -`v0_40_0.bin` | v0.40.x | First release with `Meta` at tag 1. v0.40 calls `GrpcClient.GetNetworkMap` on every OS; fixture must continue to produce a full map. -`v0_60_0.bin` | v0.60.x | Same SyncRequest shape as v0.40 but tagged with a newer `NetbirdVersion`. -`current.bin` | latest | Fully-populated `PeerSystemMeta`. -`android_current.bin` | latest, Android | Same shape as `current.bin` with `GoOS=android`; the server must never take the fast path even after the cache is primed. +`v0_20_0.bin` | v0.20.x | `message SyncRequest {}` — no fields on the wire. Main Sync loop in v0.20 gracefully skips nil `NetworkMap`, so the fixture is expected to get a full map (empty Sync payload → cache miss → slow path). **Checked in — frozen snapshot.** +`v0_40_0.bin` | v0.40.x | First release with `Meta` at tag 1. v0.40 calls `GrpcClient.GetNetworkMap` on every OS; fixture must continue to produce a full map. **Checked in — frozen snapshot.** +`v0_60_0.bin` | v0.60.x | Same SyncRequest shape as v0.40 but tagged with a newer `NetbirdVersion`. **Checked in — frozen snapshot.** +`current.bin` | latest | Fully-populated `PeerSystemMeta`. **Not checked in — regenerated at CI time by `generate.go`.** +`android_current.bin` | latest, Android | Same shape as `current.bin` with `GoOS=android`; the server must never take the fast path even after the cache is primed. **Not checked in — regenerated at CI time by `generate.go`.** ## Regenerating -The generator is forward-compatible: it uses the current proto package with only -the fields each era exposes. Re-run after an intentional proto change: +`generate.go` writes only `current.bin` and `android_current.bin`. CI invokes it +before running the management test suite: -``` +```sh go run ./management/server/testdata/sync_request_wire/generate.go ``` -and review the byte diff. An unexpected size change or diff indicates the wire -format has drifted — either adjust the generator (if the drift is intentional -and backwards-compatible) or revert the proto change (if it broke old clients). +Run the same command locally if you are running the wire tests by hand. + +The three legacy fixtures are intentionally frozen. Do not regenerate them — +their value is that they survive proto changes unchanged, so a future proto +change that silently breaks the old wire format is caught by CI replaying the +frozen bytes and failing to decode them. diff --git a/management/server/testdata/sync_request_wire/generate.go b/management/server/testdata/sync_request_wire/generate.go index e25d6e16f..355d17da4 100644 --- a/management/server/testdata/sync_request_wire/generate.go +++ b/management/server/testdata/sync_request_wire/generate.go @@ -1,17 +1,15 @@ //go:build ignore -// generate.go produces the frozen SyncRequest wire-format fixtures used by -// server_sync_legacy_wire_test.go. Run with: +// generate.go produces the SyncRequest wire-format fixtures that the current +// netbird client (and the android variant) put on the wire. These two files +// are regenerated at CI time — run with: // // go run ./management/server/testdata/sync_request_wire/generate.go // -// Each fixture is the proto-serialised SyncRequest a client of the indicated -// netbird version would put on the wire. protobuf3 is forward-compatible: an -// old client's fields live at stable tag numbers, so marshalling a current -// SyncRequest that sets only those fields produces bytes byte-for-byte -// compatible with what the old client produced. The fixtures are checked in -// so a future proto change that silently breaks the old wire format is caught -// in CI. +// The legacy fixtures (v0_20_0.bin, v0_40_0.bin, v0_60_0.bin) are frozen +// snapshots of what older clients sent. They are checked in and intentionally +// never regenerated here, so a future proto change that silently breaks the +// old wire format is caught by CI replaying the frozen bytes. package main import ( @@ -32,33 +30,6 @@ func main() { } fixtures := map[string]*mgmtProto.SyncRequest{ - // v0.20.0: message SyncRequest {} — no fields on the wire. - "v0_20_0.bin": {}, - - // v0.40.0: Meta added at tag 1. Older meta fields only. - "v0_40_0.bin": { - Meta: &mgmtProto.PeerSystemMeta{ - Hostname: "v40-host", - GoOS: "linux", - OS: "linux", - Platform: "x86_64", - Kernel: "4.15.0", - NetbirdVersion: "0.40.0", - }, - }, - - // v0.60.0: same wire shape as v0.40.0 for SyncRequest. - "v0_60_0.bin": { - Meta: &mgmtProto.PeerSystemMeta{ - Hostname: "v60-host", - GoOS: "linux", - OS: "linux", - Platform: "x86_64", - Kernel: "5.15.0", - NetbirdVersion: "0.60.0", - }, - }, - // current: fully-populated meta a modern client would send. "current.bin": { Meta: &mgmtProto.PeerSystemMeta{