Add Gateway traffic totals and dashboard chart
Build and Deploy Gateway / build-and-push (push) Successful in 14s
Build and Deploy Gateway / deploy (push) Successful in 6s

This commit is contained in:
2026-08-08 00:23:55 +03:00
parent 39f3467f9b
commit 4c61a04dc7
10 changed files with 815 additions and 271 deletions
+75 -3
View File
@@ -196,6 +196,15 @@ test('device traffic totals persist exact deltas across polls and process epochs
error: null,
proxy: { lastObservedAt: observedAt, error: null },
});
assert.deepEqual(snapshot.traffic, {
gatewayBytes: '9007199254741093',
proxyBytes: '3000',
totalBytes: '9007199254744093',
gatewayObservedAt: observedAt,
proxyObservedAt: observedAt,
observedAt,
history: [],
});
snapshot = await service.refresh();
assert.equal(snapshot.devices[0].uploadBytes, '9007199254740993');
@@ -216,6 +225,8 @@ test('device traffic totals persist exact deltas across polls and process epochs
assert.equal(snapshot.devices[0].downloadBytes, '150');
assert.equal(snapshot.devices[0].proxyUploadBytes, '1010');
assert.equal(snapshot.devices[0].proxyDownloadBytes, '2050');
assert.equal(snapshot.traffic.gatewayBytes, '9007199254741145');
assert.equal(snapshot.traffic.proxyBytes, '3060');
service = createService();
snapshot = await service.refresh();
@@ -239,6 +250,7 @@ test('device traffic totals persist exact deltas across polls and process epochs
assert.equal(snapshot.devices[0].downloadBytes, '170');
assert.equal(snapshot.devices[0].proxyUploadBytes, '1013');
assert.equal(snapshot.devices[0].proxyDownloadBytes, '2054');
assert.equal(snapshot.traffic.totalBytes, '9007199254744242');
traffic = {
...traffic,
@@ -250,6 +262,7 @@ test('device traffic totals persist exact deltas across polls and process epochs
traffic = {
...traffic,
observedAt: '2026-08-07T12:00:15.000Z',
devices: [{
...traffic.devices[0],
uploadBytes: '12',
@@ -265,12 +278,17 @@ test('device traffic totals persist exact deltas across polls and process epochs
assert.equal(snapshot.devices[0].proxyDownloadBytes, '2054');
assert.equal(snapshot.source.traffic.error, null);
assert.match(snapshot.source.traffic.proxy.error, /уменьшился/);
assert.equal(snapshot.traffic.proxyBytes, '3067');
assert.equal(snapshot.traffic.gatewayObservedAt, '2026-08-07T12:00:15.000Z');
assert.equal(snapshot.traffic.proxyObservedAt, observedAt);
assert.equal(snapshot.traffic.observedAt, observedAt);
trafficError = new Error('traffic unavailable');
snapshot = await service.refresh();
assert.equal(snapshot.devices[0].uploadBytes, '9007199254741007');
assert.equal(snapshot.source.traffic.lastObservedAt, observedAt);
assert.equal(snapshot.source.traffic.lastObservedAt, '2026-08-07T12:00:15.000Z');
assert.equal(snapshot.source.traffic.error, 'traffic unavailable');
assert.equal(snapshot.traffic.totalBytes, '9007199254744249');
});
test('device traffic history stays in bounded service memory and survives client snapshots', async (t) => {
@@ -313,9 +331,55 @@ test('device traffic history stays in bounded service memory and survives client
gatewayBytes: '30',
proxyBytes: '5',
}]);
assert.deepEqual(snapshot.traffic.history, [{
observedAt,
gatewayBytes: '30',
proxyBytes: '5',
}]);
assert.deepEqual(service.snapshot().devices[0].trafficHistory, snapshot.devices[0].trafficHistory);
assert.doesNotMatch(fs.readFileSync(filePath, 'utf8'), /trafficHistory/);
assert.deepEqual(createService().snapshot().devices[0].trafficHistory, []);
assert.deepEqual(createService().snapshot().traffic.history, []);
});
test('global traffic stays monotonic when a device expires and returns in the same epoch', async (t) => {
const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'harbor-global-traffic-retention-'));
t.after(() => fs.rmSync(directory, { recursive: true, force: true }));
const store = createJsonStore({
filePath: path.join(directory, 'devices.json'),
defaultValue: {},
migrate: migrateDeviceInventoryState,
});
const mac = '00:11:22:33:44:55';
let observedAt = '2026-07-01T12:00:00.000Z';
let present = true;
let uploadBytes = '100';
const service = createDeviceInventoryService({
store,
observe: () => ({
observedAt,
error: null,
observations: present
? [{ ip: '192.168.50.7', mac, interface: 'eth0', observedAt, active: true }]
: [],
}),
observeTraffic: () => ({
epoch: 'epoch-a', generation: 'rules-a', observedAt, source: { error: null },
devices: [{ mac, uploadBytes, downloadBytes: '0' }],
}),
});
assert.equal((await service.refresh()).traffic.totalBytes, '100');
present = false;
observedAt = '2026-08-07T12:00:00.000Z';
let snapshot = await service.refresh();
assert.equal(snapshot.devices.length, 0);
assert.equal(snapshot.traffic.totalBytes, '100');
present = true;
snapshot = await service.refresh();
assert.equal(snapshot.traffic.totalBytes, '100');
uploadBytes = '110';
assert.equal((await service.refresh()).traffic.totalBytes, '110');
});
test('legacy dataplane samples preserve saved proxy totals while Gateway totals keep advancing', async (t) => {
@@ -462,7 +526,7 @@ test('device policy is independent from pinning, persists, and keeps the last ap
assert.equal(snapshot.devices[0].desiredPolicy, 'direct');
assert.equal(snapshot.devices[0].appliedPolicy, 'direct');
assert.equal(snapshot.devices[0].policyStatus, 'applied');
assert.equal(store.read().schemaVersion, 2);
assert.equal(store.read().schemaVersion, DEVICE_INVENTORY_SCHEMA_VERSION);
assert.equal(store.read().policy.schemaVersion, 1);
service = createService();
@@ -585,7 +649,7 @@ test('device inventory backs up and normalizes a malformed additive policy check
});
const migrated = store.read();
assert.equal(migrated.schemaVersion, 2);
assert.equal(migrated.schemaVersion, DEVICE_INVENTORY_SCHEMA_VERSION);
assert.equal(migrated.policy.schemaVersion, 1);
assert.equal(migrated.policy.dataplaneEpoch, null);
assert.deepEqual(migrated.policy.byMac, {});
@@ -666,6 +730,12 @@ test('device inventory backs up malformed v2 traffic and re-baselines without do
new Set(migrated.traffic.rebaselineMacs),
new Set([firstMac, secondMac, missingBaselineMac, missingTotalMac, expiredMac]),
);
assert.equal(migrated.traffic.global.gateway.uploadBytes, '1300');
assert.equal(migrated.traffic.global.gateway.downloadBytes, '1500');
assert.deepEqual(
new Set(migrated.traffic.global.gateway.rebaselineMacs),
new Set([firstMac, secondMac, missingBaselineMac, missingTotalMac, expiredMac]),
);
let counters = [
{ mac: firstMac, uploadBytes: '200', downloadBytes: '300' },
@@ -703,6 +773,7 @@ test('device inventory backs up malformed v2 traffic and re-baselines without do
assert.equal(byMac.get(missingBaselineMac).uploadBytes, '800');
assert.equal(byMac.get(missingTotalMac).uploadBytes, '0');
assert.equal(byMac.has(expiredMac), false);
assert.equal(snapshot.traffic.totalBytes, '2800');
assert.deepEqual(store.read().traffic.rebaselineMacs, []);
assert.equal(snapshot.source.traffic.error, null);
counters = [
@@ -721,6 +792,7 @@ test('device inventory backs up malformed v2 traffic and re-baselines without do
assert.equal(byMac.get(missingBaselineMac).downloadBytes, '905');
assert.equal(byMac.get(missingTotalMac).uploadBytes, '20');
assert.equal(byMac.get(missingTotalMac).downloadBytes, '20');
assert.equal(snapshot.traffic.totalBytes, '2945');
});
test('malformed proxy totals are backed up and an expired recovery marker is cleared', async (t) => {