fix: injectable tunnel log label, honest truncate budget comment
This commit is contained in:
1 parent
6a2706130a
commit
703df24b13
4 files changed
+42
-6
No files matched your search
@@ -32,7 +32,10 @@ export const sliceUtf8 = (s: string, maxBytes: number): string => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Serialize `value` to JSON and, if it exceeds `maxBytes` (UTF-8), return a
|
// Serialize `value` to JSON and, if it exceeds `maxBytes` (UTF-8), return a
|
||||||
// truncated marker object whose own serialization stays within `maxBytes`.
|
// truncated marker object. A fixed 256-byte margin is reserved for the marker
|
||||||
|
// metadata, so the marker's own serialization stays within `maxBytes` for any
|
||||||
|
// budget at or above a few hundred bytes (esmole uses 100 KB). For
|
||||||
|
// pathologically tiny budgets the marker may exceed `maxBytes`.
|
||||||
export const truncateJsonBudget = (
|
export const truncateJsonBudget = (
|
||||||
value: unknown,
|
value: unknown,
|
||||||
maxBytes: number
|
maxBytes: number
|
||||||
|
|||||||
@@ -50,7 +50,8 @@ export const buildSshAuth = (
|
|||||||
export const openTunnel = async (
|
export const openTunnel = async (
|
||||||
ssh: SshConfig,
|
ssh: SshConfig,
|
||||||
dstHost: string,
|
dstHost: string,
|
||||||
dstPort: number
|
dstPort: number,
|
||||||
|
logLabel: string = 'ssh-tunnel'
|
||||||
): Promise<Tunnel> => {
|
): Promise<Tunnel> => {
|
||||||
const client = new Client()
|
const client = new Client()
|
||||||
|
|
||||||
@@ -67,7 +68,7 @@ export const openTunnel = async (
|
|||||||
|
|
||||||
// After 'ready' resolved, later ssh errors must not crash the process.
|
// After 'ready' resolved, later ssh errors must not crash the process.
|
||||||
client.on('error', (error) => {
|
client.on('error', (error) => {
|
||||||
console.error('ssh-tunnel: ssh tunnel error: ' + error.message)
|
console.error(logLabel + ': ssh tunnel error: ' + error.message)
|
||||||
})
|
})
|
||||||
|
|
||||||
let closed = false
|
let closed = false
|
||||||
@@ -140,7 +141,7 @@ export const openTunnel = async (
|
|||||||
// without a persistent one, a later listener error is swallowed and the
|
// without a persistent one, a later listener error is swallowed and the
|
||||||
// tunnel silently serves a dead socket. Mark it closed instead.
|
// tunnel silently serves a dead socket. Mark it closed instead.
|
||||||
server.on('error', (error) => {
|
server.on('error', (error) => {
|
||||||
console.error('ssh-tunnel: tunnel listener error: ' + error.message)
|
console.error(logLabel + ': tunnel listener error: ' + error.message)
|
||||||
markClosed()
|
markClosed()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -248,11 +248,34 @@ describe('openTunnel', () => {
|
|||||||
created.push(server)
|
created.push(server)
|
||||||
return server
|
return server
|
||||||
})
|
})
|
||||||
|
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
|
||||||
const tunnel = await openTunnel(sshConfig({ password: 'pw' }), 'db-host', 5432)
|
const tunnel = await openTunnel(sshConfig({ password: 'pw' }), 'db-host', 5432)
|
||||||
// A listener error after listen() resolved must not be swallowed: it
|
// A listener error after listen() resolved must not be swallowed: it
|
||||||
// marks the tunnel closed so the manager can rebuild it.
|
// marks the tunnel closed so the manager can rebuild it.
|
||||||
created[0].emit('error', new Error('EADDRNOTAVAIL'))
|
created[0].emit('error', new Error('EADDRNOTAVAIL'))
|
||||||
expect(tunnel.isClosed()).toBe(true)
|
expect(tunnel.isClosed()).toBe(true)
|
||||||
|
// Default log label brands the diagnostic with 'ssh-tunnel:'.
|
||||||
|
expect(errSpy).toHaveBeenCalledWith('ssh-tunnel: tunnel listener error: EADDRNOTAVAIL')
|
||||||
|
errSpy.mockRestore()
|
||||||
|
spy.mockRestore()
|
||||||
|
await tunnel.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('uses a custom logLabel prefix on diagnostics', async () => {
|
||||||
|
const created: net.Server[] = []
|
||||||
|
const realCreateServer = net.createServer.bind(net)
|
||||||
|
const spy = vi.spyOn(net, 'createServer').mockImplementation((handler) => {
|
||||||
|
const server = realCreateServer(handler as (socket: net.Socket) => void)
|
||||||
|
created.push(server)
|
||||||
|
return server
|
||||||
|
})
|
||||||
|
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
|
||||||
|
const tunnel = await openTunnel(sshConfig({ password: 'pw' }), 'db-host', 5432, 'dbmole')
|
||||||
|
created[0].emit('error', new Error('EADDRNOTAVAIL'))
|
||||||
|
expect(tunnel.isClosed()).toBe(true)
|
||||||
|
// The injected label replaces the default prefix token.
|
||||||
|
expect(errSpy).toHaveBeenCalledWith('dbmole: tunnel listener error: EADDRNOTAVAIL')
|
||||||
|
errSpy.mockRestore()
|
||||||
spy.mockRestore()
|
spy.mockRestore()
|
||||||
await tunnel.close()
|
await tunnel.close()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import { createManager as coreCreate, type ManagedConnection, type Manager } from '@dbmole/core'
|
import {
|
||||||
|
createManager as coreCreate,
|
||||||
|
type ManagedConnection,
|
||||||
|
type Manager,
|
||||||
|
openTunnel
|
||||||
|
} from '@dbmole/core'
|
||||||
import type { DbRegistry } from '../config/registry.js'
|
import type { DbRegistry } from '../config/registry.js'
|
||||||
import type { ConnectionConfig } from '../schema.js'
|
import type { ConnectionConfig } from '../schema.js'
|
||||||
import { createBackend, resolvePort } from '../sqlBackend.js'
|
import { createBackend, resolvePort } from '../sqlBackend.js'
|
||||||
@@ -8,4 +13,8 @@ export type DbManager = Manager<ConnectionConfig, Driver>
|
|||||||
export type DbManaged = ManagedConnection<ConnectionConfig, Driver>
|
export type DbManaged = ManagedConnection<ConnectionConfig, Driver>
|
||||||
|
|
||||||
export const createManager = (registry: DbRegistry): DbManager =>
|
export const createManager = (registry: DbRegistry): DbManager =>
|
||||||
coreCreate<ConnectionConfig, Driver>(registry, { createBackend, resolvePort })
|
coreCreate<ConnectionConfig, Driver>(registry, {
|
||||||
|
createBackend,
|
||||||
|
resolvePort,
|
||||||
|
createTunnel: (ssh, host, port) => openTunnel(ssh, host, port, 'dbmole')
|
||||||
|
})
|
||||||
Reference in new issue
Block a user