From 24b94291960e2c97a5cf64ce2f86287d1440c0c1 Mon Sep 17 00:00:00 2001 From: bzp2010 Date: Thu, 7 Aug 2025 13:04:18 +0800 Subject: [PATCH 1/4] feat(core): add ingress server status endpoint --- .../cli/src/command/ingress-server.command.ts | 66 ++++++++++++------- apps/cli/src/server/index.ts | 44 ++++++++----- 2 files changed, 69 insertions(+), 41 deletions(-) diff --git a/apps/cli/src/command/ingress-server.command.ts b/apps/cli/src/command/ingress-server.command.ts index aa3ee95e..8bef430f 100644 --- a/apps/cli/src/command/ingress-server.command.ts +++ b/apps/cli/src/command/ingress-server.command.ts @@ -7,6 +7,7 @@ import { BaseCommand, BaseOptions, processCertificateFile } from './helper'; type IngressServerOptions = { listen?: URL; + listenStatus?: number; caCertFile?: string; tlsCertFile?: string; tlsKeyFile?: string; @@ -16,7 +17,7 @@ export const IngressServerCommand = new BaseCommand( 'server', ) .option( - '--listen ', + '--listen ', 'listen address of ADC server, the format is scheme://host:port', (val) => { try { @@ -27,6 +28,19 @@ export const IngressServerCommand = new BaseCommand( }, new URL('http://127.0.0.1:3000'), ) + .option( + '--listen-status ', + 'status listen port', + (val) => { + const port = parseInt(val, 10); + if (!port || isNaN(port) || port < 1 || port > 65535) + throw new commander.InvalidArgumentError( + 'The status listen port must be a number between 1 and 65535', + ); + return port; + }, + 3001, + ) .addOption( new Option( '--ca-cert-file ', @@ -60,28 +74,30 @@ export const IngressServerCommand = new BaseCommand( ), ), ) - .handle(async ({ listen, tlsCertFile, tlsKeyFile, caCertFile }) => { - if (listen.protocol === 'https:' && (!tlsCertFile || !tlsKeyFile)) { - console.error( - chalk.red( - 'Error: When using HTTPS, both --tls-cert-file and --tls-key-file must be provided', - ), + .handle( + async ({ listen, listenStatus, tlsCertFile, tlsKeyFile, caCertFile }) => { + if (listen.protocol === 'https:' && (!tlsCertFile || !tlsKeyFile)) { + console.error( + chalk.red( + 'Error: When using HTTPS, both --tls-cert-file and --tls-key-file must be provided', + ), + ); + return; + } + const server = new ADCServer({ + listen, + listenStatus, + tlsCert: tlsCertFile ? readFileSync(tlsCertFile, 'utf-8') : undefined, + tlsKey: tlsKeyFile ? readFileSync(tlsKeyFile, 'utf-8') : undefined, + tlsCACert: caCertFile ? readFileSync(caCertFile, 'utf-8') : undefined, + }); + await server.start(); + console.log( + `ADC server is running on: ${listen.protocol === 'unix:' ? listen.pathname : listen.origin}`, ); - return; - } - - const server = new ADCServer({ - listen, - tlsCert: tlsCertFile ? readFileSync(tlsCertFile, 'utf-8') : undefined, - tlsKey: tlsKeyFile ? readFileSync(tlsKeyFile, 'utf-8') : undefined, - tlsCACert: caCertFile ? readFileSync(caCertFile, 'utf-8') : undefined, - }); - await server.start(); - console.log( - `ADC server is running on: ${listen.protocol === 'unix:' ? listen.pathname : listen.origin}`, - ); - process.on('SIGINT', () => { - console.log('Stopping, see you next time!'); - server.stop(); - }); - }); + process.on('SIGINT', () => { + console.log('Stopping, see you next time!'); + server.stop(); + }); + }, + ); diff --git a/apps/cli/src/server/index.ts b/apps/cli/src/server/index.ts index 6603035e..863aa601 100644 --- a/apps/cli/src/server/index.ts +++ b/apps/cli/src/server/index.ts @@ -8,20 +8,27 @@ import { syncHandler } from './sync'; interface ADCServerOptions { listen: URL; + listenStatus: number; tlsCert?: string; tlsKey?: string; tlsCACert?: string; } export class ADCServer { private readonly express: Express; + private readonly expressStatus: Express; private server?: http.Server | https.Server; constructor(private readonly opts: ADCServerOptions) { this.express = express(); - this.express.disable('x-powered-by'); - this.express.disable('etag'); + this.expressStatus = express(); + [this.express, this.expressStatus].forEach( + (app) => (app.disable('x-powered-by'), app.disable('etag')), + ); this.express.use(express.json({ limit: '100mb' })); this.express.put('/sync', syncHandler); + this.expressStatus.get('/healthz/ready', (_, res) => + res.status(200).send('OK'), + ); } public async start() { @@ -48,20 +55,25 @@ export class ADCServer { this.server = http.createServer(this.express); break; } - return new Promise((resolve) => { - const listen = this.opts.listen; - if (listen.protocol === 'unix:') { - if (fs.existsSync(listen.pathname)) fs.unlinkSync(listen.pathname); - this.server.listen(listen.pathname, () => { - fs.chmodSync(listen.pathname, 0o660); - resolve(); - }); - } else { - this.server.listen(parseInt(listen.port), listen.hostname, () => - resolve(), - ); - } - }); + return Promise.all([ + new Promise((resolve) => { + const listen = this.opts.listen; + if (listen.protocol === 'unix:') { + if (fs.existsSync(listen.pathname)) fs.unlinkSync(listen.pathname); + this.server.listen(listen.pathname, () => { + fs.chmodSync(listen.pathname, 0o660); + resolve(); + }); + } else { + this.server.listen(parseInt(listen.port), listen.hostname, () => + resolve(), + ); + } + }), + new Promise((resolve) => { + this.expressStatus.listen(this.opts.listenStatus, () => resolve()); + }), + ]); } public async stop() { From a1d634463f4e7f923e44d8cd37e321c5c08036f3 Mon Sep 17 00:00:00 2001 From: bzp2010 Date: Thu, 7 Aug 2025 13:12:58 +0800 Subject: [PATCH 2/4] feat: add status server close --- .../cli/src/command/ingress-server.command.ts | 1 + apps/cli/src/server/index.ts | 30 +++++++++++++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/apps/cli/src/command/ingress-server.command.ts b/apps/cli/src/command/ingress-server.command.ts index 8bef430f..92faf46b 100644 --- a/apps/cli/src/command/ingress-server.command.ts +++ b/apps/cli/src/command/ingress-server.command.ts @@ -98,6 +98,7 @@ export const IngressServerCommand = new BaseCommand( process.on('SIGINT', () => { console.log('Stopping, see you next time!'); server.stop(); + process.exit(0); }); }, ); diff --git a/apps/cli/src/server/index.ts b/apps/cli/src/server/index.ts index 863aa601..bf3f46d1 100644 --- a/apps/cli/src/server/index.ts +++ b/apps/cli/src/server/index.ts @@ -17,6 +17,7 @@ export class ADCServer { private readonly express: Express; private readonly expressStatus: Express; private server?: http.Server | https.Server; + private serverStatus?: http.Server; constructor(private readonly opts: ADCServerOptions) { this.express = express(); @@ -65,8 +66,10 @@ export class ADCServer { resolve(); }); } else { - this.server.listen(parseInt(listen.port), listen.hostname, () => - resolve(), + this.serverStatus = this.server.listen( + parseInt(listen.port), + listen.hostname, + () => resolve(), ); } }), @@ -77,13 +80,22 @@ export class ADCServer { } public async stop() { - return new Promise((resolve) => { - if (this.server) { - this.server.close(() => resolve()); - } else { - resolve(); - } - }); + return Promise.all([ + new Promise((resolve) => { + if (this.server) { + this.server.close(() => resolve()); + } else { + resolve(); + } + }), + new Promise((resolve) => { + if (this.serverStatus) { + this.serverStatus.close(() => resolve()); + } else { + resolve(); + } + }), + ]); } public TEST_ONLY_getExpress() { From 1f2a03b4e8cacc341481e7b38c41a69d4e4300a0 Mon Sep 17 00:00:00 2001 From: bzp2010 Date: Thu, 7 Aug 2025 13:13:07 +0800 Subject: [PATCH 3/4] test: e2e --- apps/cli/e2e/server/basic.e2e-spec.ts | 39 ++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/apps/cli/e2e/server/basic.e2e-spec.ts b/apps/cli/e2e/server/basic.e2e-spec.ts index 46eb0557..d1dfe0d6 100644 --- a/apps/cli/e2e/server/basic.e2e-spec.ts +++ b/apps/cli/e2e/server/basic.e2e-spec.ts @@ -16,7 +16,10 @@ describe('Server - Basic', () => { beforeAll(async () => { mockedBackend = jestMockBackend(); - server = new ADCServer({ listen: new URL('http://127.0.1:3000') }); + server = new ADCServer({ + listen: new URL('http://127.0.1:3000'), + listenStatus: 3001, + }); }); it('test mocked load backend', async () => { @@ -85,7 +88,7 @@ describe('Server - Basic', () => { it('test server listen', async () => { const url = new URL(`http://127.0.0.1:48562`); - const server = new ADCServer({ listen: url }); + const server = new ADCServer({ listen: url, listenStatus: 3001 }); await server.start(); const { status, data } = await axios.put(`${url.origin}/sync`, { @@ -108,6 +111,7 @@ describe('Server - Basic', () => { const url = new URL(`https://127.0.0.1:48562`); const server = new ADCServer({ listen: url, + listenStatus: 3001, tlsCert: readFileSync( join(__dirname, '../assets/tls/server.cer'), 'utf-8', @@ -145,6 +149,7 @@ describe('Server - Basic', () => { readFileSync(join(__dirname, '../assets/tls/', fileName), 'utf-8'); const server = new ADCServer({ listen: url, + listenStatus: 3001, tlsCert: readCert('server.cer'), tlsKey: readCert('server.key'), tlsCACert: readCert('ca.cer'), @@ -198,7 +203,7 @@ describe('Server - Basic', () => { it('test server listen (with UDS)', async () => { const url = new URL(`unix:///tmp/adc-test.sock`); - const server = new ADCServer({ listen: url }); + const server = new ADCServer({ listen: url, listenStatus: 3001 }); await server.start(); const { status, data } = await new Promise<{ @@ -250,4 +255,32 @@ describe('Server - Basic', () => { await server.stop(); }); + + it('test status listen', async () => { + const server = new ADCServer({ + listen: new URL(`http://127.0.0.1:3000`), + listenStatus: 3001, + }); + await server.start(); + + const { status, data } = await axios.get( + `http://127.0.0.1:3001/healthz/ready`, + ); + expect(status).toEqual(200); + expect(data).toEqual('OK'); + }); + + it('test status listen (custom port)', async () => { + const server = new ADCServer({ + listen: new URL(`http://127.0.0.1:3000`), + listenStatus: 30001, + }); + await server.start(); + + const { status, data } = await axios.get( + `http://127.0.0.1:30001/healthz/ready`, + ); + expect(status).toEqual(200); + expect(data).toEqual('OK'); + }); }); From e9c9040828761ff955be85179f9a63642d622d23 Mon Sep 17 00:00:00 2001 From: bzp2010 Date: Thu, 7 Aug 2025 14:15:53 +0800 Subject: [PATCH 4/4] test: fix --- apps/cli/e2e/server/basic.e2e-spec.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/cli/e2e/server/basic.e2e-spec.ts b/apps/cli/e2e/server/basic.e2e-spec.ts index d1dfe0d6..e05ad713 100644 --- a/apps/cli/e2e/server/basic.e2e-spec.ts +++ b/apps/cli/e2e/server/basic.e2e-spec.ts @@ -268,6 +268,8 @@ describe('Server - Basic', () => { ); expect(status).toEqual(200); expect(data).toEqual('OK'); + + await server.stop(); }); it('test status listen (custom port)', async () => { @@ -282,5 +284,7 @@ describe('Server - Basic', () => { ); expect(status).toEqual(200); expect(data).toEqual('OK'); + + await server.stop(); }); });