From 042a128791eb6113c0721d57d19226a835f11a49 Mon Sep 17 00:00:00 2001 From: Michal Kimle Date: Thu, 1 Dec 2022 13:49:23 +0100 Subject: [PATCH] Replace Airnode address short with deployment ID --- .changeset/unlucky-eggs-drive.md | 7 +++ .../src/infrastructure/index.test.ts | 45 +++++++---------- .../src/infrastructure/index.ts | 48 +++++++------------ packages/airnode-deployer/src/utils/evm.ts | 12 ----- .../terraform/aws/variables.tf | 18 +++---- .../terraform/gcp/variables.tf | 18 +++---- .../src/adapters/http/worker.test.ts | 8 ++-- .../airnode-node/src/coordinator/state.ts | 6 ++- packages/airnode-node/src/evm/wallet.test.ts | 8 ---- packages/airnode-node/src/evm/wallet.ts | 5 -- packages/airnode-node/src/evm/workers.test.ts | 10 ++-- .../src/handlers/start-coordinator.test.ts | 2 +- .../src/handlers/start-coordinator.ts | 3 +- .../src/providers/actions.test.ts | 4 +- .../airnode-node/src/providers/state.test.ts | 4 +- packages/airnode-node/src/providers/state.ts | 4 +- .../airnode-node/src/providers/worker.test.ts | 6 +-- packages/airnode-node/src/types.ts | 5 +- .../src/workers/cloud-platforms/aws.test.ts | 8 ++-- .../src/workers/cloud-platforms/aws.ts | 2 +- .../src/workers/cloud-platforms/gcp.test.ts | 4 +- .../src/workers/cloud-platforms/gcp.ts | 2 +- packages/airnode-node/src/workers/index.ts | 13 +++-- .../test/fixtures/config/worker-options.ts | 3 +- 24 files changed, 95 insertions(+), 150 deletions(-) create mode 100644 .changeset/unlucky-eggs-drive.md diff --git a/.changeset/unlucky-eggs-drive.md b/.changeset/unlucky-eggs-drive.md new file mode 100644 index 0000000000..545c6baeab --- /dev/null +++ b/.changeset/unlucky-eggs-drive.md @@ -0,0 +1,7 @@ +--- +'@api3/airnode-deployer': minor +'@api3/airnode-node': minor +'@api3/airnode-validator': minor +--- + +Replace Airnode's short address with deployment ID diff --git a/packages/airnode-deployer/src/infrastructure/index.test.ts b/packages/airnode-deployer/src/infrastructure/index.test.ts index fbc7dd8e5d..415ee42c52 100644 --- a/packages/airnode-deployer/src/infrastructure/index.test.ts +++ b/packages/airnode-deployer/src/infrastructure/index.test.ts @@ -246,8 +246,7 @@ describe('prepareCloudProviderAirnodeApplyDestoryArguments', () => { describe('prepareAirnodeApplyDestroyArguments', () => { const variables = { - airnodeAddressShort: 'd0624e6', - stage: 'dev', + deploymentId: 'aws7195b548', configPath: '/some/path/config.json', secretsPath: '/some/path/secrets.env', handlerDir: '/some/path/handlers', @@ -257,8 +256,7 @@ describe('prepareAirnodeApplyDestroyArguments', () => { it('returns cloud provider agnostic Terraform variables', () => { const expectedArguments = [ - ['var', 'airnode_address_short', variables.airnodeAddressShort], - ['var', 'stage', variables.stage], + ['var', 'deployment_id', variables.deploymentId], ['var', 'configuration_file', variables.configPath], ['var', 'secrets_file', variables.secretsPath], ['var', 'handler_dir', variables.handlerDir], @@ -272,14 +270,11 @@ describe('prepareAirnodeApplyDestroyArguments', () => { it('sets the missing optional arguments correctly', () => { const onlyRequiredVariables = pick(variables, [ - 'airnodeAddressShort', - 'stage', 'handlerDir', 'disableConcurrencyReservations', ]) as infrastructure.AirnodeApplyDestroyVariables; const expectedArguments = [ - ['var', 'airnode_address_short', onlyRequiredVariables.airnodeAddressShort], - ['var', 'stage', onlyRequiredVariables.stage], + ['var', 'deployment_id', onlyRequiredVariables.deploymentId], ['var', 'configuration_file', 'NULL'], ['var', 'secrets_file', 'NULL'], ['var', 'handler_dir', onlyRequiredVariables.handlerDir], @@ -354,7 +349,7 @@ describe('terraformAirnodeApply', () => { ); expect(exec).toHaveBeenNthCalledWith( 2, - `terraform apply -var="aws_region=us-east-1" -var="airnode_address_short=a30ca71" -var="stage=dev" -var="configuration_file=${configPath}" -var="secrets_file=${secretsPath}" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=0xd627c727db73ed7067cbc1e15295f7004b83c01d243aa90711d549cda6bd5bca" -input=false -no-color -var="max_concurrency=100" -var="http_gateway_enabled=true" -var="http_max_concurrency=20" -var="http_signed_data_gateway_enabled=true" -var="http_signed_data_max_concurrency=20" -auto-approve`, + `terraform apply -var="aws_region=us-east-1" -var="deployment_id=aws40207f25" -var="configuration_file=${configPath}" -var="secrets_file=${secretsPath}" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=0xd627c727db73ed7067cbc1e15295f7004b83c01d243aa90711d549cda6bd5bca" -input=false -no-color -var="max_concurrency=100" -var="http_gateway_enabled=true" -var="http_max_concurrency=20" -var="http_signed_data_gateway_enabled=true" -var="http_signed_data_max_concurrency=20" -auto-approve`, execOptions ); }); @@ -380,12 +375,12 @@ describe('terraformAirnodeApply', () => { ); expect(exec).toHaveBeenNthCalledWith( 2, - `terraform import -var="gcp_region=us-east1" -var="gcp_project=airnode-test-123456" -var="airnode_bucket=airnode-123456789" -var="deployment_bucket_dir=airnode-address/stage/timestamp" -var="airnode_address_short=a30ca71" -var="stage=dev" -var="configuration_file=${configPath}" -var="secrets_file=${secretsPath}" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=0xd627c727db73ed7067cbc1e15295f7004b83c01d243aa90711d549cda6bd5bca" -input=false -no-color -var="max_concurrency=100" -var="http_gateway_enabled=true" -var="http_max_concurrency=20" -var="http_signed_data_gateway_enabled=true" -var="http_signed_data_max_concurrency=20" module.startCoordinator.google_app_engine_application.app[0] airnode-test-123456`, + `terraform import -var="gcp_region=us-east1" -var="gcp_project=airnode-test-123456" -var="airnode_bucket=airnode-123456789" -var="deployment_bucket_dir=airnode-address/stage/timestamp" -var="deployment_id=gcp1fc73e56" -var="configuration_file=${configPath}" -var="secrets_file=${secretsPath}" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=0xd627c727db73ed7067cbc1e15295f7004b83c01d243aa90711d549cda6bd5bca" -input=false -no-color -var="max_concurrency=100" -var="http_gateway_enabled=true" -var="http_max_concurrency=20" -var="http_signed_data_gateway_enabled=true" -var="http_signed_data_max_concurrency=20" module.startCoordinator.google_app_engine_application.app[0] airnode-test-123456`, { ignoreError: true } ); expect(exec).toHaveBeenNthCalledWith( 3, - `terraform apply -var="gcp_region=us-east1" -var="gcp_project=airnode-test-123456" -var="airnode_bucket=airnode-123456789" -var="deployment_bucket_dir=airnode-address/stage/timestamp" -var="airnode_address_short=a30ca71" -var="stage=dev" -var="configuration_file=${configPath}" -var="secrets_file=${secretsPath}" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=0xd627c727db73ed7067cbc1e15295f7004b83c01d243aa90711d549cda6bd5bca" -input=false -no-color -var="max_concurrency=100" -var="http_gateway_enabled=true" -var="http_max_concurrency=20" -var="http_signed_data_gateway_enabled=true" -var="http_signed_data_max_concurrency=20" -auto-approve`, + `terraform apply -var="gcp_region=us-east1" -var="gcp_project=airnode-test-123456" -var="airnode_bucket=airnode-123456789" -var="deployment_bucket_dir=airnode-address/stage/timestamp" -var="deployment_id=gcp1fc73e56" -var="configuration_file=${configPath}" -var="secrets_file=${secretsPath}" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=0xd627c727db73ed7067cbc1e15295f7004b83c01d243aa90711d549cda6bd5bca" -input=false -no-color -var="max_concurrency=100" -var="http_gateway_enabled=true" -var="http_max_concurrency=20" -var="http_signed_data_gateway_enabled=true" -var="http_signed_data_max_concurrency=20" -auto-approve`, execOptions ); }); @@ -419,7 +414,7 @@ describe('terraformAirnodeApply', () => { ); expect(exec).toHaveBeenNthCalledWith( 2, - `terraform apply -var="aws_region=us-east-1" -var="airnode_address_short=a30ca71" -var="stage=dev" -var="configuration_file=${configPath}" -var="secrets_file=${secretsPath}" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=0xd627c727db73ed7067cbc1e15295f7004b83c01d243aa90711d549cda6bd5bca" -input=false -no-color -var="max_concurrency=100" -auto-approve`, + `terraform apply -var="aws_region=us-east-1" -var="deployment_id=aws40207f25" -var="configuration_file=${configPath}" -var="secrets_file=${secretsPath}" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=0xd627c727db73ed7067cbc1e15295f7004b83c01d243aa90711d549cda6bd5bca" -input=false -no-color -var="max_concurrency=100" -auto-approve`, execOptions ); }); @@ -487,7 +482,7 @@ describe('deployAirnode', () => { ); expect(exec).toHaveBeenNthCalledWith( 2, - `terraform apply -var="aws_region=us-east-1" -var="airnode_address_short=a30ca71" -var="stage=dev" -var="configuration_file=${configPath}" -var="secrets_file=${secretsPath}" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=0xd627c727db73ed7067cbc1e15295f7004b83c01d243aa90711d549cda6bd5bca" -input=false -no-color -var="max_concurrency=100" -var="http_gateway_enabled=true" -var="http_max_concurrency=20" -var="http_signed_data_gateway_enabled=true" -var="http_signed_data_max_concurrency=20" -auto-approve`, + `terraform apply -var="aws_region=us-east-1" -var="deployment_id=aws40207f25" -var="configuration_file=${configPath}" -var="secrets_file=${secretsPath}" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=0xd627c727db73ed7067cbc1e15295f7004b83c01d243aa90711d549cda6bd5bca" -input=false -no-color -var="max_concurrency=100" -var="http_gateway_enabled=true" -var="http_max_concurrency=20" -var="http_signed_data_gateway_enabled=true" -var="http_signed_data_max_concurrency=20" -auto-approve`, { cwd: 'tmpDir' } ); expect(exec).toHaveBeenNthCalledWith(3, 'terraform output -json -no-color', { cwd: 'tmpDir' }); @@ -527,7 +522,7 @@ describe('deployAirnode', () => { ); expect(exec).toHaveBeenNthCalledWith( 2, - `terraform apply -var="aws_region=us-east-1" -var="airnode_address_short=a30ca71" -var="stage=dev" -var="configuration_file=${configPath}" -var="secrets_file=${secretsPath}" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=0xd627c727db73ed7067cbc1e15295f7004b83c01d243aa90711d549cda6bd5bca" -input=false -no-color -var="max_concurrency=100" -var="http_gateway_enabled=true" -var="http_max_concurrency=20" -var="http_signed_data_gateway_enabled=true" -var="http_signed_data_max_concurrency=20" -auto-approve`, + `terraform apply -var="aws_region=us-east-1" -var="deployment_id=aws40207f25" -var="configuration_file=${configPath}" -var="secrets_file=${secretsPath}" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=0xd627c727db73ed7067cbc1e15295f7004b83c01d243aa90711d549cda6bd5bca" -input=false -no-color -var="max_concurrency=100" -var="http_gateway_enabled=true" -var="http_max_concurrency=20" -var="http_signed_data_gateway_enabled=true" -var="http_signed_data_max_concurrency=20" -auto-approve`, { cwd: 'tmpDir' } ); expect(exec).toHaveBeenNthCalledWith(3, 'terraform output -json -no-color', { cwd: 'tmpDir' }); @@ -570,7 +565,7 @@ describe('deployAirnode', () => { ); expect(exec).toHaveBeenNthCalledWith( 2, - `terraform apply -var="aws_region=us-east-1" -var="airnode_address_short=a30ca71" -var="stage=dev" -var="configuration_file=${configPath}" -var="secrets_file=${secretsPath}" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=0xd627c727db73ed7067cbc1e15295f7004b83c01d243aa90711d549cda6bd5bca" -input=false -no-color -var="max_concurrency=100" -var="http_gateway_enabled=true" -var="http_max_concurrency=20" -var="http_signed_data_gateway_enabled=true" -var="http_signed_data_max_concurrency=20" -auto-approve`, + `terraform apply -var="aws_region=us-east-1" -var="deployment_id=aws40207f25" -var="configuration_file=${configPath}" -var="secrets_file=${secretsPath}" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=0xd627c727db73ed7067cbc1e15295f7004b83c01d243aa90711d549cda6bd5bca" -input=false -no-color -var="max_concurrency=100" -var="http_gateway_enabled=true" -var="http_max_concurrency=20" -var="http_signed_data_gateway_enabled=true" -var="http_signed_data_max_concurrency=20" -auto-approve`, { cwd: 'tmpDir' } ); expect(exec).toHaveBeenNthCalledWith(3, 'terraform output -json -no-color', { cwd: 'tmpDir' }); @@ -638,8 +633,7 @@ describe('terraformAirnodeDestroy', () => { region: 'europe-central-1', } as AwsCloudProvider; const handlerDir = path.resolve(`${__dirname}/../../.webpack`); - const airnodeAddressShort = 'a30ca71'; - const stage = 'dev'; + const deploymentId = 'aws7195b548'; const bucket = { name: 'airnode-123456789', region: 'us-east-1', @@ -650,14 +644,7 @@ describe('terraformAirnodeDestroy', () => { const commandOutput = 'example command output'; exec.mockImplementation(() => ({ stdout: commandOutput })); - await infrastructure.terraformAirnodeDestroy( - execOptions, - cloudProvider, - airnodeAddressShort, - stage, - bucket, - bucketPath - ); + await infrastructure.terraformAirnodeDestroy(execOptions, cloudProvider, deploymentId, bucket, bucketPath); expect(exec).toHaveBeenNthCalledWith( 1, `terraform init -backend-config="region=us-east-1" -backend-config="bucket=airnode-123456789" -backend-config="key=airnode-address/stage/timestamp/default.tfstate" -from-module=${terraformDir}/aws`, @@ -665,7 +652,7 @@ describe('terraformAirnodeDestroy', () => { ); expect(exec).toHaveBeenNthCalledWith( 2, - `terraform destroy -var="aws_region=europe-central-1" -var="airnode_address_short=a30ca71" -var="stage=dev" -var="configuration_file=NULL" -var="secrets_file=NULL" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=NULL" -input=false -no-color -auto-approve`, + `terraform destroy -var="aws_region=europe-central-1" -var="deployment_id=aws7195b548" -var="configuration_file=NULL" -var="secrets_file=NULL" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=NULL" -input=false -no-color -auto-approve`, execOptions ); }); @@ -734,7 +721,7 @@ describe('removeAirnode', () => { ); expect(exec).toHaveBeenNthCalledWith( 2, - `terraform destroy -var="aws_region=us-east-1" -var="airnode_address_short=d0624e6" -var="stage=dev" -var="configuration_file=NULL" -var="secrets_file=NULL" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=NULL" -input=false -no-color -auto-approve`, + `terraform destroy -var="aws_region=us-east-1" -var="deployment_id=${happyPathDeploymentId}" -var="configuration_file=NULL" -var="secrets_file=NULL" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=NULL" -input=false -no-color -auto-approve`, { cwd: 'tmpDir' } ); expect(awsGetBucketDirectoryStructureSpy).toHaveBeenNthCalledWith(2, bucket.name); @@ -765,7 +752,7 @@ describe('removeAirnode', () => { ); expect(exec).toHaveBeenNthCalledWith( 2, - `terraform destroy -var="aws_region=us-east-1" -var="airnode_address_short=a30ca71" -var="stage=dev" -var="configuration_file=NULL" -var="secrets_file=NULL" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=NULL" -input=false -no-color -auto-approve`, + `terraform destroy -var="aws_region=us-east-1" -var="deployment_id=${deploymentId}" -var="configuration_file=NULL" -var="secrets_file=NULL" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=NULL" -input=false -no-color -auto-approve`, { cwd: 'tmpDir' } ); expect(awsGetBucketDirectoryStructureSpy).toHaveBeenNthCalledWith(2, bucket.name); @@ -805,7 +792,7 @@ describe('removeAirnode', () => { ); expect(exec).toHaveBeenNthCalledWith( 2, - `terraform destroy -var="aws_region=us-east-1" -var="airnode_address_short=a30ca71" -var="stage=dev" -var="configuration_file=NULL" -var="secrets_file=NULL" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=NULL" -input=false -no-color -auto-approve`, + `terraform destroy -var="aws_region=us-east-1" -var="deployment_id=${deploymentId}" -var="configuration_file=NULL" -var="secrets_file=NULL" -var="handler_dir=${handlerDir}" -var="disable_concurrency_reservation=false" -var="airnode_wallet_private_key=NULL" -input=false -no-color -auto-approve`, { cwd: 'tmpDir' } ); expect(awsGetBucketDirectoryStructureSpy).toHaveBeenNthCalledWith(2, bucket.name); diff --git a/packages/airnode-deployer/src/infrastructure/index.ts b/packages/airnode-deployer/src/infrastructure/index.ts index 44de850429..d115ebeed2 100644 --- a/packages/airnode-deployer/src/infrastructure/index.ts +++ b/packages/airnode-deployer/src/infrastructure/index.ts @@ -37,7 +37,7 @@ import { Bucket, } from '../utils/infrastructure'; import { version as nodeVersion } from '../../package.json'; -import { deriveAirnodeAddress, shortenAirnodeAddress } from '../utils'; +import { deriveAirnodeAddress } from '../utils'; import { airnodeAddressReadable, cloudProviderReadable, timestampReadable } from '../utils/cli'; export const TF_STATE_FILENAME = 'default.tfstate'; @@ -188,8 +188,7 @@ export function prepareCloudProviderAirnodeApplyDestoryArguments( } export type AirnodeApplyDestroyVariables = { - airnodeAddressShort: string; - stage: string; + deploymentId: string; configPath?: string; secretsPath?: string; handlerDir: string; @@ -198,19 +197,11 @@ export type AirnodeApplyDestroyVariables = { }; export function prepareAirnodeApplyDestroyArguments(variables: AirnodeApplyDestroyVariables): CommandArg[] { - const { - airnodeAddressShort, - stage, - configPath, - secretsPath, - handlerDir, - disableConcurrencyReservations, - airnodeWalletPrivateKey, - } = variables; + const { deploymentId, configPath, secretsPath, handlerDir, disableConcurrencyReservations, airnodeWalletPrivateKey } = + variables; return [ - ['var', 'airnode_address_short', airnodeAddressShort], - ['var', 'stage', stage], + ['var', 'deployment_id', deploymentId], ['var', 'configuration_file', configPath ? path.resolve(configPath) : 'NULL'], ['var', 'secrets_file', secretsPath ? path.resolve(secretsPath) : 'NULL'], ['var', 'handler_dir', handlerDir], @@ -245,17 +236,22 @@ export async function terraformAirnodeApply( configPath: string, secretsPath: string ) { - const { airnodeWalletMnemonic, stage, httpGateway, httpSignedDataGateway } = config.nodeSettings; + const { + airnodeWalletMnemonic, + stage, + httpGateway, + httpSignedDataGateway, + nodeVersion: configNodeVersion, + } = config.nodeSettings; const cloudProvider = config.nodeSettings.cloudProvider as CloudProvider; - const airnodeAddressShort = shortenAirnodeAddress(deriveAirnodeAddress(airnodeWalletMnemonic)); + const airnodeAddress = deriveAirnodeAddress(airnodeWalletMnemonic); const airnodeWalletPrivateKey = evm.getAirnodeWallet(config).privateKey; const maxConcurrency = config.chains.reduce((concurrency: number, chain) => concurrency + chain.maxConcurrency, 0); await terraformAirnodeInit(execOptions, cloudProvider, bucket, bucketDeploymentPath); const commonArguments = prepareAirnodeApplyDestroyArguments({ - airnodeAddressShort, - stage, + deploymentId: deriveDeploymentId(cloudProvider, airnodeAddress, stage, configNodeVersion), configPath, secretsPath, handlerDir, @@ -395,16 +391,14 @@ export const deployAirnode = async (config: Config, configPath: string, secretsP export async function terraformAirnodeDestroy( execOptions: child.ExecOptions, cloudProvider: CloudProvider, - airnodeAddressShort: string, - stage: string, + deploymentId: string, bucket: Bucket, bucketDeploymentPath: string ) { await terraformAirnodeInit(execOptions, cloudProvider, bucket, bucketDeploymentPath); const commonArguments = prepareAirnodeApplyDestroyArguments({ - airnodeAddressShort, - stage, + deploymentId, handlerDir, disableConcurrencyReservations: cloudProvider.disableConcurrencyReservations, }); @@ -560,17 +554,9 @@ export async function removeAirnode(deploymentId: string) { } logger.debug('Removing Airnode via Terraform recipes'); - const airnodeAddressShort = shortenAirnodeAddress(airnodeAddress); const airnodeTmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'airnode')); const execOptions = { cwd: airnodeTmpDir }; - await terraformAirnodeDestroy( - execOptions, - cloudProvider, - airnodeAddressShort, - stage, - bucket, - bucketLatestDeploymentPath - ); + await terraformAirnodeDestroy(execOptions, cloudProvider, deploymentId, bucket, bucketLatestDeploymentPath); fs.rmSync(airnodeTmpDir, { recursive: true }); // Refreshing the bucket content because the source code archives were removed by Terraform diff --git a/packages/airnode-deployer/src/utils/evm.ts b/packages/airnode-deployer/src/utils/evm.ts index 9cfba7b82f..0993280e5e 100644 --- a/packages/airnode-deployer/src/utils/evm.ts +++ b/packages/airnode-deployer/src/utils/evm.ts @@ -1,6 +1,4 @@ import * as ethers from 'ethers'; -import { goSync } from '@api3/promise-utils'; -import { logAndReturnError } from './infrastructure'; import * as logger from '../utils/logger'; export function deriveAirnodeAddress(mnemonic: string) { @@ -14,13 +12,3 @@ export function deriveAirnodeXpub(mnemonic: string) { const airnodeHdNode = ethers.utils.HDNode.fromMnemonic(mnemonic).derivePath("m/44'/60'/0'"); return airnodeHdNode.neuter().extendedKey; } - -export function shortenAirnodeAddress(airnodeAddress: string) { - logger.debug('Shortening Airnode Address'); - - const goGetAddress = goSync(() => ethers.utils.getAddress(airnodeAddress)); - if (!goGetAddress.success) throw logAndReturnError('"airnodeAddress" is not a valid hex string'); - - // NOTE: AWS doesn't allow uppercase letters in S3 bucket and lambda function names - return airnodeAddress.substring(2, 9).toLowerCase(); -} diff --git a/packages/airnode-deployer/terraform/aws/variables.tf b/packages/airnode-deployer/terraform/aws/variables.tf index cb877db775..d9a9b863de 100644 --- a/packages/airnode-deployer/terraform/aws/variables.tf +++ b/packages/airnode-deployer/terraform/aws/variables.tf @@ -1,12 +1,11 @@ locals { # Be aware when using `name-prefix` for naming resources - # as it can be up to 32 characters long: + # as it can be up to 19 characters long: # # infrastructure_name - "airnode" - 7 characters - # airnode_address_short - 7 characters - # stage - up to 16 characters - # dashes between - 2 characters - name_prefix = "${var.infrastructure_name}-${var.airnode_address_short}-${var.stage}" + # deployment_id - 11 characters + # dash between - 1 character + name_prefix = "${var.infrastructure_name}-${var.deployment_id}" } variable "aws_region" { @@ -14,18 +13,13 @@ variable "aws_region" { default = "us-east-1" } -variable "stage" { - description = "Infrastructure environment" - default = "testing" -} - variable "infrastructure_name" { description = "Infrastructure name" default = "airnode" } -variable "airnode_address_short" { - description = "Airnode address (short)" +variable "deployment_id" { + description = "ID of the deployment" } variable "configuration_file" { diff --git a/packages/airnode-deployer/terraform/gcp/variables.tf b/packages/airnode-deployer/terraform/gcp/variables.tf index 1a1a1bc1ae..48a5e7e068 100644 --- a/packages/airnode-deployer/terraform/gcp/variables.tf +++ b/packages/airnode-deployer/terraform/gcp/variables.tf @@ -1,12 +1,11 @@ locals { # Be aware when using `name-prefix` for naming resources - # as it can be up to 32 characters long: + # as it can be up to 19 characters long: # # infrastructure_name - "airnode" - 7 characters - # airnode_address_short - 7 characters - # stage - up to 16 characters - # dashes between - 2 characters - name_prefix = "${var.infrastructure_name}-${var.airnode_address_short}-${var.stage}" + # deployment_id - 11 characters + # dash between - 1 character + name_prefix = "${var.infrastructure_name}-${var.deployment_id}" } variable "gcp_project" { @@ -18,18 +17,13 @@ variable "gcp_region" { default = "us-east1" } -variable "stage" { - description = "Infrastructure environment" - default = "testing" -} - variable "infrastructure_name" { description = "Infrastructure name" default = "airnode" } -variable "airnode_address_short" { - description = "Airnode address (short)" +variable "deployment_id" { + description = "ID of the deployment" } variable "configuration_file" { diff --git a/packages/airnode-node/src/adapters/http/worker.test.ts b/packages/airnode-node/src/adapters/http/worker.test.ts index a421d3a228..1139423264 100644 --- a/packages/airnode-node/src/adapters/http/worker.test.ts +++ b/packages/airnode-node/src/adapters/http/worker.test.ts @@ -31,7 +31,7 @@ describe('spawnNewApiCall', () => { expect(invokeMock).toHaveBeenCalledTimes(1); expect(invokeMock).toHaveBeenCalledWith( { - FunctionName: 'airnode-19255a4-test-run', + FunctionName: 'airnode-local02cce763-run', Payload: JSON.stringify({ aggregatedApiCall, logOptions, functionName: 'callApi' }), }, expect.anything() @@ -52,7 +52,7 @@ describe('spawnNewApiCall', () => { expect(invokeMock).toHaveBeenCalledTimes(1); expect(invokeMock).toHaveBeenCalledWith( { - FunctionName: 'airnode-19255a4-test-run', + FunctionName: 'airnode-local02cce763-run', Payload: JSON.stringify({ aggregatedApiCall, logOptions, functionName: 'callApi' }), }, expect.anything() @@ -74,7 +74,7 @@ describe('spawnNewApiCall', () => { expect(invokeMock).toHaveBeenCalledTimes(1); expect(invokeMock).toHaveBeenCalledWith( { - FunctionName: 'airnode-19255a4-test-run', + FunctionName: 'airnode-local02cce763-run', Payload: JSON.stringify({ aggregatedApiCall, logOptions, functionName: 'callApi' }), }, expect.anything() @@ -95,7 +95,7 @@ describe('spawnNewApiCall', () => { expect(invokeMock).toHaveBeenCalledTimes(1); expect(invokeMock).toHaveBeenCalledWith( { - FunctionName: 'airnode-19255a4-test-run', + FunctionName: 'airnode-local02cce763-run', Payload: JSON.stringify({ aggregatedApiCall, logOptions, functionName: 'callApi' }), }, expect.anything() diff --git a/packages/airnode-node/src/coordinator/state.ts b/packages/airnode-node/src/coordinator/state.ts index 407b459a3d..2ed344849a 100644 --- a/packages/airnode-node/src/coordinator/state.ts +++ b/packages/airnode-node/src/coordinator/state.ts @@ -1,15 +1,17 @@ import * as wallet from '../evm/wallet'; import { CoordinatorSettings, CoordinatorState } from '../types'; +import { deriveDeploymentId } from '../workers'; import { Config } from '../config'; import { CoordinatorStateWithApiResponses, RegularAggregatedApiCallsWithResponseById } from '..'; export function create(config: Config, coordinatorId: string): CoordinatorState { const airnodeAddress = wallet.getAirnodeWalletFromPrivateKey().address; - const airnodeAddressShort = wallet.getAirnodeAddressShort(airnodeAddress); + const { cloudProvider, stage, nodeVersion } = config.nodeSettings; + const deploymentId = deriveDeploymentId(cloudProvider, airnodeAddress, stage, nodeVersion); const settings: CoordinatorSettings = { airnodeAddress, - airnodeAddressShort, + deploymentId, logFormat: config.nodeSettings.logFormat, logLevel: config.nodeSettings.logLevel, stage: config.nodeSettings.stage, diff --git a/packages/airnode-node/src/evm/wallet.test.ts b/packages/airnode-node/src/evm/wallet.test.ts index 34dd544118..886c7769df 100644 --- a/packages/airnode-node/src/evm/wallet.test.ts +++ b/packages/airnode-node/src/evm/wallet.test.ts @@ -21,14 +21,6 @@ describe('getAirnodeWallet', () => { }); }); -describe('getAirnodeAddressShort', () => { - it('returns a shortened airnodeAddress', () => { - const airnodeWallet = wallet.getAirnodeWallet(config); - const res = wallet.getAirnodeAddressShort(airnodeWallet.address); - expect(res).toEqual('a30ca71'); - }); -}); - describe('deriveSponsorWallet', () => { it('returns the wallet for the given sponsor', () => { const masterHDNode = wallet.getMasterHDNode(config); diff --git a/packages/airnode-node/src/evm/wallet.ts b/packages/airnode-node/src/evm/wallet.ts index c405e5b383..59ea3084e2 100644 --- a/packages/airnode-node/src/evm/wallet.ts +++ b/packages/airnode-node/src/evm/wallet.ts @@ -47,11 +47,6 @@ export function getExtendedPublicKey(masterHDNode: ethers.utils.HDNode): string return masterHDNode.derivePath("m/44'/60'/0'").neuter().extendedKey; } -export function getAirnodeAddressShort(airnodeAddress: string): string { - // NOTE: AWS doesn't allow uppercase letters in S3 bucket and lambda function names - return airnodeAddress.substring(2, 9).toLowerCase(); -} - export function deriveSponsorWallet( masterHDNode: ethers.utils.HDNode, sponsorAddress: string, diff --git a/packages/airnode-node/src/evm/workers.test.ts b/packages/airnode-node/src/evm/workers.test.ts index 11969425b3..7027648bd0 100644 --- a/packages/airnode-node/src/evm/workers.test.ts +++ b/packages/airnode-node/src/evm/workers.test.ts @@ -11,7 +11,7 @@ import * as fixtures from '../../test/fixtures'; const workers = ['spawnNewProvider', 'spawnProviderRequestProcessor'] as ReadonlyArray; -const serverlessFunctionName = 'airnode-19255a4-test-run'; +const serverlessFunctionName = 'airnode-local02cce763-run'; const functionNames = { spawnNewProvider: 'initializeProvider', spawnProviderRequestProcessor: 'processTransactions', @@ -27,7 +27,7 @@ workers.forEach((workerType) => { it('handles remote AWS calls', async () => { const state = fixtures.buildEVMProviderSponsorState(); - invokeMock.mockImplementationOnce((params, callback) => + invokeMock.mockImplementationOnce((_params, callback) => callback(null, { Payload: JSON.stringify({ body: JSON.stringify({ ok: true, data: state }) }) }) ); const workerOpts = fixtures.buildWorkerOptions({ @@ -48,7 +48,7 @@ workers.forEach((workerType) => { it('returns an error if the worker rejects', async () => { const state = fixtures.buildEVMProviderSponsorState(); - invokeMock.mockImplementationOnce((params, callback) => callback(new Error('Something went wrong'), null)); + invokeMock.mockImplementationOnce((_params, callback) => callback(new Error('Something went wrong'), null)); const workerOpts = fixtures.buildWorkerOptions({ cloudProvider: { type: 'aws', region: 'us-east-1', disableConcurrencyReservations: false }, }); @@ -74,7 +74,7 @@ workers.forEach((workerType) => { it('returns an error if the response has an error log', async () => { const state = fixtures.buildEVMProviderSponsorState(); const errorLog = logger.pend('ERROR', 'Something went wrong'); - invokeMock.mockImplementationOnce((params, callback) => + invokeMock.mockImplementationOnce((_params, callback) => callback(null, { Payload: JSON.stringify({ body: JSON.stringify({ ok: false, errorLog }) }) }) ); const workerOpts = fixtures.buildWorkerOptions({ @@ -95,7 +95,7 @@ workers.forEach((workerType) => { it('returns an error if the response is not ok', async () => { const state = fixtures.buildEVMProviderSponsorState(); - invokeMock.mockImplementationOnce((params, callback) => + invokeMock.mockImplementationOnce((_params, callback) => callback(null, { Payload: JSON.stringify({ body: JSON.stringify({ ok: false }) }) }) ); const workerOpts = fixtures.buildWorkerOptions({ diff --git a/packages/airnode-node/src/handlers/start-coordinator.test.ts b/packages/airnode-node/src/handlers/start-coordinator.test.ts index 9cd77876e0..adfba1c179 100644 --- a/packages/airnode-node/src/handlers/start-coordinator.test.ts +++ b/packages/airnode-node/src/handlers/start-coordinator.test.ts @@ -170,7 +170,7 @@ describe('startCoordinator', () => { await startCoordinator(config, coordinatorId); // cached requests should not trigger an API call - expect(callApisSpy).toHaveBeenCalledWith([], expect.objectContaining({ stage: 'test' })); + expect(callApisSpy).toHaveBeenCalledWith([], expect.objectContaining({ deploymentId: 'locale78fb65c' })); expect(blockWithTransactionsSpy).toHaveBeenCalled(); // API call was submitted diff --git a/packages/airnode-node/src/handlers/start-coordinator.ts b/packages/airnode-node/src/handlers/start-coordinator.ts index 2012d7bc12..fa77461973 100644 --- a/packages/airnode-node/src/handlers/start-coordinator.ts +++ b/packages/airnode-node/src/handlers/start-coordinator.ts @@ -45,8 +45,7 @@ function getWorkerOptions(state: CoordinatorState): WorkerOptions { return { cloudProvider: config.nodeSettings.cloudProvider, - airnodeAddressShort: settings.airnodeAddressShort, - stage: config.nodeSettings.stage, + deploymentId: settings.deploymentId, }; } diff --git a/packages/airnode-node/src/providers/actions.test.ts b/packages/airnode-node/src/providers/actions.test.ts index aa51ce61aa..d656e5a2ca 100644 --- a/packages/airnode-node/src/providers/actions.test.ts +++ b/packages/airnode-node/src/providers/actions.test.ts @@ -124,7 +124,7 @@ describe('initialize', () => { }, settings: { airnodeAddress: '0xA30CA71Ba54E83127214D3271aEA8F5D6bD4Dace', - airnodeAddressShort: 'a30ca71', + deploymentId: 'locale78fb65c', authorizers: { requesterEndpointAuthorizers: [ethers.constants.AddressZero], crossChainRequesterAuthorizers: [], @@ -178,7 +178,7 @@ describe('initialize', () => { }, settings: { airnodeAddress: '0xA30CA71Ba54E83127214D3271aEA8F5D6bD4Dace', - airnodeAddressShort: 'a30ca71', + deploymentId: 'locale78fb65c', authorizers: { requesterEndpointAuthorizers: [ethers.constants.AddressZero], crossChainRequesterAuthorizers: [], diff --git a/packages/airnode-node/src/providers/state.test.ts b/packages/airnode-node/src/providers/state.test.ts index 0e9408ceba..ed0fd8f971 100644 --- a/packages/airnode-node/src/providers/state.test.ts +++ b/packages/airnode-node/src/providers/state.test.ts @@ -53,7 +53,7 @@ describe('create', () => { }, settings: { airnodeAddress: '0xA30CA71Ba54E83127214D3271aEA8F5D6bD4Dace', - airnodeAddressShort: 'a30ca71', + deploymentId: 'locale78fb65c', authorizers: { requesterEndpointAuthorizers: [ethers.constants.AddressZero], crossChainRequesterAuthorizers: [], @@ -148,7 +148,7 @@ describe('create', () => { }, settings: { airnodeAddress: '0xA30CA71Ba54E83127214D3271aEA8F5D6bD4Dace', - airnodeAddressShort: 'a30ca71', + deploymentId: 'locale78fb65c', authorizers: { requesterEndpointAuthorizers: [ethers.constants.AddressZero], crossChainRequesterAuthorizers: [], diff --git a/packages/airnode-node/src/providers/state.ts b/packages/airnode-node/src/providers/state.ts index a06a39ca9b..a164c75b7e 100644 --- a/packages/airnode-node/src/providers/state.ts +++ b/packages/airnode-node/src/providers/state.ts @@ -6,6 +6,7 @@ import { EVMProviderState, ProviderSettings, ProviderState, ProviderStates, EVMP import { BLOCK_COUNT_HISTORY_LIMIT, BLOCK_MIN_CONFIRMATIONS } from '../constants'; import { groupRequestsBySponsorAddress } from '../requests/grouping'; import { ChainConfig, ChainType, Config } from '../config'; +import { deriveDeploymentId } from '../workers'; export function buildEVMState( coordinatorId: string, @@ -17,10 +18,11 @@ export function buildEVMState( const masterHDNode = evm.getMasterHDNode(config); const chainProviderUrl = chain.providers[chainProviderName].url; const provider = evm.buildEVMProvider(chainProviderUrl, chain.id); + const { cloudProvider, stage, nodeVersion } = config.nodeSettings; const providerSettings: ProviderSettings = { airnodeAddress, - airnodeAddressShort: evm.getAirnodeAddressShort(airnodeAddress), + deploymentId: deriveDeploymentId(cloudProvider, airnodeAddress, stage, nodeVersion), authorizers: chain.authorizers, authorizations: chain.authorizations, // The number of blocks to look back for events to process diff --git a/packages/airnode-node/src/providers/worker.test.ts b/packages/airnode-node/src/providers/worker.test.ts index d0fdab061b..9bbe316da2 100644 --- a/packages/airnode-node/src/providers/worker.test.ts +++ b/packages/airnode-node/src/providers/worker.test.ts @@ -26,8 +26,7 @@ describe('spawnNewProvider', () => { disableConcurrencyReservations: false, }, payload: { state, functionName: 'initializeProvider' }, - airnodeAddressShort: '19255a4', - stage: 'test', + deploymentId: 'local02cce763', }); }); }); @@ -52,8 +51,7 @@ describe('spawnTransactionsProcessor', () => { disableConcurrencyReservations: false, }, payload: { state, functionName: 'processTransactions' }, - airnodeAddressShort: '19255a4', - stage: 'test', + deploymentId: 'local02cce763', }); }); }); diff --git a/packages/airnode-node/src/types.ts b/packages/airnode-node/src/types.ts index 30affde38e..8cafb116e3 100644 --- a/packages/airnode-node/src/types.ts +++ b/packages/airnode-node/src/types.ts @@ -164,7 +164,7 @@ export interface RegularAggregatedApiCallsWithResponseById { export interface CoordinatorSettings { readonly airnodeAddress: string; - readonly airnodeAddressShort: string; + readonly deploymentId: string; readonly logFormat: LogFormat; readonly logLevel: LogLevel; readonly stage: string; @@ -319,8 +319,7 @@ export type ApiCallPayload = RegularApiCallPayload | HttpApiCallPayload | HttpSi // =========================================== export interface WorkerOptions { readonly cloudProvider: LocalOrCloudProvider; - readonly airnodeAddressShort: string; - readonly stage: string; + readonly deploymentId: string; } export interface InitializeProviderPayload { diff --git a/packages/airnode-node/src/workers/cloud-platforms/aws.test.ts b/packages/airnode-node/src/workers/cloud-platforms/aws.test.ts index 6030eacf6c..caddcad7cf 100644 --- a/packages/airnode-node/src/workers/cloud-platforms/aws.test.ts +++ b/packages/airnode-node/src/workers/cloud-platforms/aws.test.ts @@ -16,7 +16,7 @@ describe('spawn', () => { it('derives the function name, invokes and returns the response', async () => { const lambda = new AWS.Lambda(); const invoke = lambda.invoke as jest.Mock; - invoke.mockImplementationOnce((params, callback) => + invoke.mockImplementationOnce((_params, callback) => callback(null, { Payload: JSON.stringify({ body: JSON.stringify({ value: 7777 }) }) }) ); const state = fixtures.buildEVMProviderState(); @@ -33,7 +33,7 @@ describe('spawn', () => { expect(invoke).toHaveBeenCalledTimes(1); expect(invoke).toHaveBeenCalledWith( { - FunctionName: 'airnode-19255a4-test-run', + FunctionName: 'airnode-local02cce763-run', Payload: JSON.stringify({ functionName: 'initializeProvider', state, logOptions }), }, expect.any(Function) @@ -43,7 +43,7 @@ describe('spawn', () => { it('throws an error if the lambda returns an error', async () => { const lambda = new AWS.Lambda(); const invoke = lambda.invoke as jest.Mock; - invoke.mockImplementationOnce((params, callback) => callback(new Error('Something went wrong'), null)); + invoke.mockImplementationOnce((_params, callback) => callback(new Error('Something went wrong'), null)); const state = fixtures.buildEVMProviderState(); const workerOpts = fixtures.buildWorkerOptions({ cloudProvider: { type: 'aws', region: 'us-east-1', disableConcurrencyReservations: false }, @@ -57,7 +57,7 @@ describe('spawn', () => { expect(invoke).toHaveBeenCalledTimes(1); expect(invoke).toHaveBeenCalledWith( { - FunctionName: 'airnode-19255a4-test-run', + FunctionName: 'airnode-local02cce763-run', Payload: JSON.stringify({ functionName: 'initializeProvider', state, logOptions }), }, expect.any(Function) diff --git a/packages/airnode-node/src/workers/cloud-platforms/aws.ts b/packages/airnode-node/src/workers/cloud-platforms/aws.ts index 2fab9c6590..b48da70740 100644 --- a/packages/airnode-node/src/workers/cloud-platforms/aws.ts +++ b/packages/airnode-node/src/workers/cloud-platforms/aws.ts @@ -8,7 +8,7 @@ export function spawn(params: WorkerParameters): Promise { const lambda = new AWS.Lambda(); // AWS doesn't allow uppercase letters in lambda function names - const resolvedName = `airnode-${params.airnodeAddressShort}-${params.stage}-run`; + const resolvedName = `airnode-${params.deploymentId}-run`; const options = { FunctionName: resolvedName, diff --git a/packages/airnode-node/src/workers/cloud-platforms/gcp.test.ts b/packages/airnode-node/src/workers/cloud-platforms/gcp.test.ts index 6f67d88830..b027103329 100644 --- a/packages/airnode-node/src/workers/cloud-platforms/gcp.test.ts +++ b/packages/airnode-node/src/workers/cloud-platforms/gcp.test.ts @@ -31,7 +31,7 @@ describe('spawn', () => { ...workerOpts, payload: { functionName: 'initializeProvider', state, logOptions }, }; - const url = 'https://us-east1-projectId123.cloudfunctions.net/airnode-19255a4-test-run'; + const url = 'https://us-east1-projectId123.cloudfunctions.net/airnode-local02cce763-run'; const res = await gcp.spawn(parameters); expect(res).toEqual({ value: 7777 }); @@ -63,7 +63,7 @@ describe('spawn', () => { ...workerOpts, payload: { functionName: 'initializeProvider', state, logOptions }, }; - const url = 'https://us-east1-projectId123.cloudfunctions.net/airnode-19255a4-test-run'; + const url = 'https://us-east1-projectId123.cloudfunctions.net/airnode-local02cce763-run'; await expect(gcp.spawn(parameters)).rejects.toThrow(new Error('Something went wrong')); diff --git a/packages/airnode-node/src/workers/cloud-platforms/gcp.ts b/packages/airnode-node/src/workers/cloud-platforms/gcp.ts index 96dee094c9..40e5803e44 100644 --- a/packages/airnode-node/src/workers/cloud-platforms/gcp.ts +++ b/packages/airnode-node/src/workers/cloud-platforms/gcp.ts @@ -5,7 +5,7 @@ import { WorkerParameters, WorkerResponse } from '../../types'; export async function spawn(params: WorkerParameters): Promise { const auth = new GoogleAuth(); - const resolvedName = `airnode-${params.airnodeAddressShort}-${params.stage}-run`; + const resolvedName = `airnode-${params.deploymentId}-run`; const cloudProvider = params.cloudProvider as GcpCloudProvider; const url = `https://${cloudProvider.region}-${cloudProvider.projectId}.cloudfunctions.net/${resolvedName}`; diff --git a/packages/airnode-node/src/workers/index.ts b/packages/airnode-node/src/workers/index.ts index 3993bebb2f..c88d1a4ddf 100644 --- a/packages/airnode-node/src/workers/index.ts +++ b/packages/airnode-node/src/workers/index.ts @@ -3,7 +3,7 @@ import * as aws from './cloud-platforms/aws'; import * as gcp from './cloud-platforms/gcp'; import * as localHandlers from './local-handlers'; import { WorkerParameters, WorkerResponse } from '../types'; -import { CloudProvider } from '../config'; +import { LocalOrCloudProvider } from '../config'; export function spawn(params: WorkerParameters): Promise { switch (params.cloudProvider.type) { @@ -25,8 +25,11 @@ export function spawn(params: WorkerParameters): Promise { } } -function cloudProviderHashElements(cloudProvider: CloudProvider) { - const hashElements = [cloudProvider.type, cloudProvider.region]; +function cloudProviderHashElements(cloudProvider: LocalOrCloudProvider) { + const hashElements: string[] = [cloudProvider.type]; + if (cloudProvider.type !== 'local') { + hashElements.push(cloudProvider.region); + } if (cloudProvider.type === 'gcp') { hashElements.push(cloudProvider.projectId); } @@ -35,7 +38,7 @@ function cloudProviderHashElements(cloudProvider: CloudProvider) { } export function deriveDeploymentId( - cloudProvider: CloudProvider, + cloudProvider: LocalOrCloudProvider, airnodeAddress: string, stage: string, airnodeVersion: string @@ -48,7 +51,7 @@ export function deriveDeploymentId( } export function deriveDeploymentVersionId( - cloudProvider: CloudProvider, + cloudProvider: LocalOrCloudProvider, airnodeAddress: string, stage: string, airnodeVersion: string, diff --git a/packages/airnode-node/test/fixtures/config/worker-options.ts b/packages/airnode-node/test/fixtures/config/worker-options.ts index dbad504d15..7200abdbd6 100644 --- a/packages/airnode-node/test/fixtures/config/worker-options.ts +++ b/packages/airnode-node/test/fixtures/config/worker-options.ts @@ -6,8 +6,7 @@ export function buildWorkerOptions(options?: Partial): WorkerOpti type: 'local', gatewayServerPort: 3000, }, - airnodeAddressShort: '19255a4', - stage: 'test', + deploymentId: 'local02cce763', ...options, }; }