From 3f12e316c1aa4786803fbbb0d91106fb8e4b235b Mon Sep 17 00:00:00 2001 From: Sergey Shelomentsev Date: Sun, 11 Feb 2024 19:27:44 +0200 Subject: [PATCH] feat: update all cache behavior that has Fingerprint pro association --- mgmt-lambda/DefaultSettings.ts | 3 +- mgmt-lambda/app.ts | 1 + mgmt-lambda/handlers/updateHandler.ts | 92 ++++++++++++++----- mgmt-lambda/test/app.test.ts | 10 +- .../test/handlers/handleUpdate.test.ts | 12 +-- 5 files changed, 84 insertions(+), 34 deletions(-) diff --git a/mgmt-lambda/DefaultSettings.ts b/mgmt-lambda/DefaultSettings.ts index cd9cd093..5168dba9 100644 --- a/mgmt-lambda/DefaultSettings.ts +++ b/mgmt-lambda/DefaultSettings.ts @@ -1,6 +1,7 @@ export const defaults = { AWS_REGION: 'us-east-1', LAMBDA_DISTRIBUTION_BUCKET: 'fingerprint-pro-cloudfront-integration-lambda-function', - LAMBDA_DISTRIBUTION_BUCKET_KEY: 'release/lambda_latest.zip', + LAMBDA_DISTRIBUTION_BUCKET_KEY: 'releaseV2/lambda_latest.zip', LAMBDA_HANDLER_NAME: 'fingerprintjs-pro-cloudfront-lambda-function.handler', + FP_CDN_URL: 'fpcdn.io', } diff --git a/mgmt-lambda/app.ts b/mgmt-lambda/app.ts index 8673e6df..3c7596cc 100644 --- a/mgmt-lambda/app.ts +++ b/mgmt-lambda/app.ts @@ -47,6 +47,7 @@ export async function handler( try { return await handleUpdate(lambdaClient, cloudFrontClient, deploymentSettings) } catch (e: any) { + console.error(e) return handleError(e) } } diff --git a/mgmt-lambda/handlers/updateHandler.ts b/mgmt-lambda/handlers/updateHandler.ts index 5d7f51f1..58f95ab8 100644 --- a/mgmt-lambda/handlers/updateHandler.ts +++ b/mgmt-lambda/handlers/updateHandler.ts @@ -28,7 +28,7 @@ export async function handleUpdate( settings: DeploymentSettings, ): Promise { console.info(`Going to upgrade Fingerprint Pro function association at CloudFront distribution.`) - console.info(`Settings: ${settings}`) + console.info(`Settings: ${JSON.stringify(settings)}`) const isLambdaFunctionExist = await checkIfLambdaFunctionWithNameExists(lambdaClient, settings.LambdaFunctionName) if (!isLambdaFunctionExist) { @@ -71,20 +71,67 @@ async function updateCloudFrontConfig( throw new ApiException(ErrorCode.CloudFrontDistributionNotFound) } - const cacheBehaviors = cfConfig.DistributionConfig.CacheBehaviors - const fpCbs = cacheBehaviors?.Items?.filter((it) => it.TargetOriginId === 'fpcdn.io') - if (!fpCbs || fpCbs?.length === 0) { + const distributionConfig = cfConfig.DistributionConfig + + let fpCacheBehaviorsFound = 0 + let fpCacheBehaviorsUpdated = 0 + const pathPatterns = [] + + if (distributionConfig.DefaultCacheBehavior?.TargetOriginId === defaults.FP_CDN_URL) { + fpCacheBehaviorsFound++ + const lambdas = distributionConfig.DefaultCacheBehavior.LambdaFunctionAssociations?.Items?.filter( + (it) => it && it.EventType === 'origin-request' && it.LambdaFunctionARN?.includes(`${lambdaFunctionName}:`), + ) + if (lambdas?.length === 1) { + lambdas[0].LambdaFunctionARN = latestFunctionArn + fpCacheBehaviorsUpdated++ + pathPatterns.push('/*') + console.info('Updated Fingerprint Pro Lambda@Edge function association in the default cache behavior') + } else { + console.info( + 'The default cache behavior has targeted to FP CDN, but has no Fingerprint Pro Lambda@Edge association', + ) + } + } + + const fpCbs = distributionConfig.CacheBehaviors?.Items?.filter((it) => it.TargetOriginId === defaults.FP_CDN_URL) + if (fpCbs && fpCbs?.length > 0) { + fpCacheBehaviorsFound += fpCbs.length + fpCbs.forEach((cacheBehavior) => { + const lambdas = cacheBehavior.LambdaFunctionAssociations?.Items?.filter( + (it) => it && it.EventType === 'origin-request' && it.LambdaFunctionARN?.includes(`${lambdaFunctionName}:`), + ) + if (lambdas?.length === 1) { + lambdas[0].LambdaFunctionARN = latestFunctionArn + fpCacheBehaviorsUpdated++ + if (cacheBehavior.PathPattern) { + let pathPattern = cacheBehavior.PathPattern + if (!cacheBehavior.PathPattern.startsWith('/')) { + pathPattern = '/' + pathPattern + } + pathPatterns.push(pathPattern) + } else { + console.error(`Path pattern is not defined for cache behavior ${JSON.stringify(cacheBehavior)}`) + } + } else { + console.info( + `Cache behavior ${JSON.stringify( + cacheBehavior, + )} has targeted to FP CDN, but has no Fingerprint Pro Lambda@Edge association`, + ) + } + }) + } + + if (fpCacheBehaviorsFound === 0) { throw new ApiException(ErrorCode.CacheBehaviorNotFound) } - const cacheBehavior = fpCbs[0] - const lambdas = cacheBehavior.LambdaFunctionAssociations?.Items?.filter( - (it) => it && it.EventType === 'origin-request' && it.LambdaFunctionARN?.includes(`${lambdaFunctionName}:`), - ) - if (!lambdas || lambdas?.length === 0) { + if (fpCacheBehaviorsUpdated === 0) { throw new ApiException(ErrorCode.LambdaFunctionAssociationNotFound) } - const lambda = lambdas[0] - lambda.LambdaFunctionARN = latestFunctionArn + if (pathPatterns.length === 0) { + throw new ApiException(ErrorCode.CacheBehaviorPatternNotDefined) + } const updateParams: UpdateDistributionCommandInput = { DistributionConfig: cfConfig.DistributionConfig, @@ -97,21 +144,20 @@ async function updateCloudFrontConfig( console.info(`CloudFront update has finished, ${JSON.stringify(updateCFResult)}`) console.info('Going to invalidate routes for upgraded cache behavior') - if (!cacheBehavior.PathPattern) { - throw new ApiException(ErrorCode.CacheBehaviorPatternNotDefined) - } - - let pathPattern = cacheBehavior.PathPattern - if (!pathPattern.startsWith('/')) { - pathPattern = '/' + pathPattern - } + invalidateFingerprintIntegrationCache(cloudFrontClient, cloudFrontDistributionId, pathPatterns) +} +async function invalidateFingerprintIntegrationCache( + cloudFrontClient: CloudFrontClient, + distributionId: string, + pathPatterns: string[], +) { const invalidationParams: CreateInvalidationCommandInput = { - DistributionId: cloudFrontDistributionId, + DistributionId: distributionId, InvalidationBatch: { Paths: { Quantity: 1, - Items: [pathPattern], + Items: pathPatterns, }, CallerReference: 'fingerprint-pro-management-lambda-function', }, @@ -133,9 +179,11 @@ async function updateLambdaFunctionCode(lambdaClient: LambdaClient, functionName FunctionName: functionName, Publish: true, }) - console.info('Sending update command to Lambda runtime') + console.info(`Sending update command to Lambda runtime with data ${JSON.stringify(command)}`) const result = await lambdaClient.send(command) + console.info(`Got update command result: ${JSON.stringify(result)}`) + if (!result) { throw new ApiException(ErrorCode.LambdaFunctionARNNotFound) } diff --git a/mgmt-lambda/test/app.test.ts b/mgmt-lambda/test/app.test.ts index 33ce26ac..4ff237b0 100644 --- a/mgmt-lambda/test/app.test.ts +++ b/mgmt-lambda/test/app.test.ts @@ -322,7 +322,7 @@ describe('Update endpoint', () => { lambdaMock .on(UpdateFunctionCodeCommand, { S3Bucket: 'fingerprint-pro-cloudfront-integration-lambda-function', - S3Key: 'release/lambda_latest.zip', + S3Key: 'releaseV2/lambda_latest.zip', FunctionName: 'arn:aws:lambda:us-east-1:1234567890:function:fingerprint-pro-lambda-function', Publish: true, }) @@ -363,7 +363,7 @@ describe('Update endpoint', () => { lambdaMock .on(UpdateFunctionCodeCommand, { S3Bucket: 'fingerprint-pro-cloudfront-integration-lambda-function', - S3Key: 'release/lambda_latest.zip', + S3Key: 'releaseV2/lambda_latest.zip', FunctionName: 'incorrect', Publish: true, }) @@ -444,7 +444,7 @@ describe('Update endpoint', () => { lambdaMock .on(UpdateFunctionCodeCommand, { S3Bucket: 'fingerprint-pro-cloudfront-integration-lambda-function', - S3Key: 'release/lambda_latest.zip', + S3Key: 'releaseV2/lambda_latest.zip', FunctionName: 'arn:aws:lambda:us-east-1:1234567890:function:fingerprint-pro-lambda-function', Publish: true, }) @@ -486,7 +486,7 @@ describe('Update endpoint', () => { lambdaMock .on(UpdateFunctionCodeCommand, { S3Bucket: 'fingerprint-pro-cloudfront-integration-lambda-function', - S3Key: 'release/lambda_latest.zip', + S3Key: 'releaseV2/lambda_latest.zip', FunctionName: 'arn:aws:lambda:us-east-1:1234567890:function:fingerprint-pro-lambda-function', Publish: true, }) @@ -554,7 +554,7 @@ describe('Update endpoint', () => { lambdaMock .on(UpdateFunctionCodeCommand, { S3Bucket: 'fingerprint-pro-cloudfront-integration-lambda-function', - S3Key: 'release/lambda_latest.zip', + S3Key: 'releaseV2/lambda_latest.zip', FunctionName: 'arn:aws:lambda:us-east-1:1234567890:function:fingerprint-pro-lambda-function', Publish: true, }) diff --git a/mgmt-lambda/test/handlers/handleUpdate.test.ts b/mgmt-lambda/test/handlers/handleUpdate.test.ts index 0c049e0d..77f57c44 100644 --- a/mgmt-lambda/test/handlers/handleUpdate.test.ts +++ b/mgmt-lambda/test/handlers/handleUpdate.test.ts @@ -164,7 +164,7 @@ describe('Handle mgmt-update', () => { lambdaMock .on(UpdateFunctionCodeCommand, { S3Bucket: 'fingerprint-pro-cloudfront-integration-lambda-function', - S3Key: 'release/lambda_latest.zip', + S3Key: 'releaseV2/lambda_latest.zip', FunctionName: 'fingerprint-pro-lambda-function', Publish: true, }) @@ -224,7 +224,7 @@ describe('Handle mgmt-update', () => { lambdaMock .on(UpdateFunctionCodeCommand, { S3Bucket: 'fingerprint-pro-cloudfront-integration-lambda-function', - S3Key: 'release/lambda_latest.zip', + S3Key: 'releaseV2/lambda_latest.zip', FunctionName: 'fingerprint-pro-lambda-function', Publish: true, }) @@ -303,7 +303,7 @@ describe('Handle mgmt-update', () => { lambdaMock .on(UpdateFunctionCodeCommand, { S3Bucket: 'fingerprint-pro-cloudfront-integration-lambda-function', - S3Key: 'release/lambda_latest.zip', + S3Key: 'releaseV2/lambda_latest.zip', FunctionName: 'fingerprint-pro-lambda-function', Publish: true, }) @@ -400,7 +400,7 @@ describe('Handle mgmt-update', () => { lambdaMock .on(UpdateFunctionCodeCommand, { S3Bucket: 'fingerprint-pro-cloudfront-integration-lambda-function', - S3Key: 'release/lambda_latest.zip', + S3Key: 'releaseV2/lambda_latest.zip', FunctionName: 'fingerprint-pro-lambda-function', Publish: true, }) @@ -433,7 +433,7 @@ describe('Handle mgmt-update', () => { lambdaMock .on(UpdateFunctionCodeCommand, { S3Bucket: 'fingerprint-pro-cloudfront-integration-lambda-function', - S3Key: 'release/lambda_latest.zip', + S3Key: 'releaseV2/lambda_latest.zip', FunctionName: 'fingerprint-pro-lambda-function', Publish: true, }) @@ -496,7 +496,7 @@ describe('Handle mgmt-update', () => { lambdaMock .on(UpdateFunctionCodeCommand, { S3Bucket: 'fingerprint-pro-cloudfront-integration-lambda-function', - S3Key: 'release/lambda_latest.zip', + S3Key: 'releaseV2/lambda_latest.zip', FunctionName: 'fingerprint-pro-lambda-function', Publish: true, })