Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix broken temp-v0.10.0 branch merge #1522

Merged
merged 6 commits into from
Nov 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/late-poets-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@api3/airnode-deployer': minor
'@api3/airnode-node': minor
'@api3/airnode-utilities': minor
---

Refactor constants to avoid unexpected side effects
7 changes: 7 additions & 0 deletions .changeset/old-dancers-sin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@api3/airnode-deployer': minor
'@api3/airnode-node': minor
'@api3/airnode-validator': minor
---

Remove heartbeatId from config and heartbeat payload
7 changes: 7 additions & 0 deletions .changeset/twenty-berries-argue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@api3/airnode-deployer': patch
'@api3/airnode-node': patch
'@api3/airnode-validator': patch
---

Add cloud_provider, stage, region to heartbeat payload
7 changes: 7 additions & 0 deletions .changeset/two-berries-beam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@api3/airnode-deployer': minor
'@api3/airnode-node': minor
'@api3/airnode-validator': minor
---

Add heartbeat payload signing
3 changes: 0 additions & 3 deletions packages/airnode-deployer/config/config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,15 @@
"heartbeat": {
"enabled": true,
"apiKey": "${HEARTBEAT_API_KEY}",
"id": "${HEARTBEAT_ID}",
"url": "${HEARTBEAT_URL}"
},
"httpGateway": {
"enabled": true,
"apiKey": "${HTTP_GATEWAY_API_KEY}",
"maxConcurrency": 20,
"corsOrigins": []
},
"httpSignedDataGateway": {
"enabled": true,
"apiKey": "${HTTP_SIGNED_DATA_GATEWAY_API_KEY}",
"maxConcurrency": 20,
"corsOrigins": []
},
Expand Down
4 changes: 0 additions & 4 deletions packages/airnode-deployer/config/secrets.example.env
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,4 @@ AIRNODE_WALLET_MNEMONIC=achieve climb couple wait accident symbol spy blouse red
PROVIDER_URL=http://127.0.0.1:8545

HEARTBEAT_API_KEY=00000000-0000-0000-0000-000000000000
HEARTBEAT_ID=caccf290-e683-11ec-8fea-0242ac120002
HEARTBEAT_URL=https://example.com/convert

HTTP_GATEWAY_API_KEY=12345678-0000-0000-0000-000000000000
HTTP_SIGNED_DATA_GATEWAY_API_KEY=87654321-0000-0000-0000-000000000000
17 changes: 3 additions & 14 deletions packages/airnode-deployer/src/handlers/aws/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import * as path from 'path';
import {
logger,
DEFAULT_RETRY_DELAY_MS,
randomHexString,
setLogOptions,
addMetadata,
caching,
} from '@api3/airnode-utilities';
import { logger, randomHexString, setLogOptions, addMetadata, caching } from '@api3/airnode-utilities';
import { go } from '@api3/promise-utils';
import {
handlers,
Expand Down Expand Up @@ -62,9 +55,7 @@ async function initializeProvider(payload: InitializeProviderPayload) {
addMetadata({ 'Chain-ID': chainId, Provider: providerName });
const stateWithConfig = { ...state, config: parsedConfig };

const goInitializedState = await go(() => handlers.initializeProvider(stateWithConfig), {
delay: { type: 'static', delayMs: DEFAULT_RETRY_DELAY_MS },
});
const goInitializedState = await go(() => handlers.initializeProvider(stateWithConfig));
if (!goInitializedState.success) {
const msg = `Failed to initialize provider: ${stateWithConfig.settings.name}`;
logger.error(goInitializedState.error.toString());
Expand Down Expand Up @@ -101,9 +92,7 @@ async function processTransactions(payload: ProcessTransactionsPayload) {
const stateWithConfig = { ...state, config: parsedConfig };
addMetadata({ 'Chain-ID': chainId, Provider: providerName, 'Sponsor-Address': state.sponsorAddress });

const goUpdatedState = await go(() => handlers.processTransactions(stateWithConfig), {
delay: { type: 'static', delayMs: DEFAULT_RETRY_DELAY_MS },
});
const goUpdatedState = await go(() => handlers.processTransactions(stateWithConfig));
if (!goUpdatedState.success) {
const msg = `Failed to process provider requests: ${stateWithConfig.settings.name}`;
logger.error(goUpdatedState.error.toString());
Expand Down
47 changes: 3 additions & 44 deletions packages/airnode-deployer/src/handlers/gcp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Request, Response } from '@google-cloud/functions-framework/build/src/f
import {
handlers,
providers,
config,
InitializeProviderPayload,
CallApiPayload,
ProcessTransactionsPayload,
Expand All @@ -12,17 +11,9 @@ import {
EnabledGateway,
verifyHttpSignedDataRequest,
verifyHttpRequest,
VerificationResult,
verifyRequestOrigin,
} from '@api3/airnode-node';
import {
logger,
DEFAULT_RETRY_DELAY_MS,
randomHexString,
setLogOptions,
addMetadata,
caching,
} from '@api3/airnode-utilities';
import { logger, randomHexString, setLogOptions, addMetadata, caching } from '@api3/airnode-utilities';
import { go } from '@api3/promise-utils';
import { z } from 'zod';

Expand Down Expand Up @@ -66,9 +57,7 @@ async function initializeProvider(payload: InitializeProviderPayload, res: Respo
addMetadata({ 'Chain-ID': chainId, Provider: providerName });
const stateWithConfig = { ...state, config: parsedConfig };

const goInitializedState = await go(() => handlers.initializeProvider(stateWithConfig), {
delay: { type: 'static', delayMs: DEFAULT_RETRY_DELAY_MS },
});
const goInitializedState = await go(() => handlers.initializeProvider(stateWithConfig));
if (!goInitializedState.success) {
const msg = `Failed to initialize provider: ${stateWithConfig.settings.name}`;
logger.log(goInitializedState.error.toString());
Expand Down Expand Up @@ -107,9 +96,7 @@ async function processTransactions(payload: ProcessTransactionsPayload, res: Res
const stateWithConfig = { ...state, config: parsedConfig };
addMetadata({ 'Chain-ID': chainId, Provider: providerName, 'Sponsor-Address': state.sponsorAddress });

const goUpdatedState = await go(() => handlers.processTransactions(stateWithConfig), {
delay: { type: 'static', delayMs: DEFAULT_RETRY_DELAY_MS },
});
const goUpdatedState = await go(() => handlers.processTransactions(stateWithConfig));
if (!goUpdatedState.success) {
const msg = `Failed to process provider requests: ${stateWithConfig.settings.name}`;
const errorLog = logger.pend('ERROR', msg, goUpdatedState.error);
Expand All @@ -122,20 +109,6 @@ async function processTransactions(payload: ProcessTransactionsPayload, res: Res
res.status(200).send(body);
}

// We need to check for an API key manually because GCP HTTP Gateway doesn't support managing API keys via API
function verifyGcpApiKey(
req: Request,
apiKeyName: 'HTTP_GATEWAY_API_KEY' | 'HTTP_SIGNED_DATA_GATEWAY_API_KEY'
): VerificationResult<{}> {
const apiKey = req.header('x-api-key');
if (!apiKey || apiKey !== config.getEnvValue(apiKeyName)) {
// Mimics the behavior of AWS HTTP Gateway
return { success: false, statusCode: 403, error: { message: 'Forbidden' } };
}

return { success: true };
}

// We do not want to enable ".strict()" - we want to allow extra fields in the request body
const httpRequestBodySchema = z.object({
parameters: z.any(), // Parameter validation is performed later
Expand Down Expand Up @@ -165,13 +138,6 @@ export async function processHttpRequest(req: Request, res: Response) {
// Set headers for the responses
res.set(originVerification.headers);

const apiKeyVerification = verifyGcpApiKey(req, 'HTTP_GATEWAY_API_KEY');
if (!apiKeyVerification.success) {
const { statusCode, error } = apiKeyVerification;
res.status(statusCode).send(error);
return;
}

const parsedBody = httpRequestBodySchema.safeParse(req.body);
if (!parsedBody.success) {
// This error and status code is returned by AWS gateway when the request does not match the openAPI
Expand Down Expand Up @@ -235,13 +201,6 @@ export async function processHttpSignedDataRequest(req: Request, res: Response)
// Set headers for the responses
res.set(originVerification.headers);

const apiKeyVerification = verifyGcpApiKey(req, 'HTTP_SIGNED_DATA_GATEWAY_API_KEY');
if (!apiKeyVerification.success) {
const { statusCode, error } = apiKeyVerification;
res.status(statusCode).send(error);
return;
}

const parsedBody = httpSignedDataBodySchema.safeParse(req.body);
if (!parsedBody.success) {
// This error and status code is returned by AWS gateway when the request does not match the openAPI
Expand Down
12 changes: 6 additions & 6 deletions packages/airnode-deployer/src/infrastructure/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,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_api_key=12345678-0000-0000-0000-000000000000" -var="http_max_concurrency=20" -var="http_signed_data_api_key=87654321-0000-0000-0000-000000000000" -var="http_signed_data_max_concurrency=20" -auto-approve`,
`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`,
execOptions
);
});
Expand All @@ -380,12 +380,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_api_key=12345678-0000-0000-0000-000000000000" -var="http_max_concurrency=20" -var="http_signed_data_api_key=87654321-0000-0000-0000-000000000000" -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="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`,
{ 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_api_key=12345678-0000-0000-0000-000000000000" -var="http_max_concurrency=20" -var="http_signed_data_api_key=87654321-0000-0000-0000-000000000000" -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="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`,
execOptions
);
});
Expand Down Expand Up @@ -487,7 +487,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_api_key=12345678-0000-0000-0000-000000000000" -var="http_max_concurrency=20" -var="http_signed_data_api_key=87654321-0000-0000-0000-000000000000" -var="http_signed_data_max_concurrency=20" -auto-approve`,
`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`,
{ cwd: 'tmpDir' }
);
expect(exec).toHaveBeenNthCalledWith(3, 'terraform output -json -no-color', { cwd: 'tmpDir' });
Expand Down Expand Up @@ -527,7 +527,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_api_key=12345678-0000-0000-0000-000000000000" -var="http_max_concurrency=20" -var="http_signed_data_api_key=87654321-0000-0000-0000-000000000000" -var="http_signed_data_max_concurrency=20" -auto-approve`,
`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`,
{ cwd: 'tmpDir' }
);
expect(exec).toHaveBeenNthCalledWith(3, 'terraform output -json -no-color', { cwd: 'tmpDir' });
Expand Down Expand Up @@ -570,7 +570,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_api_key=12345678-0000-0000-0000-000000000000" -var="http_max_concurrency=20" -var="http_signed_data_api_key=87654321-0000-0000-0000-000000000000" -var="http_signed_data_max_concurrency=20" -auto-approve`,
`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`,
{ cwd: 'tmpDir' }
);
expect(exec).toHaveBeenNthCalledWith(3, 'terraform output -json -no-color', { cwd: 'tmpDir' });
Expand Down
4 changes: 2 additions & 2 deletions packages/airnode-deployer/src/infrastructure/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,14 +267,14 @@ export async function terraformAirnodeApply(
commonArguments.push(['var', 'max_concurrency', `${maxConcurrency}`]);

if (httpGateway?.enabled) {
commonArguments.push(['var', 'http_api_key', httpGateway.apiKey!]);
commonArguments.push(['var', 'http_gateway_enabled', 'true']);
if (httpGateway.maxConcurrency) {
commonArguments.push(['var', 'http_max_concurrency', `${httpGateway.maxConcurrency}`]);
}
}

if (httpSignedDataGateway?.enabled) {
commonArguments.push(['var', 'http_signed_data_api_key', httpSignedDataGateway.apiKey!]);
commonArguments.push(['var', 'http_signed_data_gateway_enabled', 'true']);
if (httpSignedDataGateway.maxConcurrency) {
commonArguments.push(['var', 'http_signed_data_max_concurrency', `${httpSignedDataGateway.maxConcurrency}`]);
}
Expand Down
18 changes: 12 additions & 6 deletions packages/airnode-deployer/terraform/aws/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
# * switch between local and remote lambda source
# * variable validation

resource "random_uuid" "http_path_key" {
}

resource "random_uuid" "http_signed_data_path_key" {
}

module "run" {
source = "./modules/function"

Expand Down Expand Up @@ -117,7 +123,7 @@ module "startCoordinatorBothGws" {

module "httpReq" {
source = "./modules/function"
count = var.http_api_key == null ? 0 : 1
count = var.http_gateway_enabled == false ? 0 : 1

name = "${local.name_prefix}-httpReq"
handler = "index.httpReq"
Expand All @@ -131,24 +137,24 @@ module "httpReq" {

module "httpGw" {
source = "./modules/apigateway"
count = var.http_api_key == null ? 0 : 1
count = var.http_gateway_enabled == false ? 0 : 1

name = "${local.name_prefix}-httpGw"
stage = "v1"
template_file = "./templates/httpGw.yaml.tpl"
template_variables = {
proxy_lambda = module.httpReq[0].lambda_arn
region = var.aws_region
path_key = random_uuid.http_path_key.result
}
lambdas = [
module.httpReq[0].lambda_arn
]
api_key = var.http_api_key
}

module "httpSignedReq" {
source = "./modules/function"
count = var.http_signed_data_api_key == null ? 0 : 1
count = var.http_signed_data_gateway_enabled == false ? 0 : 1

name = "${local.name_prefix}-httpSignedReq"
handler = "index.httpSignedReq"
Expand All @@ -166,17 +172,17 @@ module "httpSignedReq" {

module "httpSignedGw" {
source = "./modules/apigateway"
count = var.http_signed_data_api_key == null ? 0 : 1
count = var.http_signed_data_gateway_enabled == false ? 0 : 1

name = "${local.name_prefix}-httpSignedGw"
stage = "v1"
template_file = "./templates/httpSignedGw.yaml.tpl"
template_variables = {
proxy_lambda = module.httpSignedReq[0].lambda_arn
region = var.aws_region
path_key = random_uuid.http_signed_data_path_key.result
}
lambdas = [
module.httpSignedReq[0].lambda_arn
]
api_key = var.http_signed_data_api_key
}
11 changes: 0 additions & 11 deletions packages/airnode-deployer/terraform/aws/modules/apigateway/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,3 @@ resource "aws_api_gateway_usage_plan" "usage_plan" {
stage = aws_api_gateway_stage.stage.stage_name
}
}

resource "aws_api_gateway_api_key" "api_key" {
name = var.name
value = var.api_key
}

resource "aws_api_gateway_usage_plan_key" "main" {
key_id = aws_api_gateway_api_key.api_key.id
key_type = "API_KEY"
usage_plan_id = aws_api_gateway_usage_plan.usage_plan.id
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,3 @@ variable "lambdas" {
type = list(string)
default = []
}

variable "api_key" {
description = "API key to access the APIGateway"
type = string
}
Loading