2024-08-23 04:43:26 -07:00
#!/usr/bin/env zx
/ * *
* Script to run benchmarks on the cloud benchmark environment .
* This script will :
* 1. Provision a benchmark environment using Terraform .
* 2. Run the benchmarks on the VM .
* 3. Destroy the cloud environment .
*
* NOTE : Must be run in the root of the package .
* /
// @ts-check
2024-09-02 04:58:24 -07:00
import { sleep , which , $ , tmpdir } from 'zx' ;
2024-08-23 04:43:26 -07:00
import path from 'path' ;
2024-09-12 06:06:43 -07:00
import { SshClient } from './clients/ssh-client.mjs' ;
import { TerraformClient } from './clients/terraform-client.mjs' ;
2024-09-09 23:25:41 -07:00
import { flagsObjectToCliArgs } from './utils/flags.mjs' ;
2024-08-23 04:43:26 -07:00
/ * *
* @ typedef { Object } BenchmarkEnv
* @ property { string } vmName
2024-09-02 04:58:24 -07:00
* @ property { string } ip
* @ property { string } sshUsername
* @ property { string } sshPrivateKeyPath
2024-08-23 04:43:26 -07:00
* /
2024-08-29 22:46:55 -07:00
/ * *
* @ typedef { Object } Config
* @ property { boolean } isVerbose
* @ property { string [ ] } n8nSetupsToUse
* @ property { string } n8nTag
* @ property { string } benchmarkTag
* @ property { string } [ k6ApiToken ]
2024-09-10 07:41:33 -07:00
* @ property { string } [ resultWebhookUrl ]
* @ property { string } [ resultWebhookAuthHeader ]
2024-09-08 23:07:32 -07:00
* @ property { string } [ n8nLicenseCert ]
2024-09-09 23:25:41 -07:00
* @ property { string } [ vus ]
* @ property { string } [ duration ]
2024-08-29 22:46:55 -07:00
*
* @ param { Config } config
* /
export async function runInCloud ( config ) {
2024-08-23 04:43:26 -07:00
await ensureDependencies ( ) ;
const terraformClient = new TerraformClient ( {
isVerbose : config . isVerbose ,
} ) ;
2024-09-04 03:14:41 -07:00
const benchmarkEnv = await terraformClient . getTerraformOutputs ( ) ;
2024-08-23 04:43:26 -07:00
2024-09-04 03:14:41 -07:00
await runBenchmarksOnVm ( config , benchmarkEnv ) ;
2024-08-23 04:43:26 -07:00
}
async function ensureDependencies ( ) {
await which ( 'terraform' ) ;
await which ( 'az' ) ;
}
/ * *
* @ param { Config } config
* @ param { BenchmarkEnv } benchmarkEnv
* /
async function runBenchmarksOnVm ( config , benchmarkEnv ) {
2024-08-29 22:46:55 -07:00
console . log ( ` Setting up the environment... ` ) ;
2024-08-23 04:43:26 -07:00
const sshClient = new SshClient ( {
2024-09-02 04:58:24 -07:00
ip : benchmarkEnv . ip ,
username : benchmarkEnv . sshUsername ,
privateKeyPath : benchmarkEnv . sshPrivateKeyPath ,
2024-08-23 04:43:26 -07:00
verbose : config . isVerbose ,
} ) ;
await ensureVmIsReachable ( sshClient ) ;
2024-09-02 04:58:24 -07:00
const scriptsDir = await transferScriptsToVm ( sshClient , config ) ;
2024-08-23 04:43:26 -07:00
// Bootstrap the environment with dependencies
console . log ( 'Running bootstrap script...' ) ;
const bootstrapScriptPath = path . join ( scriptsDir , 'bootstrap.sh' ) ;
await sshClient . ssh ( ` chmod a+x ${ bootstrapScriptPath } && ${ bootstrapScriptPath } ` ) ;
// Give some time for the VM to be ready
await sleep ( 1000 ) ;
2024-08-29 22:46:55 -07:00
for ( const n8nSetup of config . n8nSetupsToUse ) {
2024-08-27 07:51:43 -07:00
await runBenchmarkForN8nSetup ( {
config ,
sshClient ,
scriptsDir ,
2024-08-29 22:46:55 -07:00
n8nSetup ,
2024-08-27 07:51:43 -07:00
} ) ;
}
}
/ * *
* @ param { { config : Config ; sshClient : any ; scriptsDir : string ; n8nSetup : string ; } } opts
* /
async function runBenchmarkForN8nSetup ( { config , sshClient , scriptsDir , n8nSetup } ) {
console . log ( ` Running benchmarks for ${ n8nSetup } ... ` ) ;
2024-09-12 06:06:43 -07:00
const runScriptPath = path . join ( scriptsDir , 'run-for-n8n-setup.mjs' ) ;
2024-08-23 08:14:19 -07:00
2024-09-09 23:25:41 -07:00
const cliArgs = flagsObjectToCliArgs ( {
2024-08-23 08:14:19 -07:00
n8nDockerTag : config . n8nTag ,
benchmarkDockerTag : config . benchmarkTag ,
k6ApiToken : config . k6ApiToken ,
2024-09-10 07:41:33 -07:00
resultWebhookUrl : config . resultWebhookUrl ,
resultWebhookAuthHeader : config . resultWebhookAuthHeader ,
2024-09-08 23:07:32 -07:00
n8nLicenseCert : config . n8nLicenseCert ,
2024-09-09 23:25:41 -07:00
vus : config . vus ,
duration : config . duration ,
2024-09-10 07:41:33 -07:00
env : 'cloud' ,
2024-09-09 23:25:41 -07:00
} ) ;
2024-08-23 08:14:19 -07:00
2024-09-09 23:25:41 -07:00
const flagsString = cliArgs . join ( ' ' ) ;
2024-08-23 08:14:19 -07:00
2024-08-27 07:51:43 -07:00
await sshClient . ssh ( ` npx zx ${ runScriptPath } ${ flagsString } ${ n8nSetup } ` , {
2024-08-23 08:14:19 -07:00
// Test run should always log its output
verbose : true ,
} ) ;
2024-08-23 04:43:26 -07:00
}
async function ensureVmIsReachable ( sshClient ) {
2024-09-04 03:14:41 -07:00
try {
await sshClient . ssh ( 'echo "VM is reachable"' ) ;
} catch ( error ) {
console . error ( ` VM is not reachable: ${ error . message } ` ) ;
console . error (
` Did you provision the cloud environment first with 'pnpm provision-cloud-env'? You can also run the benchmarks locally with 'pnpm run benchmark-locally'. ` ,
) ;
process . exit ( 1 ) ;
}
2024-08-23 04:43:26 -07:00
}
/ * *
* @ returns Path where the scripts are located on the VM
* /
2024-09-02 04:58:24 -07:00
async function transferScriptsToVm ( sshClient , config ) {
const cwd = process . cwd ( ) ;
const scriptsDir = path . resolve ( cwd , './scripts' ) ;
const tarFilename = 'scripts.tar.gz' ;
const scriptsTarPath = path . join ( tmpdir ( 'n8n-benchmark' ) , tarFilename ) ;
const $$ = $ ( { verbose : config . isVerbose } ) ;
// Compress the scripts folder
await $$ ` tar -czf ${ scriptsTarPath } ${ scriptsDir } -C ${ cwd } ./scripts ` ;
// Transfer the scripts to the VM
await sshClient . scp ( scriptsTarPath , ` ~/ ${ tarFilename } ` ) ;
// Extract the scripts on the VM
await sshClient . ssh ( ` tar -xzf ~/ ${ tarFilename } ` ) ;
2024-08-23 04:43:26 -07:00
2024-09-02 04:58:24 -07:00
return '~/scripts' ;
2024-08-23 04:43:26 -07:00
}