(config: Config, configPath: string, secretsPath: string, timestamp: number)
| 331 | } |
| 332 | |
| 333 | export const deployAirnode = async (config: Config, configPath: string, secretsPath: string, timestamp: number) => { |
| 334 | const { airnodeWalletMnemonic, cloudProvider, stage } = config.nodeSettings; |
| 335 | const airnodeAddress = deriveAirnodeAddress(airnodeWalletMnemonic); |
| 336 | const { type, region } = cloudProvider as CloudProvider; |
| 337 | |
| 338 | const spinner = logger.getSpinner(); |
| 339 | spinner.start(`Deploying Airnode ${airnodeAddress} ${stage} to ${type} ${region}`); |
| 340 | if (logger.inDebugMode()) { |
| 341 | spinner.info(); |
| 342 | } |
| 343 | |
| 344 | const goDeploy = await go(async () => { |
| 345 | logger.debug('Fetching Airnode bucket'); |
| 346 | let bucket = await cloudProviderLib[type].getAirnodeBucket(); |
| 347 | if (!bucket) { |
| 348 | logger.debug('No Airnode bucket found, creating'); |
| 349 | bucket = await cloudProviderLib[type].createAirnodeBucket(cloudProvider as any); |
| 350 | } |
| 351 | logger.debug(`Using Airnode bucket '${bucket.name}'`); |
| 352 | |
| 353 | logger.debug('Fetching Airnode bucket content'); |
| 354 | const directoryStructure = await cloudProviderLib[type].getBucketDirectoryStructure(bucket); |
| 355 | |
| 356 | const bucketStagePath = `${airnodeAddress}/${stage}`; |
| 357 | const bucketDeploymentPath = `${bucketStagePath}/${timestamp}`; |
| 358 | |
| 359 | const stageDirectory = getStageDirectory(directoryStructure, airnodeAddress, stage); |
| 360 | if (stageDirectory) { |
| 361 | logger.debug(`Deployment '${bucketStagePath}' already exists`); |
| 362 | |
| 363 | const bucketMissingFiles = getMissingBucketFiles(directoryStructure); |
| 364 | if ( |
| 365 | bucketMissingFiles[airnodeAddress] && |
| 366 | bucketMissingFiles[airnodeAddress][stage] && |
| 367 | bucketMissingFiles[airnodeAddress][stage].length !== 0 |
| 368 | ) { |
| 369 | throw new Error( |
| 370 | `Can't update an Airnode with missing files: ${bucketMissingFiles[airnodeAddress][stage].join( |
| 371 | ', ' |
| 372 | )}. Deployer commands may fail and manual removal may be necessary.` |
| 373 | ); |
| 374 | } |
| 375 | |
| 376 | const latestDeployment = Object.keys(stageDirectory.children).sort().reverse()[0]; |
| 377 | const bucketConfigPath = `${bucketStagePath}/${latestDeployment}/config.json`; |
| 378 | logger.debug(`Fetching configuration file '${bucketConfigPath}'`); |
| 379 | const goGetRemoteConfigFileFromBucket = await go(() => |
| 380 | cloudProviderLib[type].getFileFromBucket(bucket!, bucketConfigPath) |
| 381 | ); |
| 382 | if (!goGetRemoteConfigFileFromBucket.success) { |
| 383 | throw new Error(`Failed to fetch configuration file. Error: ${goGetRemoteConfigFileFromBucket.error.message}`); |
| 384 | } |
| 385 | const goRemoteConfig = goSync(() => JSON.parse(goGetRemoteConfigFileFromBucket.data)); |
| 386 | if (!goRemoteConfig.success) { |
| 387 | throw new Error(`Failed to parse configuration file. Error: ${goRemoteConfig.error.message}`); |
| 388 | } |
| 389 | |
| 390 | const remoteNodeSettings = goRemoteConfig.data.nodeSettings; |
no test coverage detected