From 091bd105f7063ee27cbfb617894673e4edb21f0a Mon Sep 17 00:00:00 2001 From: Redcinelli Date: Thu, 19 Jun 2025 18:15:40 +0200 Subject: [PATCH 01/14] Update: Add proxy support to the cli --- src/cli.ts | 2 ++ src/common_types.ts | 2 ++ src/locations/index.ts | 1 + src/push/kibana_api.ts | 7 ++++++- src/push/request.ts | 7 +++++-- src/push/utils.ts | 12 ++++++++++++ 6 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index 8115b31d5..f0baa5bbb 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -193,6 +193,8 @@ program '--space ', 'the target Kibana spaces for the pushed monitors — spaces help you organise pushed monitors.' ) + .option('--proxy_uri ', 'proxy uri to use when pushing to kibana') + .option('--proxy_token ', 'auth token to use the proxy') .option('-y, --yes', 'skip all questions and run non-interactively') .addOption(pattern) .addOption(tags) diff --git a/src/common_types.ts b/src/common_types.ts index 476ccb8ed..4551f94e9 100644 --- a/src/common_types.ts +++ b/src/common_types.ts @@ -263,6 +263,8 @@ export type PushOptions = Partial & retestOnFailure?: MonitorConfig['retestOnFailure']; enabled?: boolean; grepOpts?: GrepOptions; + proxy_uri?: string; + proxy_token?: string; }; export type ProjectSettings = { diff --git a/src/locations/index.ts b/src/locations/index.ts index d5acf09eb..28b6fef44 100644 --- a/src/locations/index.ts +++ b/src/locations/index.ts @@ -49,6 +49,7 @@ export async function getLocations(options: LocationCmdOptions) { url: generateURL(options, 'location'), method: 'GET', auth: options.auth, + proxyAgent: null, // for now we don't support proxy with this. }); return resp.locations; } diff --git a/src/push/kibana_api.ts b/src/push/kibana_api.ts index 123a38aa7..290f90b50 100644 --- a/src/push/kibana_api.ts +++ b/src/push/kibana_api.ts @@ -34,7 +34,7 @@ import { sendRequest, APIMonitorError, } from './request'; -import { generateURL } from './utils'; +import { build_proxy_settings, generateURL } from './utils'; // Default batch size for bulk put / delete export const BATCH_SIZE = parseInt(process.env.CHUNK_SIZE) || 250; @@ -57,6 +57,7 @@ export async function bulkPutMonitors( method: 'PUT', auth: options.auth, body: JSON.stringify({ monitors: schemas }), + proxyAgent: build_proxy_settings(options.proxy_uri, options.proxy_token), }); const { failedMonitors } = resp; @@ -103,6 +104,7 @@ const fetchMonitors = async (options: PushOptions, afterKey?: string) => { url, method: 'GET', auth: options.auth, + proxyAgent: build_proxy_settings(options.proxy_uri, options.proxy_token), }); return { afterKey: resp.after_key, @@ -124,6 +126,7 @@ export async function bulkDeleteMonitors( method: 'DELETE', auth: options.auth, body: JSON.stringify({ monitors: monitorIDs }), + proxyAgent: build_proxy_settings(options.proxy_uri, options.proxy_token), }); } @@ -138,6 +141,7 @@ export async function getVersion(options: PushOptions) { url: generateURL(options, 'status'), method: 'GET', auth: options.auth, + proxyAgent: build_proxy_settings(options?.proxy_uri, options?.proxy_token), }); return data.kibana.version; @@ -169,6 +173,7 @@ export async function createMonitorsLegacy({ method: 'PUT', auth: options.auth, body: JSON.stringify(schema), + proxyAgent: build_proxy_settings(options.proxy_uri, options.proxy_token), }); const resBody = await handleError(statusCode, url, body); diff --git a/src/push/request.ts b/src/push/request.ts index ef97399b2..c0fbd3142 100644 --- a/src/push/request.ts +++ b/src/push/request.ts @@ -32,6 +32,7 @@ import { indent, symbols } from '../helpers'; const { version } = require('../../package.json'); export type APIRequestOptions = { + proxyAgent: Dispatcher; url: string; method: Dispatcher.HttpMethod; auth: string; @@ -39,7 +40,7 @@ export type APIRequestOptions = { }; export async function sendRequest(options: APIRequestOptions) { - return await request(options.url, { + const request_options = { method: options.method, body: options.body, headers: { @@ -51,7 +52,9 @@ export async function sendRequest(options: APIRequestOptions) { }, // align with the default timeout of the kibana route headersTimeout: 2 * 60 * 1000, - }); + }; + if (options.proxyAgent) request_options['dispatcher'] = options.proxyAgent; + return await request(options.url, request_options); } export async function sendReqAndHandleError( diff --git a/src/push/utils.ts b/src/push/utils.ts index 9beb67e44..5b28ff48d 100644 --- a/src/push/utils.ts +++ b/src/push/utils.ts @@ -28,6 +28,7 @@ import { progress, removeTrailingSlash } from '../helpers'; import { green, red, grey, yellow, Colorize, bold } from 'kleur/colors'; import { PushOptions } from '../common_types'; import { Monitor } from '../dsl/monitor'; +import { ProxyAgent } from 'undici'; export function logDiff>( newIDs: T, @@ -199,3 +200,14 @@ export function normalizeMonitorName(p: string, replacement = '_') { p = p.replace(/[:]+/g, replacement); return p; } + +export function build_proxy_settings(proxy_uri: string, proxy_auth: string) { + let proxyAgent = null; + if (proxy_uri) { + proxyAgent = new ProxyAgent({ + uri: proxy_uri, + token: proxy_auth, + }); + } + return proxyAgent; +} From 1a3a08b75785b56784b02f03a95dc97909ce7d3a Mon Sep 17 00:00:00 2001 From: emilioalvap Date: Sat, 12 Jul 2025 21:34:10 +0200 Subject: [PATCH 02/14] Add initial proxy support for push command --- package-lock.json | 22 +++++----------------- package.json | 2 +- src/cli.ts | 23 +++++++++++++++++++++-- src/common_types.ts | 7 +++++-- src/locations/index.ts | 1 - src/options.ts | 42 ++++++++++++++++++++++++++++++++++++++++++ src/push/kibana_api.ts | 7 +------ src/push/request.ts | 2 -- src/push/utils.ts | 29 +++++++++++++++++++---------- 9 files changed, 94 insertions(+), 41 deletions(-) diff --git a/package-lock.json b/package-lock.json index 998462d18..75915a813 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ "sonic-boom": "^3.3.0", "source-map-support": "^0.5.21", "stack-utils": "^2.0.6", - "undici": "^5.29.0", + "undici": "^7.11.0", "unzip-stream": "^0.3.4", "yaml": "^2.2.2" }, @@ -1550,14 +1550,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@fastify/busboy": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz", - "integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==", - "engines": { - "node": ">=14" - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", @@ -15648,15 +15640,11 @@ } }, "node_modules/undici": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", - "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", - "license": "MIT", - "dependencies": { - "@fastify/busboy": "^2.0.0" - }, + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.11.0.tgz", + "integrity": "sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==", "engines": { - "node": ">=14.0" + "node": ">=20.18.1" } }, "node_modules/undici-types": { diff --git a/package.json b/package.json index 41fff58ec..994188a49 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "sonic-boom": "^3.3.0", "source-map-support": "^0.5.21", "stack-utils": "^2.0.6", - "undici": "^5.29.0", + "undici": "^7.11.0", "unzip-stream": "^0.3.4", "yaml": "^2.2.2" }, diff --git a/src/cli.ts b/src/cli.ts index f0baa5bbb..51767deb9 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -54,6 +54,7 @@ import { createLightweightMonitors } from './push/monitor'; import { getVersion } from './push/kibana_api'; import { installTransform } from './core/transform'; import { totp, TOTPCmdOptions } from './core/mfa'; +import { setGlobalProxy } from './push/utils'; /* eslint-disable-next-line @typescript-eslint/no-var-requires */ const { name, version } = require('../package.json'); @@ -68,6 +69,11 @@ const { tags, match, fields, + proxyToken, + proxyUri, + proxyNoVerify, + proxyCa, + proxyCert, } = getCommonCommandOpts(); program @@ -193,9 +199,12 @@ program '--space ', 'the target Kibana spaces for the pushed monitors — spaces help you organise pushed monitors.' ) - .option('--proxy_uri ', 'proxy uri to use when pushing to kibana') - .option('--proxy_token ', 'auth token to use the proxy') .option('-y, --yes', 'skip all questions and run non-interactively') + .addOption(proxyUri) + .addOption(proxyToken) + .addOption(proxyNoVerify) + .addOption(proxyCa) + .addOption(proxyCert) .addOption(pattern) .addOption(tags) .addOption(fields) @@ -218,6 +227,16 @@ program }, 'push' )) as PushOptions; + + //Set up global proxy agent if any of the related options are set + setGlobalProxy( + options.proxyUri, + options.proxyToken, + options.proxyNoVerify ? false : true, + options.proxyCa, + options.proxyCert + ); + await validatePush(options, settings); const monitors = runner._buildMonitors(options); if ((options as CliArgs).throttling == null) { diff --git a/src/common_types.ts b/src/common_types.ts index 4551f94e9..01af4ac4b 100644 --- a/src/common_types.ts +++ b/src/common_types.ts @@ -263,8 +263,11 @@ export type PushOptions = Partial & retestOnFailure?: MonitorConfig['retestOnFailure']; enabled?: boolean; grepOpts?: GrepOptions; - proxy_uri?: string; - proxy_token?: string; + proxyUri?: string; + proxyToken?: string; + proxyCa?: string; + proxyCert?: string; + proxyNoVerify?: string; }; export type ProjectSettings = { diff --git a/src/locations/index.ts b/src/locations/index.ts index 28b6fef44..d5acf09eb 100644 --- a/src/locations/index.ts +++ b/src/locations/index.ts @@ -49,7 +49,6 @@ export async function getLocations(options: LocationCmdOptions) { url: generateURL(options, 'location'), method: 'GET', auth: options.auth, - proxyAgent: null, // for now we don't support proxy with this. }); return resp.locations; } diff --git a/src/options.ts b/src/options.ts index 355b5491b..1f2b61442 100644 --- a/src/options.ts +++ b/src/options.ts @@ -28,6 +28,8 @@ import { createOption } from 'commander'; import { readConfig } from './config'; import type { CliArgs, RunOptions } from './common_types'; import { THROTTLING_WARNING_MSG, warn } from './helpers'; +import path from 'path'; +import { existsSync, readFileSync } from 'fs'; type Mode = 'run' | 'push'; @@ -255,6 +257,27 @@ export function getCommonCommandOpts() { }, {}); }); + const proxyUri = createOption( + '--proxy-uri ', + 'proxy uri to use when pushing to kibana' + ); + const proxyToken = createOption( + '--proxy-token ', + 'auth token to use the proxy' + ); + const proxyCa = createOption( + '--proxy-ca ', + 'provide a CA override for proxy endpoint, as a path to be loaded or as string ' + ).argParser(parseFileOption); + const proxyCert = createOption( + '--proxy-cert ', + 'provide a cert override for proxy endpoint, as a path to be loaded or as string ' + ).argParser(parseFileOption); + const proxyNoVerify = createOption( + '--proxy-no-verify', + 'disable TLS verification for the proxy connection' + ); + return { auth, authMandatory, @@ -265,6 +288,11 @@ export function getCommonCommandOpts() { tags, match, fields, + proxyUri, + proxyToken, + proxyCa, + proxyCert, + proxyNoVerify, }; } @@ -299,3 +327,17 @@ function parseAsBuffer(value: any): Buffer { return value; } } + +function parseFileOption(value: string) { + const filePath = path.resolve(value); + + if (!existsSync(filePath)) { + return value; + } + + try { + return readFileSync(filePath); + } catch (e) { + throw new Error(`could not be read provided path ${filePath}: ${e}`); + } +} diff --git a/src/push/kibana_api.ts b/src/push/kibana_api.ts index 290f90b50..123a38aa7 100644 --- a/src/push/kibana_api.ts +++ b/src/push/kibana_api.ts @@ -34,7 +34,7 @@ import { sendRequest, APIMonitorError, } from './request'; -import { build_proxy_settings, generateURL } from './utils'; +import { generateURL } from './utils'; // Default batch size for bulk put / delete export const BATCH_SIZE = parseInt(process.env.CHUNK_SIZE) || 250; @@ -57,7 +57,6 @@ export async function bulkPutMonitors( method: 'PUT', auth: options.auth, body: JSON.stringify({ monitors: schemas }), - proxyAgent: build_proxy_settings(options.proxy_uri, options.proxy_token), }); const { failedMonitors } = resp; @@ -104,7 +103,6 @@ const fetchMonitors = async (options: PushOptions, afterKey?: string) => { url, method: 'GET', auth: options.auth, - proxyAgent: build_proxy_settings(options.proxy_uri, options.proxy_token), }); return { afterKey: resp.after_key, @@ -126,7 +124,6 @@ export async function bulkDeleteMonitors( method: 'DELETE', auth: options.auth, body: JSON.stringify({ monitors: monitorIDs }), - proxyAgent: build_proxy_settings(options.proxy_uri, options.proxy_token), }); } @@ -141,7 +138,6 @@ export async function getVersion(options: PushOptions) { url: generateURL(options, 'status'), method: 'GET', auth: options.auth, - proxyAgent: build_proxy_settings(options?.proxy_uri, options?.proxy_token), }); return data.kibana.version; @@ -173,7 +169,6 @@ export async function createMonitorsLegacy({ method: 'PUT', auth: options.auth, body: JSON.stringify(schema), - proxyAgent: build_proxy_settings(options.proxy_uri, options.proxy_token), }); const resBody = await handleError(statusCode, url, body); diff --git a/src/push/request.ts b/src/push/request.ts index c0fbd3142..849153e93 100644 --- a/src/push/request.ts +++ b/src/push/request.ts @@ -32,7 +32,6 @@ import { indent, symbols } from '../helpers'; const { version } = require('../../package.json'); export type APIRequestOptions = { - proxyAgent: Dispatcher; url: string; method: Dispatcher.HttpMethod; auth: string; @@ -53,7 +52,6 @@ export async function sendRequest(options: APIRequestOptions) { // align with the default timeout of the kibana route headersTimeout: 2 * 60 * 1000, }; - if (options.proxyAgent) request_options['dispatcher'] = options.proxyAgent; return await request(options.url, request_options); } diff --git a/src/push/utils.ts b/src/push/utils.ts index 5b28ff48d..692e5216d 100644 --- a/src/push/utils.ts +++ b/src/push/utils.ts @@ -28,7 +28,8 @@ import { progress, removeTrailingSlash } from '../helpers'; import { green, red, grey, yellow, Colorize, bold } from 'kleur/colors'; import { PushOptions } from '../common_types'; import { Monitor } from '../dsl/monitor'; -import { ProxyAgent } from 'undici'; +import { ProxyAgent, setGlobalDispatcher } from 'undici'; +import { EnvHttpProxyAgent } from 'undici'; export function logDiff>( newIDs: T, @@ -201,13 +202,21 @@ export function normalizeMonitorName(p: string, replacement = '_') { return p; } -export function build_proxy_settings(proxy_uri: string, proxy_auth: string) { - let proxyAgent = null; - if (proxy_uri) { - proxyAgent = new ProxyAgent({ - uri: proxy_uri, - token: proxy_auth, - }); - } - return proxyAgent; +export function setGlobalProxy( + uri: string, + token: string, + rejectUnauthorized: boolean, + ca: string, + cert: string +) { + // Create proxy agent if options exist, if not attempt to load env proxy + const proxyAgent = uri + ? new ProxyAgent({ + uri, + token, + proxyTls: { ca: ca ?? null, cert: cert ?? null, rejectUnauthorized }, + }) + : new EnvHttpProxyAgent(); + + setGlobalDispatcher(proxyAgent); } From 9846500af26db72b4e4d8a5fa5b2c0cafac3caf2 Mon Sep 17 00:00:00 2001 From: emilioalvap Date: Thu, 31 Jul 2025 15:17:11 +0200 Subject: [PATCH 03/14] Add proxy options to location cmd --- __tests__/locations/index.test.ts | 52 +++++++++++++++++++++++++++++-- src/cli.ts | 13 ++++++++ src/locations/index.ts | 5 +++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/__tests__/locations/index.test.ts b/__tests__/locations/index.test.ts index 23b88cde9..d3776ef08 100644 --- a/__tests__/locations/index.test.ts +++ b/__tests__/locations/index.test.ts @@ -33,6 +33,8 @@ import { LOCATIONS } from '../fixtures/locationinfo'; import { Server } from '../utils/server'; import { CLIMock } from '../utils/test-config'; import { mkdir, rm, writeFile } from 'fs/promises'; +// import { createProxyServer } from 'http-proxy'; +// import http from "http"; describe('Locations', () => { const apiKey = 'foo'; @@ -97,10 +99,11 @@ describe('Locations', () => { }); const runLocations = async (args: Array = []) => { - const cli = new CLIMock() + const cli = new CLIMock(true) .args(['locations', ...args]) .run({ cwd: PROJECT_DIR, env: process.env }); - expect(await cli.exitCode).toBe(0); + // expect(await cli.exitCode).toBe(0); + console.log(cli.stderr()); return cli.stderr(); }; @@ -125,5 +128,50 @@ describe('Locations', () => { expect(output).toContain(`custom location 1`); expect(output).toContain(`custom location 2`); }); + + describe('Proxy options', () => { + // let requests: Array = []; + // let proxyServer; + + beforeAll(async () => { + await fakeProjectSetup({ url: server.PREFIX }); + // proxyServer = createProxyServer({ target: server.PREFIX }).listen(8019); + // proxyServer.on('proxyReq', function (proxyReq, req) { + // requests.push(req); + // }); + // const proxy = createProxyServer({}); + + // + // Create your custom server and just call `proxy.web()` to proxy + // a web request to the target passed in the options + // also you can use `proxy.ws()` to proxy a websockets request + // + // proxyServer = http.createServer(function (req, res) { + // proxy.web(req, res, { target: server.PREFIX }); + // }).listen(8019); + }); + + // afterAll(async () => { + // proxyServer.close(); + // }); + + // beforeEach(() => { + // requests = [] + // }); + + it('enables proxy based on HTTP_PROXY', async () => { + const output = await runLocations([ + '--url', + server.PREFIX, + '--auth', + apiKey, + '--proxy-uri', + 'http://localhost:9191', + ]); + console.log(output); + // expect(requests).toHaveLength(1); + expect(output).toContain(`custom location 1`); + }); + }); }); }); diff --git a/src/cli.ts b/src/cli.ts index 51767deb9..2b021ab72 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -275,10 +275,23 @@ program ) .option('--url ', 'Kibana URL to fetch all public and private locations') .addOption(auth) + .addOption(proxyUri) + .addOption(proxyToken) + .addOption(proxyNoVerify) + .addOption(proxyCa) + .addOption(proxyCert) .action(async (cmdOpts: LocationCmdOptions) => { const revert = installTransform(); const url = cmdOpts.url ?? (await loadSettings(null, true))?.url; try { + //Set up global proxy agent if any of the related options are set + setGlobalProxy( + cmdOpts.proxyUri, + cmdOpts.proxyToken, + cmdOpts.proxyNoVerify ? false : true, + cmdOpts.proxyCa, + cmdOpts.proxyCert + ); if (url && cmdOpts.auth) { const allLocations = await getLocations({ url, diff --git a/src/locations/index.ts b/src/locations/index.ts index d5acf09eb..c0cc068e1 100644 --- a/src/locations/index.ts +++ b/src/locations/index.ts @@ -30,6 +30,11 @@ import { generateURL } from '../push/utils'; export type LocationCmdOptions = { auth: string; url: string; + proxyUri?: string; + proxyToken?: string; + proxyCa?: string; + proxyCert?: string; + proxyNoVerify?: string; }; type LocationMetadata = { From 24ba9fabab07a0549c4c8ead7868c1777ec34757 Mon Sep 17 00:00:00 2001 From: emilioalvap Date: Mon, 4 Aug 2025 16:11:19 +0200 Subject: [PATCH 04/14] Add unit test for proxy settings --- __tests__/locations/index.test.ts | 88 ++++++++++------ __tests__/push/index.test.ts | 138 ++++++++++++++++++++++++++ __tests__/utils/kibana-test-server.ts | 56 +++++++++-- package-lock.json | 18 ++++ package.json | 1 + src/cli.ts | 4 +- src/helpers.ts | 21 ++++ src/push/utils.ts | 21 ---- 8 files changed, 284 insertions(+), 63 deletions(-) diff --git a/__tests__/locations/index.test.ts b/__tests__/locations/index.test.ts index d3776ef08..289087996 100644 --- a/__tests__/locations/index.test.ts +++ b/__tests__/locations/index.test.ts @@ -33,8 +33,7 @@ import { LOCATIONS } from '../fixtures/locationinfo'; import { Server } from '../utils/server'; import { CLIMock } from '../utils/test-config'; import { mkdir, rm, writeFile } from 'fs/promises'; -// import { createProxyServer } from 'http-proxy'; -// import http from "http"; +import { Straightforward } from 'straightforward'; describe('Locations', () => { const apiKey = 'foo'; @@ -102,8 +101,7 @@ describe('Locations', () => { const cli = new CLIMock(true) .args(['locations', ...args]) .run({ cwd: PROJECT_DIR, env: process.env }); - // expect(await cli.exitCode).toBe(0); - console.log(cli.stderr()); + expect(await cli.exitCode).toBe(0); return cli.stderr(); }; @@ -130,47 +128,73 @@ describe('Locations', () => { }); describe('Proxy options', () => { - // let requests: Array = []; - // let proxyServer; + let requests: Array = []; + let proxyServer; + let tlsServer; beforeAll(async () => { - await fakeProjectSetup({ url: server.PREFIX }); - // proxyServer = createProxyServer({ target: server.PREFIX }).listen(8019); - // proxyServer.on('proxyReq', function (proxyReq, req) { - // requests.push(req); - // }); - // const proxy = createProxyServer({}); - - // - // Create your custom server and just call `proxy.web()` to proxy - // a web request to the target passed in the options - // also you can use `proxy.ws()` to proxy a websockets request - // - // proxyServer = http.createServer(function (req, res) { - // proxy.web(req, res, { target: server.PREFIX }); - // }).listen(8019); + proxyServer = new Straightforward(); + proxyServer.onConnect.use(async ({ req }, next) => { + requests.push(req); + return next(); + }); + await proxyServer.listen(8019); + tlsServer = await Server.create({ tls: true }); + tlsServer.route('/internal/uptime/service/locations', (req, res) => { + res.setHeader('content-type', 'application/json'); + res.end(JSON.stringify({ locations: LOCATIONS })); + }); }); - // afterAll(async () => { - // proxyServer.close(); - // }); + afterAll(async () => { + proxyServer.close(); + }); - // beforeEach(() => { - // requests = [] - // }); + beforeEach(() => { + requests = []; + delete process.env.HTTP_PROXY, + process.env.HTTP_PROXYS, + process.env.NO_PROXY, + process.env.NODE_TLS_REJECT_UNAUTHORIZED; + }); it('enables proxy based on HTTP_PROXY', async () => { + process.env.HTTP_PROXY = 'http://localhost:8019'; + await fakeProjectSetup({ url: server.PREFIX }); + const output = await runLocations(['--auth', apiKey]); + expect(output).toContain(`custom location 1`); + expect(requests).toHaveLength(1); + }); + + it('honors NO_PROXY with env variables', async () => { + process.env.HTTP_PROXY = 'http://localhost:8019'; + process.env.NO_PROXY = '*'; + await fakeProjectSetup({ url: server.PREFIX }); + const output = await runLocations(['--auth', apiKey]); + expect(output).toContain(`custom location 1`); + expect(requests).toHaveLength(0); + }); + + it('enables proxy based on HTTPS_PROXY', async () => { + process.env.HTTPS_PROXY = 'http://localhost:8019'; + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + await fakeProjectSetup({ url: tlsServer.PREFIX }); + const output = await runLocations(['--auth', apiKey]); + delete process.env.NODE_TLS_REJECT_UNAUTHORIZED; + expect(output).toContain(`custom location 1`); + expect(requests).toHaveLength(1); + }); + + it('enables proxy based on --proxy-uri', async () => { + await fakeProjectSetup({ url: server.PREFIX }); const output = await runLocations([ - '--url', - server.PREFIX, '--auth', apiKey, '--proxy-uri', - 'http://localhost:9191', + 'http://localhost:8019', ]); - console.log(output); - // expect(requests).toHaveLength(1); expect(output).toContain(`custom location 1`); + expect(requests).toHaveLength(1); }); }); }); diff --git a/__tests__/push/index.test.ts b/__tests__/push/index.test.ts index 3c8e6ecf9..061946fd8 100644 --- a/__tests__/push/index.test.ts +++ b/__tests__/push/index.test.ts @@ -32,6 +32,7 @@ import { createKibanaTestServer } from '../utils/kibana-test-server'; import { Server } from '../utils/server'; import { CLIMock } from '../utils/test-config'; import { THROTTLING_WARNING_MSG } from '../../src/helpers'; +import Straightforward from 'straightforward'; describe('Push', () => { let server: Server; @@ -353,4 +354,141 @@ heartbeat.monitors: expect(output).toMatchSnapshot(); }); }); + describe('Proxy options', () => { + let requests: Array = []; + let proxyServer; + let tlsServer; + + beforeAll(async () => { + proxyServer = new Straightforward(); + proxyServer.onConnect.use(async ({ req }, next) => { + requests.push(req); + return next(); + }); + await proxyServer.listen(8019); + tlsServer = await createKibanaTestServer('8.6.0', true, (req: any) => + requests.push(req) + ); + }); + + afterAll(async () => { + proxyServer.close(); + }); + + beforeEach(() => { + requests = []; + delete process.env.HTTP_PROXY, + process.env.HTTP_PROXYS, + process.env.NO_PROXY, + process.env.NODE_TLS_REJECT_UNAUTHORIZED; + }); + + it('enables proxy based on HTTP_PROXY', async () => { + process.env.HTTP_PROXY = 'http://localhost:8019'; + await fakeProjectSetup( + { project: { id: 'test-project', space: 'dummy', url: server.PREFIX } }, + { locations: ['test-loc'], schedule: 3 } + ); + const testJourney = join(PROJECT_DIR, 'chunk.journey.ts'); + await writeFile( + testJourney, + `import {journey, monitor} from '../../../'; + journey('a', () => monitor.use({ tags: ['chunk'] })); + journey('b', () => monitor.use({ tags: ['chunk'] }));` + ); + const output = await runPush([...DEFAULT_ARGS, '--tags', 'chunk'], { + CHUNK_SIZE: '1', + }); + await rm(testJourney, { force: true }); + expect(output).toContain('Added(2)'); + expect(output).toContain('creating or updating 1 monitors'); + expect(output).toContain('✓ Pushed:'); + expect(requests.length).toBeGreaterThan(0); + }); + + it('honors NO_PROXY with env variables', async () => { + process.env.HTTP_PROXY = 'http://localhost:8019'; + process.env.NO_PROXY = '*'; + await fakeProjectSetup( + { project: { id: 'test-project', space: 'dummy', url: server.PREFIX } }, + { locations: ['test-loc'], schedule: 3 } + ); + const testJourney = join(PROJECT_DIR, 'chunk.journey.ts'); + await writeFile( + testJourney, + `import {journey, monitor} from '../../../'; + journey('a', () => monitor.use({ tags: ['chunk'] })); + journey('b', () => monitor.use({ tags: ['chunk'] }));` + ); + const output = await runPush([...DEFAULT_ARGS, '--tags', 'chunk'], { + CHUNK_SIZE: '1', + }); + await rm(testJourney, { force: true }); + expect(output).toContain('Added(2)'); + expect(output).toContain('creating or updating 1 monitors'); + expect(output).toContain('✓ Pushed:'); + expect(requests).toHaveLength(0); + }); + + it('enables proxy based on HTTPS_PROXY', async () => { + process.env.HTTPS_PROXY = 'http://localhost:8019'; + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + await fakeProjectSetup( + { + project: { + id: 'test-project', + space: 'dummy', + url: tlsServer.PREFIX, + }, + }, + { locations: ['test-loc'], schedule: 3 } + ); + const testJourney = join(PROJECT_DIR, 'chunk.journey.ts'); + await writeFile( + testJourney, + `import {journey, monitor} from '../../../'; + journey('a', () => monitor.use({ tags: ['chunk'] })); + journey('b', () => monitor.use({ tags: ['chunk'] }));` + ); + const output = await runPush([...DEFAULT_ARGS, '--tags', 'chunk'], { + CHUNK_SIZE: '1', + }); + await rm(testJourney, { force: true }); + expect(output).toContain('Added(2)'); + expect(output).toContain('creating or updating 1 monitors'); + expect(output).toContain('✓ Pushed:'); + expect(requests.length).toBeGreaterThan(0); + }); + + it('enables proxy based on --proxy-uri', async () => { + await fakeProjectSetup( + { project: { id: 'test-project', space: 'dummy', url: server.PREFIX } }, + { locations: ['test-loc'], schedule: 3 } + ); + const testJourney = join(PROJECT_DIR, 'chunk.journey.ts'); + await writeFile( + testJourney, + `import {journey, monitor} from '../../../'; + journey('a', () => monitor.use({ tags: ['chunk'] })); + journey('b', () => monitor.use({ tags: ['chunk'] }));` + ); + const output = await runPush( + [ + ...DEFAULT_ARGS, + '--tags', + 'chunk', + '--proxy-uri', + 'http://localhost:8019', + ], + { + CHUNK_SIZE: '1', + } + ); + await rm(testJourney, { force: true }); + expect(output).toContain('Added(2)'); + expect(output).toContain('creating or updating 1 monitors'); + expect(output).toContain('✓ Pushed:'); + expect(requests.length).toBeGreaterThan(0); + }); + }); }); diff --git a/__tests__/utils/kibana-test-server.ts b/__tests__/utils/kibana-test-server.ts index 0945e27ff..5e47f770b 100644 --- a/__tests__/utils/kibana-test-server.ts +++ b/__tests__/utils/kibana-test-server.ts @@ -29,18 +29,40 @@ import { PutResponse, } from '../../src/push/kibana_api'; -export const createKibanaTestServer = async (kibanaVersion: string) => { - const server = await Server.create({ port: 0 }); - server.route('/s/dummy/api/status', (req, res) => - res.end(JSON.stringify({ version: { number: kibanaVersion } })) - ); - server.route('/s/dummy/api/stats', (req, res) => - res.end(JSON.stringify({ kibana: { version: kibanaVersion } })) - ); +export const createKibanaTestServer = async ( + kibanaVersion: string, + tls?: boolean, + reqCallback?: any +) => { + const server = await Server.create({ port: 0, tls }); + server.route('/s/dummy/api/status', (req, res) => { + ( + reqCallback ?? + (() => { + return; + }) + )(req); + res.end(JSON.stringify({ version: { number: kibanaVersion } })); + }); + server.route('/s/dummy/api/stats', (req, res) => { + ( + reqCallback ?? + (() => { + return; + }) + )(req); + res.end(JSON.stringify({ kibana: { version: kibanaVersion } })); + }); // Legacy server.route( '/s/dummy/api/synthetics/service/project/monitors', async (req, res) => { + ( + reqCallback ?? + (() => { + return; + }) + )(req); await new Promise(r => setTimeout(r, 20)); req.on('data', chunks => { const schema = JSON.parse(chunks.toString()) as LegacyAPISchema; @@ -66,6 +88,12 @@ export const createKibanaTestServer = async (kibanaVersion: string) => { // Post 8.6 const basePath = '/s/dummy/api/synthetics/project/test-project/monitors'; server.route(basePath, (req, res) => { + ( + reqCallback ?? + (() => { + return; + }) + )(req); const getResp = { total: 2, monitors: [ @@ -76,6 +104,12 @@ export const createKibanaTestServer = async (kibanaVersion: string) => { res.end(JSON.stringify(getResp)); }); server.route(basePath + '/_bulk_update', (req, res) => { + ( + reqCallback ?? + (() => { + return; + }) + )(req); const updateResponse = { createdMonitors: ['j1', 'j2'], updatedMonitors: [], @@ -84,6 +118,12 @@ export const createKibanaTestServer = async (kibanaVersion: string) => { res.end(JSON.stringify(updateResponse)); }); server.route(basePath + '/_bulk_delete', (req, res) => { + ( + reqCallback ?? + (() => { + return; + }) + )(req); res.end(JSON.stringify({ deleted_monitors: ['j3', 'j4'] })); }); return server; diff --git a/package-lock.json b/package-lock.json index 75915a813..dbe446a72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -58,6 +58,7 @@ "prettier": "^2.7.1", "rimraf": "^3.0.2", "semantic-release": "^22.0.3", + "straightforward": "^4.2.2", "ts-jest": "^29.1.1", "typescript": "^5.1.6" }, @@ -15067,6 +15068,23 @@ "node": ">=10" } }, + "node_modules/straightforward": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/straightforward/-/straightforward-4.2.2.tgz", + "integrity": "sha512-MxfuNnyTP4RPjadI3DkYIcNIp0DMXeDmAXY4/6QivU8lLIPGUqaS5VsEkaQ2QC+FICzc7QTb/lJPRIhGRKVuMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "yargs": "^17.6.2" + }, + "bin": { + "straightforward": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/stream-combiner2": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", diff --git a/package.json b/package.json index 994188a49..0a9f15348 100644 --- a/package.json +++ b/package.json @@ -99,6 +99,7 @@ "prettier": "^2.7.1", "rimraf": "^3.0.2", "semantic-release": "^22.0.3", + "straightforward": "^4.2.2", "ts-jest": "^29.1.1", "typescript": "^5.1.6" }, diff --git a/src/cli.ts b/src/cli.ts index 2b021ab72..dc0c73f08 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -54,7 +54,7 @@ import { createLightweightMonitors } from './push/monitor'; import { getVersion } from './push/kibana_api'; import { installTransform } from './core/transform'; import { totp, TOTPCmdOptions } from './core/mfa'; -import { setGlobalProxy } from './push/utils'; +import { setGlobalProxy } from './helpers'; /* eslint-disable-next-line @typescript-eslint/no-var-requires */ const { name, version } = require('../package.json'); @@ -284,7 +284,7 @@ program const revert = installTransform(); const url = cmdOpts.url ?? (await loadSettings(null, true))?.url; try { - //Set up global proxy agent if any of the related options are set + // Set up global proxy agent if any of the related options are set setGlobalProxy( cmdOpts.proxyUri, cmdOpts.proxyToken, diff --git a/src/helpers.ts b/src/helpers.ts index 8a952bd73..94d51e397 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -38,6 +38,8 @@ import { ThrottlingOptions, } from './common_types'; import micromatch from 'micromatch'; +import { ProxyAgent, setGlobalDispatcher } from 'undici'; +import { EnvHttpProxyAgent } from 'undici'; const SEPARATOR = '\n'; @@ -404,3 +406,22 @@ export function tagsMatch(tags, pattern) { const matchess = micromatch(tags || ['*'], pattern); return matchess.length > 0; } + +export function setGlobalProxy( + uri: string, + token: string, + rejectUnauthorized: boolean, + ca: string, + cert: string +) { + // Create proxy agent if options exist, if not attempt to load env proxy + const proxyAgent = uri + ? new ProxyAgent({ + uri, + token, + proxyTls: { ca: ca ?? null, cert: cert ?? null, rejectUnauthorized }, + }) + : new EnvHttpProxyAgent(); + + setGlobalDispatcher(proxyAgent); +} diff --git a/src/push/utils.ts b/src/push/utils.ts index 692e5216d..9beb67e44 100644 --- a/src/push/utils.ts +++ b/src/push/utils.ts @@ -28,8 +28,6 @@ import { progress, removeTrailingSlash } from '../helpers'; import { green, red, grey, yellow, Colorize, bold } from 'kleur/colors'; import { PushOptions } from '../common_types'; import { Monitor } from '../dsl/monitor'; -import { ProxyAgent, setGlobalDispatcher } from 'undici'; -import { EnvHttpProxyAgent } from 'undici'; export function logDiff>( newIDs: T, @@ -201,22 +199,3 @@ export function normalizeMonitorName(p: string, replacement = '_') { p = p.replace(/[:]+/g, replacement); return p; } - -export function setGlobalProxy( - uri: string, - token: string, - rejectUnauthorized: boolean, - ca: string, - cert: string -) { - // Create proxy agent if options exist, if not attempt to load env proxy - const proxyAgent = uri - ? new ProxyAgent({ - uri, - token, - proxyTls: { ca: ca ?? null, cert: cert ?? null, rejectUnauthorized }, - }) - : new EnvHttpProxyAgent(); - - setGlobalDispatcher(proxyAgent); -} From 292fbf51277ac5968c20f912dae91937d82c0928 Mon Sep 17 00:00:00 2001 From: emilioalvap Date: Thu, 25 Sep 2025 18:53:30 +0200 Subject: [PATCH 05/14] Add proxy options to synthetics config, additional unit tests --- __tests__/locations/index.test.ts | 67 ++++++++++------- __tests__/options.test.ts | 30 +++++++- __tests__/push/index.test.ts | 55 ++++++++++---- __tests__/utils/kibana-test-server.ts | 43 ++--------- package-lock.json | 2 +- src/cli.ts | 103 ++++++++++++++++++-------- src/common_types.ts | 15 ++-- src/helpers.ts | 21 +++--- src/locations/index.ts | 5 -- src/options.ts | 46 ++++-------- 10 files changed, 228 insertions(+), 159 deletions(-) diff --git a/__tests__/locations/index.test.ts b/__tests__/locations/index.test.ts index 289087996..678b04ef1 100644 --- a/__tests__/locations/index.test.ts +++ b/__tests__/locations/index.test.ts @@ -34,6 +34,7 @@ import { Server } from '../utils/server'; import { CLIMock } from '../utils/test-config'; import { mkdir, rm, writeFile } from 'fs/promises'; import { Straightforward } from 'straightforward'; +import { AddressInfo } from 'net'; describe('Locations', () => { const apiKey = 'foo'; @@ -83,10 +84,10 @@ describe('Locations', () => { describe('CLI command', () => { const PROJECT_DIR = join(__dirname, 'new-project'); - async function fakeProjectSetup(settings) { + async function fakeProjectSetup(settings: any) { await writeFile( join(PROJECT_DIR, 'synthetics.config.ts'), - `export default { project: ${JSON.stringify(settings)} }` + `export default ${JSON.stringify(settings)}` ); } @@ -97,10 +98,13 @@ describe('Locations', () => { await rm(PROJECT_DIR, { recursive: true, force: true }); }); - const runLocations = async (args: Array = []) => { - const cli = new CLIMock(true) + const runLocations = async ( + args: Array = [], + cliEnv: NodeJS.ProcessEnv = {} + ) => { + const cli = new CLIMock() .args(['locations', ...args]) - .run({ cwd: PROJECT_DIR, env: process.env }); + .run({ cwd: PROJECT_DIR, env: { ...process.env, ...cliEnv } }); expect(await cli.exitCode).toBe(0); return cli.stderr(); }; @@ -121,7 +125,7 @@ describe('Locations', () => { }); it('use project url settings for private locations', async () => { - await fakeProjectSetup({ url: server.PREFIX }); + await fakeProjectSetup({ project: { url: server.PREFIX } }); const output = await runLocations(['--auth', apiKey]); expect(output).toContain(`custom location 1`); expect(output).toContain(`custom location 2`); @@ -129,8 +133,9 @@ describe('Locations', () => { describe('Proxy options', () => { let requests: Array = []; - let proxyServer; + let proxyServer: Straightforward; let tlsServer; + let proxyPort: number; beforeAll(async () => { proxyServer = new Straightforward(); @@ -138,64 +143,74 @@ describe('Locations', () => { requests.push(req); return next(); }); - await proxyServer.listen(8019); + await proxyServer.listen(); tlsServer = await Server.create({ tls: true }); tlsServer.route('/internal/uptime/service/locations', (req, res) => { res.setHeader('content-type', 'application/json'); res.end(JSON.stringify({ locations: LOCATIONS })); }); + ({ port: proxyPort } = proxyServer.server.address() as AddressInfo); }); afterAll(async () => { proxyServer.close(); + tlsServer.close(); }); beforeEach(() => { requests = []; - delete process.env.HTTP_PROXY, - process.env.HTTP_PROXYS, - process.env.NO_PROXY, - process.env.NODE_TLS_REJECT_UNAUTHORIZED; }); it('enables proxy based on HTTP_PROXY', async () => { - process.env.HTTP_PROXY = 'http://localhost:8019'; - await fakeProjectSetup({ url: server.PREFIX }); - const output = await runLocations(['--auth', apiKey]); + await fakeProjectSetup({ project: { url: server.PREFIX } }); + const output = await runLocations(['--auth', apiKey], { + HTTP_PROXY: `http://localhost:${proxyPort}`, + }); expect(output).toContain(`custom location 1`); expect(requests).toHaveLength(1); }); it('honors NO_PROXY with env variables', async () => { - process.env.HTTP_PROXY = 'http://localhost:8019'; - process.env.NO_PROXY = '*'; - await fakeProjectSetup({ url: server.PREFIX }); - const output = await runLocations(['--auth', apiKey]); + await fakeProjectSetup({ project: { url: server.PREFIX } }); + const output = await runLocations(['--auth', apiKey], { + NO_PROXY: '*', + HTTP_PROXY: `http://localhost:${proxyPort}`, + }); expect(output).toContain(`custom location 1`); expect(requests).toHaveLength(0); }); it('enables proxy based on HTTPS_PROXY', async () => { - process.env.HTTPS_PROXY = 'http://localhost:8019'; - process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; - await fakeProjectSetup({ url: tlsServer.PREFIX }); - const output = await runLocations(['--auth', apiKey]); - delete process.env.NODE_TLS_REJECT_UNAUTHORIZED; + await fakeProjectSetup({ project: { url: tlsServer.PREFIX } }); + const output = await runLocations(['--auth', apiKey], { + HTTPS_PROXY: `http://localhost:${proxyPort}`, + NODE_TLS_REJECT_UNAUTHORIZED: '0', + }); expect(output).toContain(`custom location 1`); expect(requests).toHaveLength(1); }); it('enables proxy based on --proxy-uri', async () => { - await fakeProjectSetup({ url: server.PREFIX }); + await fakeProjectSetup({ project: { url: server.PREFIX } }); const output = await runLocations([ '--auth', apiKey, '--proxy-uri', - 'http://localhost:8019', + `http://localhost:${proxyPort}`, ]); expect(output).toContain(`custom location 1`); expect(requests).toHaveLength(1); }); + + it('enables proxy based on proxy settings', async () => { + await fakeProjectSetup({ + project: { url: server.PREFIX }, + proxy: { uri: `http://localhost:${proxyPort}` }, + }); + const output = await runLocations(['--auth', apiKey]); + expect(output).toContain(`custom location 1`); + expect(requests).toHaveLength(1); + }); }); }); }); diff --git a/__tests__/options.test.ts b/__tests__/options.test.ts index 7baa5a0d8..9dddf49fe 100644 --- a/__tests__/options.test.ts +++ b/__tests__/options.test.ts @@ -24,7 +24,12 @@ */ import { CliArgs } from '../src/common_types'; -import { normalizeOptions, parsePlaywrightOptions } from '../src/options'; +import { + collectOpts, + normalizeOptions, + parseFileOption, + parsePlaywrightOptions, +} from '../src/options'; import { join } from 'path'; describe('options', () => { @@ -187,4 +192,27 @@ describe('options', () => { expect(Buffer.isBuffer(t.passphrase)).toBeFalsy(); }); }); + + describe('parseFileOption', () => { + it('parses file', () => { + expect( + parseFileOption(join(__dirname, 'fixtures', 'synthetics.config.ts')) + ).toBeInstanceOf(Buffer); + }); + it('parses string', () => { + expect(parseFileOption('test')).toEqual('test'); + }); + }); + + describe('collectOpts', () => { + it('collects options in the accumulator', () => { + const opts = { a: 'a', b: 'b', c: true }; + const result = {}; + Object.entries(opts).forEach(([key, value]) => { + collectOpts(key, result)(value); + }); + + expect(result).toEqual(opts); + }); + }); }); diff --git a/__tests__/push/index.test.ts b/__tests__/push/index.test.ts index 061946fd8..bcc8b837f 100644 --- a/__tests__/push/index.test.ts +++ b/__tests__/push/index.test.ts @@ -33,6 +33,7 @@ import { Server } from '../utils/server'; import { CLIMock } from '../utils/test-config'; import { THROTTLING_WARNING_MSG } from '../../src/helpers'; import Straightforward from 'straightforward'; +import { AddressInfo } from 'net'; describe('Push', () => { let server: Server; @@ -354,10 +355,12 @@ heartbeat.monitors: expect(output).toMatchSnapshot(); }); }); + describe('Proxy options', () => { let requests: Array = []; - let proxyServer; + let proxyServer: Straightforward; let tlsServer; + let proxyPort: number; beforeAll(async () => { proxyServer = new Straightforward(); @@ -365,26 +368,23 @@ heartbeat.monitors: requests.push(req); return next(); }); - await proxyServer.listen(8019); + await proxyServer.listen(); tlsServer = await createKibanaTestServer('8.6.0', true, (req: any) => requests.push(req) ); + ({ port: proxyPort } = proxyServer.server.address() as AddressInfo); }); afterAll(async () => { proxyServer.close(); + tlsServer.close(); }); beforeEach(() => { requests = []; - delete process.env.HTTP_PROXY, - process.env.HTTP_PROXYS, - process.env.NO_PROXY, - process.env.NODE_TLS_REJECT_UNAUTHORIZED; }); it('enables proxy based on HTTP_PROXY', async () => { - process.env.HTTP_PROXY = 'http://localhost:8019'; await fakeProjectSetup( { project: { id: 'test-project', space: 'dummy', url: server.PREFIX } }, { locations: ['test-loc'], schedule: 3 } @@ -398,6 +398,7 @@ heartbeat.monitors: ); const output = await runPush([...DEFAULT_ARGS, '--tags', 'chunk'], { CHUNK_SIZE: '1', + HTTP_PROXY: `http://localhost:${proxyPort}`, }); await rm(testJourney, { force: true }); expect(output).toContain('Added(2)'); @@ -407,8 +408,6 @@ heartbeat.monitors: }); it('honors NO_PROXY with env variables', async () => { - process.env.HTTP_PROXY = 'http://localhost:8019'; - process.env.NO_PROXY = '*'; await fakeProjectSetup( { project: { id: 'test-project', space: 'dummy', url: server.PREFIX } }, { locations: ['test-loc'], schedule: 3 } @@ -422,6 +421,8 @@ heartbeat.monitors: ); const output = await runPush([...DEFAULT_ARGS, '--tags', 'chunk'], { CHUNK_SIZE: '1', + HTTP_PROXY: `http://localhost:${proxyPort}`, + NO_PROXY: '*', }); await rm(testJourney, { force: true }); expect(output).toContain('Added(2)'); @@ -431,8 +432,6 @@ heartbeat.monitors: }); it('enables proxy based on HTTPS_PROXY', async () => { - process.env.HTTPS_PROXY = 'http://localhost:8019'; - process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; await fakeProjectSetup( { project: { @@ -452,6 +451,8 @@ heartbeat.monitors: ); const output = await runPush([...DEFAULT_ARGS, '--tags', 'chunk'], { CHUNK_SIZE: '1', + HTTPS_PROXY: `http://localhost:${proxyPort}`, + NODE_TLS_REJECT_UNAUTHORIZED: '0', }); await rm(testJourney, { force: true }); expect(output).toContain('Added(2)'); @@ -462,7 +463,10 @@ heartbeat.monitors: it('enables proxy based on --proxy-uri', async () => { await fakeProjectSetup( - { project: { id: 'test-project', space: 'dummy', url: server.PREFIX } }, + { + project: { id: 'test-project', space: 'dummy', url: server.PREFIX }, + proxy: { uri: `http://localhost:${proxyPort}` }, + }, { locations: ['test-loc'], schedule: 3 } ); const testJourney = join(PROJECT_DIR, 'chunk.journey.ts'); @@ -478,7 +482,7 @@ heartbeat.monitors: '--tags', 'chunk', '--proxy-uri', - 'http://localhost:8019', + `http://localhost:${proxyPort}`, ], { CHUNK_SIZE: '1', @@ -490,5 +494,30 @@ heartbeat.monitors: expect(output).toContain('✓ Pushed:'); expect(requests.length).toBeGreaterThan(0); }); + + it('enables proxy based on proxy settings', async () => { + await fakeProjectSetup( + { + project: { id: 'test-project', space: 'dummy', url: server.PREFIX }, + proxy: { uri: `http://localhost:${proxyPort}` }, + }, + { locations: ['test-loc'], schedule: 3 } + ); + const testJourney = join(PROJECT_DIR, 'chunk.journey.ts'); + await writeFile( + testJourney, + `import {journey, monitor} from '../../../'; + journey('a', () => monitor.use({ tags: ['chunk'] })); + journey('b', () => monitor.use({ tags: ['chunk'] }));` + ); + const output = await runPush([...DEFAULT_ARGS, '--tags', 'chunk'], { + CHUNK_SIZE: '1', + }); + await rm(testJourney, { force: true }); + expect(output).toContain('Added(2)'); + expect(output).toContain('creating or updating 1 monitors'); + expect(output).toContain('✓ Pushed:'); + expect(requests.length).toBeGreaterThan(0); + }); }); }); diff --git a/__tests__/utils/kibana-test-server.ts b/__tests__/utils/kibana-test-server.ts index 5e47f770b..0b5566886 100644 --- a/__tests__/utils/kibana-test-server.ts +++ b/__tests__/utils/kibana-test-server.ts @@ -28,6 +28,7 @@ import { LegacyAPISchema, PutResponse, } from '../../src/push/kibana_api'; +import { noop } from '../../src/helpers'; export const createKibanaTestServer = async ( kibanaVersion: string, @@ -36,33 +37,18 @@ export const createKibanaTestServer = async ( ) => { const server = await Server.create({ port: 0, tls }); server.route('/s/dummy/api/status', (req, res) => { - ( - reqCallback ?? - (() => { - return; - }) - )(req); + (reqCallback ?? noop)(req); res.end(JSON.stringify({ version: { number: kibanaVersion } })); }); server.route('/s/dummy/api/stats', (req, res) => { - ( - reqCallback ?? - (() => { - return; - }) - )(req); + (reqCallback ?? noop)(req); res.end(JSON.stringify({ kibana: { version: kibanaVersion } })); }); // Legacy server.route( '/s/dummy/api/synthetics/service/project/monitors', async (req, res) => { - ( - reqCallback ?? - (() => { - return; - }) - )(req); + (reqCallback ?? noop)(req); await new Promise(r => setTimeout(r, 20)); req.on('data', chunks => { const schema = JSON.parse(chunks.toString()) as LegacyAPISchema; @@ -88,12 +74,7 @@ export const createKibanaTestServer = async ( // Post 8.6 const basePath = '/s/dummy/api/synthetics/project/test-project/monitors'; server.route(basePath, (req, res) => { - ( - reqCallback ?? - (() => { - return; - }) - )(req); + (reqCallback ?? noop)(req); const getResp = { total: 2, monitors: [ @@ -104,12 +85,7 @@ export const createKibanaTestServer = async ( res.end(JSON.stringify(getResp)); }); server.route(basePath + '/_bulk_update', (req, res) => { - ( - reqCallback ?? - (() => { - return; - }) - )(req); + (reqCallback ?? noop)(req); const updateResponse = { createdMonitors: ['j1', 'j2'], updatedMonitors: [], @@ -118,12 +94,7 @@ export const createKibanaTestServer = async ( res.end(JSON.stringify(updateResponse)); }); server.route(basePath + '/_bulk_delete', (req, res) => { - ( - reqCallback ?? - (() => { - return; - }) - )(req); + (reqCallback ?? noop)(req); res.end(JSON.stringify({ deleted_monitors: ['j3', 'j4'] })); }); return server; diff --git a/package-lock.json b/package-lock.json index f7e98f1ce..c015d5c1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,8 +57,8 @@ "lint-staged": "^10.5.3", "prettier": "^2.7.1", "rimraf": "^3.0.2", - "straightforward": "^4.2.2", "semantic-release": "^24.2.5", + "straightforward": "^4.2.2", "ts-jest": "^29.1.1", "typescript": "^5.1.6" }, diff --git a/src/cli.ts b/src/cli.ts index dc0c73f08..d5a7186d7 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -29,12 +29,14 @@ import { program, Option } from 'commander'; import { cwd } from 'process'; import { bold } from 'kleur/colors'; import { resolve } from 'path'; -import { CliArgs, PushOptions } from './common_types'; +import { CliArgs, PushOptions, RunOptions } from './common_types'; import { reporters } from './reporters'; import { normalizeOptions, parseThrottling, getCommonCommandOpts, + collectOpts, + parseFileOption, } from './options'; import { globalSetup } from './loader'; import { run } from './'; @@ -59,6 +61,8 @@ import { setGlobalProxy } from './helpers'; /* eslint-disable-next-line @typescript-eslint/no-var-requires */ const { name, version } = require('../package.json'); +const proxySettings = {}; + const { params, pattern, @@ -69,11 +73,6 @@ const { tags, match, fields, - proxyToken, - proxyUri, - proxyNoVerify, - proxyCa, - proxyCert, } = getCommonCommandOpts(); program @@ -200,11 +199,32 @@ program 'the target Kibana spaces for the pushed monitors — spaces help you organise pushed monitors.' ) .option('-y, --yes', 'skip all questions and run non-interactively') - .addOption(proxyUri) - .addOption(proxyToken) - .addOption(proxyNoVerify) - .addOption(proxyCa) - .addOption(proxyCert) + .option( + '--proxy-uri ', + 'proxy uri to use when pushing to kibana', + collectOpts('uri', proxySettings) + ) + .option( + '--proxy-token ', + 'auth token to use the proxy', + collectOpts('token', proxySettings) + ) + .option( + '--proxy-ca ', + 'provide a CA override for proxy endpoint, as a path to be loaded or as string', + collectOpts('ca', proxySettings, parseFileOption) + ) + .option( + '--proxy-cert ', + 'provide a cert override for proxy endpoint, as a path to be loaded or as string', + collectOpts('cert', proxySettings, parseFileOption) + ) + .option( + '--proxy-no-verify', + 'disable TLS verification for the proxy connection', + collectOpts('noVerify', proxySettings), + false + ) .addOption(pattern) .addOption(tags) .addOption(fields) @@ -224,18 +244,15 @@ program { ...settings, ...cmdOpts, + ...{ + proxy: proxySettings, + }, }, 'push' )) as PushOptions; //Set up global proxy agent if any of the related options are set - setGlobalProxy( - options.proxyUri, - options.proxyToken, - options.proxyNoVerify ? false : true, - options.proxyCa, - options.proxyCert - ); + setGlobalProxy(options.proxy ?? {}); await validatePush(options, settings); const monitors = runner._buildMonitors(options); @@ -275,23 +292,47 @@ program ) .option('--url ', 'Kibana URL to fetch all public and private locations') .addOption(auth) - .addOption(proxyUri) - .addOption(proxyToken) - .addOption(proxyNoVerify) - .addOption(proxyCa) - .addOption(proxyCert) + .option( + '--proxy-uri ', + 'proxy uri to use when pushing to kibana', + collectOpts('uri', proxySettings) + ) + .option( + '--proxy-token ', + 'auth token to use the proxy', + collectOpts('token', proxySettings) + ) + .option( + '--proxy-ca ', + 'provide a CA override for proxy endpoint, as a path to be loaded or as string', + collectOpts('ca', proxySettings, parseFileOption) + ) + .option( + '--proxy-cert ', + 'provide a cert override for proxy endpoint, as a path to be loaded or as string', + collectOpts('cert', proxySettings, parseFileOption) + ) + .option( + '--proxy-no-verify', + 'disable TLS verification for the proxy connection', + collectOpts('noVerify', proxySettings), + false + ) .action(async (cmdOpts: LocationCmdOptions) => { const revert = installTransform(); const url = cmdOpts.url ?? (await loadSettings(null, true))?.url; try { - // Set up global proxy agent if any of the related options are set - setGlobalProxy( - cmdOpts.proxyUri, - cmdOpts.proxyToken, - cmdOpts.proxyNoVerify ? false : true, - cmdOpts.proxyCa, - cmdOpts.proxyCert - ); + const settings = await loadSettings(null, true); + const options = (await normalizeOptions({ + ...settings, + ...cmdOpts, + ...{ + proxy: proxySettings, + }, + })) as RunOptions; + + //Set up global proxy agent if any of the related options are set + setGlobalProxy(options.proxy ?? {}); if (url && cmdOpts.auth) { const allLocations = await getLocations({ url, diff --git a/src/common_types.ts b/src/common_types.ts index e93bd4d47..66e140c37 100644 --- a/src/common_types.ts +++ b/src/common_types.ts @@ -225,6 +225,7 @@ type BaseArgs = { privateLocations?: MonitorConfig['privateLocations']; fields?: MonitorConfig['fields']; spaces?: MonitorConfig['spaces']; + proxy?: ProxySettings; }; export type CliArgs = BaseArgs & { @@ -264,11 +265,6 @@ export type PushOptions = Partial & retestOnFailure?: MonitorConfig['retestOnFailure']; enabled?: boolean; grepOpts?: GrepOptions; - proxyUri?: string; - proxyToken?: string; - proxyCa?: string; - proxyCert?: string; - proxyNoVerify?: string; spaces?: MonitorConfig['spaces']; }; @@ -290,6 +286,7 @@ export type SyntheticsConfig = { playwrightOptions?: PlaywrightOptions; monitor?: MonitorConfig; project?: ProjectSettings; + proxy?: ProxySettings; }; /** Runner Payload types */ @@ -332,3 +329,11 @@ export type JourneyEndResult = JourneyStartResult & }; export type StepEndResult = StepResult; + +export type ProxySettings = { + uri?: string; + token?: string; + ca?: string | Buffer; + cert?: string | Buffer; + noVerify?: boolean; +}; diff --git a/src/helpers.ts b/src/helpers.ts index 94d51e397..e5b22f3c6 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -36,6 +36,7 @@ import { NetworkConditions, Location, ThrottlingOptions, + ProxySettings, } from './common_types'; import micromatch from 'micromatch'; import { ProxyAgent, setGlobalDispatcher } from 'undici'; @@ -407,19 +408,17 @@ export function tagsMatch(tags, pattern) { return matchess.length > 0; } -export function setGlobalProxy( - uri: string, - token: string, - rejectUnauthorized: boolean, - ca: string, - cert: string -) { +export function setGlobalProxy(opts: ProxySettings) { // Create proxy agent if options exist, if not attempt to load env proxy - const proxyAgent = uri + const proxyAgent = opts.uri ? new ProxyAgent({ - uri, - token, - proxyTls: { ca: ca ?? null, cert: cert ?? null, rejectUnauthorized }, + uri: opts.uri, + token: opts.token, + proxyTls: { + ca: opts.ca ?? null, + cert: opts.cert ?? null, + rejectUnauthorized: !opts.noVerify, + }, }) : new EnvHttpProxyAgent(); diff --git a/src/locations/index.ts b/src/locations/index.ts index c0cc068e1..d5acf09eb 100644 --- a/src/locations/index.ts +++ b/src/locations/index.ts @@ -30,11 +30,6 @@ import { generateURL } from '../push/utils'; export type LocationCmdOptions = { auth: string; url: string; - proxyUri?: string; - proxyToken?: string; - proxyCa?: string; - proxyCert?: string; - proxyNoVerify?: string; }; type LocationMetadata = { diff --git a/src/options.ts b/src/options.ts index 1f2b61442..48dc633b8 100644 --- a/src/options.ts +++ b/src/options.ts @@ -27,9 +27,9 @@ import merge from 'deepmerge'; import { createOption } from 'commander'; import { readConfig } from './config'; import type { CliArgs, RunOptions } from './common_types'; -import { THROTTLING_WARNING_MSG, warn } from './helpers'; +import { isFile, THROTTLING_WARNING_MSG, warn } from './helpers'; import path from 'path'; -import { existsSync, readFileSync } from 'fs'; +import { readFileSync } from 'fs'; type Mode = 'run' | 'push'; @@ -100,6 +100,9 @@ export async function normalizeOptions( cliArgs.ignoreHttpsErrors ?? playwrightOpts?.ignoreHTTPSErrors, }; + options.proxy = Object.freeze( + merge(config?.proxy ?? {}, cliArgs?.proxy || {}) + ); /** * Merge default options based on the mode of operation whether we are running tests locally * or pushing the project monitors @@ -257,27 +260,6 @@ export function getCommonCommandOpts() { }, {}); }); - const proxyUri = createOption( - '--proxy-uri ', - 'proxy uri to use when pushing to kibana' - ); - const proxyToken = createOption( - '--proxy-token ', - 'auth token to use the proxy' - ); - const proxyCa = createOption( - '--proxy-ca ', - 'provide a CA override for proxy endpoint, as a path to be loaded or as string ' - ).argParser(parseFileOption); - const proxyCert = createOption( - '--proxy-cert ', - 'provide a cert override for proxy endpoint, as a path to be loaded or as string ' - ).argParser(parseFileOption); - const proxyNoVerify = createOption( - '--proxy-no-verify', - 'disable TLS verification for the proxy connection' - ); - return { auth, authMandatory, @@ -288,11 +270,6 @@ export function getCommonCommandOpts() { tags, match, fields, - proxyUri, - proxyToken, - proxyCa, - proxyCert, - proxyNoVerify, }; } @@ -328,10 +305,10 @@ function parseAsBuffer(value: any): Buffer { } } -function parseFileOption(value: string) { +export function parseFileOption(value: string) { const filePath = path.resolve(value); - if (!existsSync(filePath)) { + if (!isFile(filePath)) { return value; } @@ -341,3 +318,12 @@ function parseFileOption(value: string) { throw new Error(`could not be read provided path ${filePath}: ${e}`); } } + +// This is a generic util to collect multiple options into a single +// dictionary +export function collectOpts(key, accumulator, nextParser?) { + return value => { + accumulator[key] = nextParser ? nextParser(value) : value; + return accumulator[key]; + }; +} From 90b1e57b1faf28f01404d18727673b62dde45511 Mon Sep 17 00:00:00 2001 From: emilioalvap Date: Thu, 25 Sep 2025 19:15:49 +0200 Subject: [PATCH 06/14] fix some tests --- __tests__/options.test.ts | 6 ++++-- src/cli.ts | 4 ++-- src/options.ts | 23 +++++++++++------------ 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/__tests__/options.test.ts b/__tests__/options.test.ts index 9dddf49fe..8db137e89 100644 --- a/__tests__/options.test.ts +++ b/__tests__/options.test.ts @@ -196,11 +196,13 @@ describe('options', () => { describe('parseFileOption', () => { it('parses file', () => { expect( - parseFileOption(join(__dirname, 'fixtures', 'synthetics.config.ts')) + parseFileOption('test')( + join(__dirname, 'fixtures', 'synthetics.config.ts') + ) ).toBeInstanceOf(Buffer); }); it('parses string', () => { - expect(parseFileOption('test')).toEqual('test'); + expect(parseFileOption('test')('test')).toEqual('test'); }); }); diff --git a/src/cli.ts b/src/cli.ts index d5a7186d7..5b831b341 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -305,12 +305,12 @@ program .option( '--proxy-ca ', 'provide a CA override for proxy endpoint, as a path to be loaded or as string', - collectOpts('ca', proxySettings, parseFileOption) + collectOpts('ca', proxySettings, parseFileOption('--proxy-ca')) ) .option( '--proxy-cert ', 'provide a cert override for proxy endpoint, as a path to be loaded or as string', - collectOpts('cert', proxySettings, parseFileOption) + collectOpts('cert', proxySettings, parseFileOption('--proxy-cert')) ) .option( '--proxy-no-verify', diff --git a/src/options.ts b/src/options.ts index 48dc633b8..269681bd1 100644 --- a/src/options.ts +++ b/src/options.ts @@ -28,7 +28,6 @@ import { createOption } from 'commander'; import { readConfig } from './config'; import type { CliArgs, RunOptions } from './common_types'; import { isFile, THROTTLING_WARNING_MSG, warn } from './helpers'; -import path from 'path'; import { readFileSync } from 'fs'; type Mode = 'run' | 'push'; @@ -305,18 +304,18 @@ function parseAsBuffer(value: any): Buffer { } } -export function parseFileOption(value: string) { - const filePath = path.resolve(value); - - if (!isFile(filePath)) { - return value; - } +export function parseFileOption(opt: string) { + return (value: string) => { + if (!isFile(value)) { + return value; + } - try { - return readFileSync(filePath); - } catch (e) { - throw new Error(`could not be read provided path ${filePath}: ${e}`); - } + try { + return readFileSync(value); + } catch (e) { + throw new Error(`${opt} - could not read provided path ${value}: ${e}`); + } + }; } // This is a generic util to collect multiple options into a single From 6f97d307669acfb21344994529a8f7b5f903442a Mon Sep 17 00:00:00 2001 From: emilioalvap Date: Fri, 3 Oct 2025 11:51:36 +0200 Subject: [PATCH 07/14] refactor proxy url in tests --- __tests__/locations/index.test.ts | 15 ++++++++------- __tests__/push/index.test.ts | 23 +++++++++-------------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/__tests__/locations/index.test.ts b/__tests__/locations/index.test.ts index 678b04ef1..081bf8a15 100644 --- a/__tests__/locations/index.test.ts +++ b/__tests__/locations/index.test.ts @@ -135,7 +135,7 @@ describe('Locations', () => { let requests: Array = []; let proxyServer: Straightforward; let tlsServer; - let proxyPort: number; + let proxyUrl: string; beforeAll(async () => { proxyServer = new Straightforward(); @@ -149,7 +149,8 @@ describe('Locations', () => { res.setHeader('content-type', 'application/json'); res.end(JSON.stringify({ locations: LOCATIONS })); }); - ({ port: proxyPort } = proxyServer.server.address() as AddressInfo); + const server = proxyServer.server.address() as AddressInfo; + proxyUrl = `http://localhost:${server.port}`; }); afterAll(async () => { @@ -164,7 +165,7 @@ describe('Locations', () => { it('enables proxy based on HTTP_PROXY', async () => { await fakeProjectSetup({ project: { url: server.PREFIX } }); const output = await runLocations(['--auth', apiKey], { - HTTP_PROXY: `http://localhost:${proxyPort}`, + HTTP_PROXY: proxyUrl, }); expect(output).toContain(`custom location 1`); expect(requests).toHaveLength(1); @@ -174,7 +175,7 @@ describe('Locations', () => { await fakeProjectSetup({ project: { url: server.PREFIX } }); const output = await runLocations(['--auth', apiKey], { NO_PROXY: '*', - HTTP_PROXY: `http://localhost:${proxyPort}`, + HTTP_PROXY: proxyUrl, }); expect(output).toContain(`custom location 1`); expect(requests).toHaveLength(0); @@ -183,7 +184,7 @@ describe('Locations', () => { it('enables proxy based on HTTPS_PROXY', async () => { await fakeProjectSetup({ project: { url: tlsServer.PREFIX } }); const output = await runLocations(['--auth', apiKey], { - HTTPS_PROXY: `http://localhost:${proxyPort}`, + HTTPS_PROXY: proxyUrl, NODE_TLS_REJECT_UNAUTHORIZED: '0', }); expect(output).toContain(`custom location 1`); @@ -196,7 +197,7 @@ describe('Locations', () => { '--auth', apiKey, '--proxy-uri', - `http://localhost:${proxyPort}`, + proxyUrl, ]); expect(output).toContain(`custom location 1`); expect(requests).toHaveLength(1); @@ -205,7 +206,7 @@ describe('Locations', () => { it('enables proxy based on proxy settings', async () => { await fakeProjectSetup({ project: { url: server.PREFIX }, - proxy: { uri: `http://localhost:${proxyPort}` }, + proxy: { uri: proxyUrl }, }); const output = await runLocations(['--auth', apiKey]); expect(output).toContain(`custom location 1`); diff --git a/__tests__/push/index.test.ts b/__tests__/push/index.test.ts index bcc8b837f..db00f9e43 100644 --- a/__tests__/push/index.test.ts +++ b/__tests__/push/index.test.ts @@ -360,7 +360,7 @@ heartbeat.monitors: let requests: Array = []; let proxyServer: Straightforward; let tlsServer; - let proxyPort: number; + let proxyUrl: string; beforeAll(async () => { proxyServer = new Straightforward(); @@ -372,7 +372,8 @@ heartbeat.monitors: tlsServer = await createKibanaTestServer('8.6.0', true, (req: any) => requests.push(req) ); - ({ port: proxyPort } = proxyServer.server.address() as AddressInfo); + const server = proxyServer.server.address() as AddressInfo; + proxyUrl = `http://localhost:${server.port}`; }); afterAll(async () => { @@ -398,7 +399,7 @@ heartbeat.monitors: ); const output = await runPush([...DEFAULT_ARGS, '--tags', 'chunk'], { CHUNK_SIZE: '1', - HTTP_PROXY: `http://localhost:${proxyPort}`, + HTTP_PROXY: proxyUrl, }); await rm(testJourney, { force: true }); expect(output).toContain('Added(2)'); @@ -421,7 +422,7 @@ heartbeat.monitors: ); const output = await runPush([...DEFAULT_ARGS, '--tags', 'chunk'], { CHUNK_SIZE: '1', - HTTP_PROXY: `http://localhost:${proxyPort}`, + HTTP_PROXY: proxyUrl, NO_PROXY: '*', }); await rm(testJourney, { force: true }); @@ -451,7 +452,7 @@ heartbeat.monitors: ); const output = await runPush([...DEFAULT_ARGS, '--tags', 'chunk'], { CHUNK_SIZE: '1', - HTTPS_PROXY: `http://localhost:${proxyPort}`, + HTTPS_PROXY: proxyUrl, NODE_TLS_REJECT_UNAUTHORIZED: '0', }); await rm(testJourney, { force: true }); @@ -465,7 +466,7 @@ heartbeat.monitors: await fakeProjectSetup( { project: { id: 'test-project', space: 'dummy', url: server.PREFIX }, - proxy: { uri: `http://localhost:${proxyPort}` }, + proxy: { uri: proxyUrl }, }, { locations: ['test-loc'], schedule: 3 } ); @@ -477,13 +478,7 @@ heartbeat.monitors: journey('b', () => monitor.use({ tags: ['chunk'] }));` ); const output = await runPush( - [ - ...DEFAULT_ARGS, - '--tags', - 'chunk', - '--proxy-uri', - `http://localhost:${proxyPort}`, - ], + [...DEFAULT_ARGS, '--tags', 'chunk', '--proxy-uri', proxyUrl], { CHUNK_SIZE: '1', } @@ -499,7 +494,7 @@ heartbeat.monitors: await fakeProjectSetup( { project: { id: 'test-project', space: 'dummy', url: server.PREFIX }, - proxy: { uri: `http://localhost:${proxyPort}` }, + proxy: { uri: proxyUrl }, }, { locations: ['test-loc'], schedule: 3 } ); From 5b729e711fc65fac37c88605335386dc139bd4c7 Mon Sep 17 00:00:00 2001 From: emilioalvap Date: Fri, 3 Oct 2025 12:59:39 +0200 Subject: [PATCH 08/14] Extend test timeout --- __tests__/push/index.test.ts | 2 +- package.json | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/__tests__/push/index.test.ts b/__tests__/push/index.test.ts index db00f9e43..98097c688 100644 --- a/__tests__/push/index.test.ts +++ b/__tests__/push/index.test.ts @@ -359,7 +359,7 @@ heartbeat.monitors: describe('Proxy options', () => { let requests: Array = []; let proxyServer: Straightforward; - let tlsServer; + let tlsServer: any; let proxyUrl: string; beforeAll(async () => { diff --git a/package.json b/package.json index 984cadfbc..02a9beabc 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,9 @@ "test:browser-service": "WSENDPOINT=ws://localhost:9322 npm run test:unit", "coverage": "jest --coverage" }, + "jest": { + "testTimeout": 60e3 + }, "bin": { "@elastic/synthetics": "dist/cli.js", "elastic-synthetics": "dist/cli.js" From a31dd5d7ddc6452d6fd58836a58c1bdff0810084 Mon Sep 17 00:00:00 2001 From: emilioalvap Date: Fri, 3 Oct 2025 14:30:50 +0200 Subject: [PATCH 09/14] Extend test timeout --- jest.config.js | 2 +- package.json | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/jest.config.js b/jest.config.js index 4fda81f65..1cefed2f2 100644 --- a/jest.config.js +++ b/jest.config.js @@ -34,5 +34,5 @@ module.exports = { modulePathIgnorePatterns: ['/e2e/', '/utils/', '/common/', '/fixtures/'], globalSetup: `${__dirname}/__tests__/utils/jest-global-setup.ts`, globalTeardown: `${__dirname}/__tests__/utils/jest-global-teardown.ts`, - testTimeout: 15000, + testTimeout: 45e3, }; diff --git a/package.json b/package.json index 02a9beabc..984cadfbc 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,6 @@ "test:browser-service": "WSENDPOINT=ws://localhost:9322 npm run test:unit", "coverage": "jest --coverage" }, - "jest": { - "testTimeout": 60e3 - }, "bin": { "@elastic/synthetics": "dist/cli.js", "elastic-synthetics": "dist/cli.js" From f4e598c9f64519be63950b98ebf59818f24f9148 Mon Sep 17 00:00:00 2001 From: emilioalvap Date: Fri, 3 Oct 2025 15:34:01 +0200 Subject: [PATCH 10/14] Extend test timeout --- jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index 1cefed2f2..6c3d8a80d 100644 --- a/jest.config.js +++ b/jest.config.js @@ -34,5 +34,5 @@ module.exports = { modulePathIgnorePatterns: ['/e2e/', '/utils/', '/common/', '/fixtures/'], globalSetup: `${__dirname}/__tests__/utils/jest-global-setup.ts`, globalTeardown: `${__dirname}/__tests__/utils/jest-global-teardown.ts`, - testTimeout: 45e3, + testTimeout: 120e3, }; From 7a476be41922e015eca61591b01745ea13019106 Mon Sep 17 00:00:00 2001 From: emilioalvap Date: Fri, 3 Oct 2025 16:01:11 +0200 Subject: [PATCH 11/14] Extend timeout --- __tests__/locations/index.test.ts | 2 +- __tests__/push/index.test.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/__tests__/locations/index.test.ts b/__tests__/locations/index.test.ts index 081bf8a15..7f306a685 100644 --- a/__tests__/locations/index.test.ts +++ b/__tests__/locations/index.test.ts @@ -150,7 +150,7 @@ describe('Locations', () => { res.end(JSON.stringify({ locations: LOCATIONS })); }); const server = proxyServer.server.address() as AddressInfo; - proxyUrl = `http://localhost:${server.port}`; + proxyUrl = `http://127.0.0.1:${server.port}`; }); afterAll(async () => { diff --git a/__tests__/push/index.test.ts b/__tests__/push/index.test.ts index 98097c688..8ab941c4e 100644 --- a/__tests__/push/index.test.ts +++ b/__tests__/push/index.test.ts @@ -49,8 +49,8 @@ describe('Push', () => { } async function fakeProjectSetup( - settings, - monitor, + settings: any, + monitor: any, filename = 'synthetics.config.ts' ) { await writeFile( @@ -373,7 +373,7 @@ heartbeat.monitors: requests.push(req) ); const server = proxyServer.server.address() as AddressInfo; - proxyUrl = `http://localhost:${server.port}`; + proxyUrl = `http://127.0.0.1:${server.port}`; }); afterAll(async () => { From 9446b1533be82d5229e1a6de740e7c9fff22611e Mon Sep 17 00:00:00 2001 From: emilioalvap Date: Fri, 3 Oct 2025 17:26:05 +0200 Subject: [PATCH 12/14] Test ci --- __tests__/locations/index.test.ts | 6 +++--- __tests__/push/index.test.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/__tests__/locations/index.test.ts b/__tests__/locations/index.test.ts index 7f306a685..ae874f8ab 100644 --- a/__tests__/locations/index.test.ts +++ b/__tests__/locations/index.test.ts @@ -134,7 +134,7 @@ describe('Locations', () => { describe('Proxy options', () => { let requests: Array = []; let proxyServer: Straightforward; - let tlsServer; + let tlsServer: any; let proxyUrl: string; beforeAll(async () => { @@ -154,8 +154,8 @@ describe('Locations', () => { }); afterAll(async () => { - proxyServer.close(); - tlsServer.close(); + proxyServer?.close(); + tlsServer?.close(); }); beforeEach(() => { diff --git a/__tests__/push/index.test.ts b/__tests__/push/index.test.ts index 8ab941c4e..347f1e5df 100644 --- a/__tests__/push/index.test.ts +++ b/__tests__/push/index.test.ts @@ -377,8 +377,8 @@ heartbeat.monitors: }); afterAll(async () => { - proxyServer.close(); - tlsServer.close(); + proxyServer?.close(); + tlsServer?.close(); }); beforeEach(() => { From 808683a05971882401d0002287f79b28ed5453e7 Mon Sep 17 00:00:00 2001 From: emilioalvap Date: Fri, 3 Oct 2025 18:05:59 +0200 Subject: [PATCH 13/14] Remove proxy port contention in tests --- __tests__/locations/index.test.ts | 2 +- __tests__/push/index.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/__tests__/locations/index.test.ts b/__tests__/locations/index.test.ts index ae874f8ab..84f4b6744 100644 --- a/__tests__/locations/index.test.ts +++ b/__tests__/locations/index.test.ts @@ -143,7 +143,7 @@ describe('Locations', () => { requests.push(req); return next(); }); - await proxyServer.listen(); + await proxyServer.listen(Math.trunc(65e3 * Math.random())); tlsServer = await Server.create({ tls: true }); tlsServer.route('/internal/uptime/service/locations', (req, res) => { res.setHeader('content-type', 'application/json'); diff --git a/__tests__/push/index.test.ts b/__tests__/push/index.test.ts index 347f1e5df..55f9dc3c2 100644 --- a/__tests__/push/index.test.ts +++ b/__tests__/push/index.test.ts @@ -368,7 +368,7 @@ heartbeat.monitors: requests.push(req); return next(); }); - await proxyServer.listen(); + await proxyServer.listen(Math.trunc(65e3 * Math.random())); tlsServer = await createKibanaTestServer('8.6.0', true, (req: any) => requests.push(req) ); From d079393b3916b4fcef497513b5ec817ef24dcac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Alvarez=20Pi=C3=B1eiro?= <95703246+emilioalvap@users.noreply.github.com> Date: Fri, 3 Oct 2025 18:12:11 +0200 Subject: [PATCH 14/14] Update jest.config.js --- jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index 6c3d8a80d..4fda81f65 100644 --- a/jest.config.js +++ b/jest.config.js @@ -34,5 +34,5 @@ module.exports = { modulePathIgnorePatterns: ['/e2e/', '/utils/', '/common/', '/fixtures/'], globalSetup: `${__dirname}/__tests__/utils/jest-global-setup.ts`, globalTeardown: `${__dirname}/__tests__/utils/jest-global-teardown.ts`, - testTimeout: 120e3, + testTimeout: 15000, };