Handle connectivity diagnostics failures
This commit is contained in:
@@ -111,7 +111,7 @@ jobs:
|
||||
DATAPLANE_IMAGE="${IMAGE}-dataplane:${{ gitea.sha }}"
|
||||
UPDATE_DATAPLANE=false
|
||||
if git diff-tree --no-commit-id --name-only -r -m HEAD | grep -Eq \
|
||||
'^(Dockerfile|entrypoint\.sh|package(-lock)?\.json|scripts/build-runtime-base\.sh|\.gitea/workflows/gateway-build\.yml|src/server/(config|dataplane|gatewayRouting|singboxRuntime|version)\.js|src/server/(adapters/neighbors|services/(deviceTrafficService|devicePolicyService))\.js|src/shared/errors\.js)$'; then
|
||||
'^(Dockerfile|entrypoint\.sh|package(-lock)?\.json|scripts/build-runtime-base\.sh|\.gitea/workflows/gateway-build\.yml|src/server/(config|dataplane|gatewayRouting|singbox|singboxRuntime|version)\.js|src/server/(adapters/neighbors|services/(connectivityDiagnosticsService|deviceTrafficService|devicePolicyService))\.js|src/shared/errors\.js)$'; then
|
||||
UPDATE_DATAPLANE=true
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import http from 'node:http';
|
||||
import { HarborError } from '../shared/errors.js';
|
||||
|
||||
function request(socketPath, pathname, method = 'GET', body = null) {
|
||||
function request(socketPath, pathname, method = 'GET', body = null, timeoutMs = 6000) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const encoded = body == null ? null : JSON.stringify(body);
|
||||
const req = http.request({
|
||||
@@ -29,7 +29,7 @@ function request(socketPath, pathname, method = 'GET', body = null) {
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
req.setTimeout(6000, () => req.destroy(new Error('Dataplane не ответил за 6 секунд')));
|
||||
req.setTimeout(timeoutMs, () => req.destroy(new Error(`Dataplane не ответил за ${Math.ceil(timeoutMs / 1000)} секунд`)));
|
||||
req.end(encoded);
|
||||
});
|
||||
}
|
||||
@@ -56,7 +56,13 @@ export function createDataplaneClient(socketPath, send = request) {
|
||||
observeTraffic: () => send(socketPath, '/device-traffic', 'GET'),
|
||||
observeDevicePolicy: () => send(socketPath, '/device-policy', 'GET'),
|
||||
applyDevicePolicies: (devices) => send(socketPath, '/device-policy', 'PUT', { devices }),
|
||||
runConnectivityDiagnostics: () => send(socketPath, '/diagnostics/connectivity', 'POST'),
|
||||
runConnectivityDiagnostics: async () => {
|
||||
try {
|
||||
return await send(socketPath, '/diagnostics/connectivity', 'POST', null, 15_000);
|
||||
} catch (cause) {
|
||||
throw new HarborError('DIAGNOSTICS_FAILED', { cause });
|
||||
}
|
||||
},
|
||||
apply: () => update('/apply', 'POST'),
|
||||
restart: () => update('/restart', 'POST'),
|
||||
stop: () => update('/stop', 'POST'),
|
||||
|
||||
@@ -13,6 +13,7 @@ export const ERROR_DEFINITIONS = Object.freeze({
|
||||
DEVICE_NOT_FOUND: { status: 404, message: 'Устройство больше недоступно.', retryable: false },
|
||||
DEVICE_IDENTITY_AMBIGUOUS: { status: 409, message: 'Gateway не может безопасно применить маршрут к этому устройству.', retryable: true },
|
||||
DEVICE_POLICY_APPLY_FAILED: { status: 503, message: 'Не удалось применить маршрут устройства.', retryable: true },
|
||||
DIAGNOSTICS_FAILED: { status: 503, message: 'Не удалось проверить маршруты. Попробуйте ещё раз.', retryable: true },
|
||||
CONFIG_INVALID: { status: 422, message: 'Конфигурация VPN недействительна.', retryable: false },
|
||||
PROCESS_START_FAILED: { status: 503, message: 'Не удалось запустить VPN-процесс.', retryable: true },
|
||||
OPERATION_IN_PROGRESS: { status: 409, message: 'Другая операция ещё выполняется.', retryable: true },
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export const HARBOR_VERSIONS = Object.freeze({
|
||||
macClient: '0.15.0',
|
||||
gatewayClient: '0.16.0',
|
||||
gatewayBackend: '0.16.0',
|
||||
macClient: '0.15.1',
|
||||
gatewayClient: '0.16.1',
|
||||
gatewayBackend: '0.16.1',
|
||||
});
|
||||
|
||||
export function parseVersion(value) {
|
||||
|
||||
@@ -4,8 +4,8 @@ import { createDataplaneClient } from '../../src/server/dataplaneClient.js';
|
||||
|
||||
test('control uses the dataplane socket protocol', async () => {
|
||||
const requests = [];
|
||||
const send = async (socketPath, pathname, method, body) => {
|
||||
requests.push({ method, pathname, socketPath, body });
|
||||
const send = async (socketPath, pathname, method, body, timeoutMs) => {
|
||||
requests.push({ method, pathname, socketPath, body, timeoutMs });
|
||||
return {
|
||||
running: pathname !== '/stop',
|
||||
startedAt: 'now',
|
||||
@@ -42,4 +42,16 @@ test('control uses the dataplane socket protocol', async () => {
|
||||
'POST /stop /run/dataplane.sock',
|
||||
]);
|
||||
assert.deepEqual(requests[5].body, { devices: [{ id: 'dev_0011223344556677' }] });
|
||||
assert.equal(requests[6].timeoutMs, 15_000);
|
||||
});
|
||||
|
||||
test('connectivity diagnostics expose a retryable domain error', async () => {
|
||||
const client = createDataplaneClient('/run/dataplane.sock', async () => {
|
||||
throw new Error('Dataplane не ответил');
|
||||
});
|
||||
|
||||
await assert.rejects(client.runConnectivityDiagnostics(), {
|
||||
code: 'DIAGNOSTICS_FAILED',
|
||||
retryable: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,8 +16,8 @@ test('gateway deploy updates control without recreating dataplane', () => {
|
||||
assert.match(compose, /DATAPLANE_SOCKET: \/run\/vpn-proxy\/dataplane\.sock/);
|
||||
assert.match(deploy, /up -d --no-deps --wait[^\n]+vpn-proxy-control/);
|
||||
assert.match(workflow, /UPDATE_DATAPLANE="\$\{UPDATE_DATAPLANE\}"/);
|
||||
assert.match(workflow, /src\/server\/\(config\|dataplane\|gatewayRouting\|singboxRuntime\|version\)/);
|
||||
assert.match(workflow, /src\/server\/\(adapters\/neighbors\|services\/\(deviceTrafficService\|devicePolicyService\)\)/);
|
||||
assert.match(workflow, /src\/server\/\(config\|dataplane\|gatewayRouting\|singbox\|singboxRuntime\|version\)/);
|
||||
assert.match(workflow, /src\/server\/\(adapters\/neighbors\|services\/\(connectivityDiagnosticsService\|deviceTrafficService\|devicePolicyService\)\)/);
|
||||
assert.match(workflow, /src\/shared\/errors/);
|
||||
assert.doesNotMatch(workflow, /dataplaneClient/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user