diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bf1eaf..214286a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.3.0] - 2023-6-1 +### Added +- cdk-nag rule suppressions +- Updated deployment/build-s3-dist.sh to output cdk nag errors +- Added CloudWatch logs permissions to CustomResource component in cdk + +### Changed +- Upgraded to cdk v2 +- Added region name and account ID to AppRegistry Application name +- Changed AppRegistry Attribute Group name to Region-StackName +- Updated AppRegistry attribute and tag names +- Upgraded Lambda runtimes to node 16 +- Removed application insights +- Use logs bucket for cloudfront distribution logging + ## [1.2.1] - 2023-4-17 ### Changed - Updated object ownership configuration on Logs bucket and CloudFront Logging bucket @@ -14,8 +29,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Application Insights in AppRegistry - SonarQube properties file: sonar-project.properties - Added unit tests with 80% code coverage + ### Changed - Changed deployment/run-unit-tests.sh to generate unit test coverage reports + ### Contributors * @sandimciin * @eggoynes @@ -39,6 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Contributors * @eggoynes + ## [1.0.0] - 2020-11-05 ### Added - All files, initial version diff --git a/README.md b/README.md index d705f92..8d86eff 100644 --- a/README.md +++ b/README.md @@ -81,4 +81,4 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -This solution collects anonymous operational metrics to help AWS improve the quality of features of the solution. For more information, including how to disable this capability, please see the implementation guide. +This solution collects anonymized operational metrics to help AWS improve the quality of features of the solution. For more information, including how to disable this capability, please see the implementation guide. diff --git a/deployment/build-s3-dist.sh b/deployment/build-s3-dist.sh index 775d8ba..e9a8212 100755 --- a/deployment/build-s3-dist.sh +++ b/deployment/build-s3-dist.sh @@ -24,9 +24,6 @@ [ "$DEBUG" == 'true' ] && set -x set -e -# Important: CDK global version number -cdk_version=1.63.0 - # Check to see if input has been provided: if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then echo "Please provide all required parameters for the build script" @@ -57,26 +54,24 @@ mkdir -p $build_dist_dir rm -rf $staging_dist_dir mkdir -p $staging_dist_dir -echo "------------------------------------------------------------------------------" -echo "[Init] Update local CDK CLI for building" -echo "------------------------------------------------------------------------------" -npm install -g aws-cdk@latest - -echo "------------------------------------------------------------------------------" -echo "[Init] Install dependencies for the cdk-solution-helper" -echo "------------------------------------------------------------------------------" -cd $template_dir/cdk-solution-helper -npm install --production - echo "------------------------------------------------------------------------------" echo "[Synth] CDK Project" echo "------------------------------------------------------------------------------" -# Make sure user has the newest CDK version -npm uninstall -g aws-cdk && npm install -g aws-cdk@1 cd $source_dir/cdk npm install -cdk synth --output=$staging_dist_dir + +npm run cdk -- context --clear +npm run synth -- --output=$staging_dist_dir + +if [ $? -ne 0 ] +then + echo "******************************************************************************" + echo "cdk-nag found errors" + echo "******************************************************************************" + exit 1 +fi + cd $staging_dist_dir rm tree.json manifest.json cdk.out diff --git a/deployment/cdk-solution-helper/index.js b/deployment/cdk-solution-helper/index.js index 07c0430..29fa5b7 100644 --- a/deployment/cdk-solution-helper/index.js +++ b/deployment/cdk-solution-helper/index.js @@ -13,44 +13,50 @@ // Imports const fs = require('fs'); +const _regex = /[\w]*AssetParameters/g; //this regular express also takes into account lambda functions defined in nested stacks // Paths const global_s3_assets = '../global-s3-assets'; +const getAllAssetParameterKeys = (parameters) => + Object.keys(parameters).filter((key) => key.search(_regex) > -1); + // For each template in global_s3_assets ... -fs.readdirSync(global_s3_assets).forEach(file => { +fs.readdirSync(global_s3_assets).forEach((file) => { // Import and parse template file const raw_template = fs.readFileSync(`${global_s3_assets}/${file}`); let template = JSON.parse(raw_template); // Clean-up Lambda function code dependencies - const resources = (template.Resources) ? template.Resources : {}; + const resources = template.Resources ? template.Resources : {}; const lambdaFunctions = Object.keys(resources).filter(function (key) { return resources[key].Type === 'AWS::Lambda::Function'; }); - lambdaFunctions.forEach(function (f) { - const fn = template.Resources[f]; + const fn = resources[f]; if (fn.Properties.Code.hasOwnProperty('S3Bucket')) { // Set the S3 key reference - let artifactHash = Object.assign(fn.Properties.Code.S3Bucket.Ref); - artifactHash = artifactHash.replace('AssetParameters', ''); - artifactHash = artifactHash.substring(0, artifactHash.indexOf('S3Bucket')); + let artifactHash = Object.assign(fn.Properties.Code.S3Key); + artifactHash = artifactHash.replace(_regex, ''); + artifactHash = artifactHash.substring( + 0, + artifactHash.indexOf('.zip') + ); const assetPath = `asset${artifactHash}`; fn.Properties.Code.S3Key = `%%SOLUTION_NAME%%/%%VERSION%%/${assetPath}.zip`; - // Set the S3 bucket reference fn.Properties.Code.S3Bucket = { - 'Fn::Sub': '%%BUCKET_NAME%%-${AWS::Region}' + 'Fn::Sub': '%%BUCKET_NAME%%-${AWS::Region}', }; + // Set the handler + const handler = fn.Properties.Handler; + fn.Properties.Handler = `${assetPath}/${handler}`; } }); // Clean-up parameters section - const parameters = (template.Parameters) ? template.Parameters : {}; - const assetParameters = Object.keys(parameters).filter(function (key) { - return key.includes('AssetParameters'); - }); + const parameters = template.Parameters ? template.Parameters : {}; + const assetParameters = getAllAssetParameterKeys(parameters); assetParameters.forEach(function (a) { template.Parameters[a] = undefined; }); @@ -58,4 +64,4 @@ fs.readdirSync(global_s3_assets).forEach(file => { // Output modified template file const output_template = JSON.stringify(template, null, 2); fs.writeFileSync(`${global_s3_assets}/${file}`, output_template); -}); \ No newline at end of file +}); diff --git a/deployment/cdk-solution-helper/package.json b/deployment/cdk-solution-helper/package.json index 1831a48..a05f450 100644 --- a/deployment/cdk-solution-helper/package.json +++ b/deployment/cdk-solution-helper/package.json @@ -1,7 +1,10 @@ { "name": "cdk-solution-helper", + "description": "CDK Generated template post processor", + "license": "Apache-2.0", "version": "0.1.0", - "dependencies": { - "fs": "0.0.1-security" + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com/solutions" } } diff --git a/source/cdk/bin/vod-foundation.ts b/source/cdk/bin/vod-foundation.ts index 8e549c7..f4d2717 100644 --- a/source/cdk/bin/vod-foundation.ts +++ b/source/cdk/bin/vod-foundation.ts @@ -1,7 +1,21 @@ #!/usr/bin/env node import 'source-map-support/register'; -import * as cdk from '@aws-cdk/core'; +import * as cdk from 'aws-cdk-lib'; +import { DefaultStackSynthesizer } from 'aws-cdk-lib'; +import { AwsSolutionsChecks } from 'cdk-nag'; import { VodFoundation } from '../lib/vod-foundation-stack'; +function createVodFoundationApp(app: cdk.App) { + return new VodFoundation(app, 'VodFoundation', { + synthesizer: new DefaultStackSynthesizer({ + generateBootstrapVersionRule: false + }) + }); +} + const app = new cdk.App(); -new VodFoundation(app, 'VodFoundation'); // NOSONAR + +createVodFoundationApp(app); + +//cdk nag +cdk.Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true })); diff --git a/source/cdk/cdk.json b/source/cdk/cdk.json index 40fd38b..4f17fd8 100644 --- a/source/cdk/cdk.json +++ b/source/cdk/cdk.json @@ -1,7 +1,6 @@ { "app": "npx ts-node bin/vod-foundation.ts", "context": { - "@aws-cdk/core:enableStackNameDuplicates": "true", "aws-cdk:enableDiffNoFail": "true", "@aws-cdk/core:stackRelativeExports": "true" } diff --git a/source/cdk/lib/vod-foundation-stack.ts b/source/cdk/lib/vod-foundation-stack.ts index c55a006..d94a62f 100644 --- a/source/cdk/lib/vod-foundation-stack.ts +++ b/source/cdk/lib/vod-foundation-stack.ts @@ -1,21 +1,22 @@ -import * as cdk from '@aws-cdk/core'; -import * as s3 from '@aws-cdk/aws-s3'; -import * as iam from '@aws-cdk/aws-iam'; -import * as lambda from '@aws-cdk/aws-lambda'; -import * as subs from '@aws-cdk/aws-sns-subscriptions'; -import { HttpMethods } from '@aws-cdk/aws-s3'; -import * as appreg from '@aws-cdk/aws-servicecatalogappregistry'; -import * as applicationinsights from '@aws-cdk/aws-applicationinsights'; +import * as cdk from 'aws-cdk-lib'; +import * as s3 from 'aws-cdk-lib/aws-s3'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import * as subs from 'aws-cdk-lib/aws-sns-subscriptions'; +import { HttpMethods } from 'aws-cdk-lib/aws-s3'; +import * as appreg from '@aws-cdk/aws-servicecatalogappregistry-alpha'; +import { NagSuppressions } from 'cdk-nag'; +import { Construct } from 'constructs'; /** * AWS Solution Constructs: https://docs.aws.amazon.com/solutions/latest/constructs/ */ import { CloudFrontToS3 } from '@aws-solutions-constructs/aws-cloudfront-s3'; -import { EventsRuleToLambda } from '@aws-solutions-constructs/aws-events-rule-lambda'; +import { EventbridgeToLambda } from '@aws-solutions-constructs/aws-eventbridge-lambda'; import { LambdaToSns } from '@aws-solutions-constructs/aws-lambda-sns'; export class VodFoundation extends cdk.Stack { - constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); /** * CloudFormation Template Descrption @@ -24,11 +25,11 @@ export class VodFoundation extends cdk.Stack { const solutionName = 'Video on Demand on AWS Foundation' this.templateOptions.description = '(SO0146) %%VERSION%%: Video on Demand on AWS Foundation Solution Implementation'; /** - * Mapping for sending anonymous metrics to AWS Solution Builders API + * Mapping for sending anonymized metrics to AWS Solution Builders API */ new cdk.CfnMapping(this, 'Send', { // NOSONAR mapping: { - AnonymousUsage: { + AnonymizedUsage: { Data: 'Yes' } } @@ -48,7 +49,9 @@ export class VodFoundation extends cdk.Stack { encryption: s3.BucketEncryption.S3_MANAGED, publicReadAccess: false, blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, - objectOwnership: s3.ObjectOwnership.OBJECT_WRITER + objectOwnership: s3.ObjectOwnership.OBJECT_WRITER, + enforceSSL: true, + versioned: true }); /** * Get Cfn Resource for the logs bucket and add CFN_NAG rule @@ -65,6 +68,19 @@ export class VodFoundation extends cdk.Stack { }] } }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + logsBucket, + [ + { + id: 'AwsSolutions-S1', //same as cfn_nag rule W35 + reason: 'Used to store access logs for other buckets' + }, { + id: 'AwsSolutions-S10', + reason: 'Bucket is private and is not using HTTP' + } + ] + ); /** * Source S3 bucket to host source videos and jobSettings JSON files */ @@ -74,6 +90,8 @@ export class VodFoundation extends cdk.Stack { encryption: s3.BucketEncryption.S3_MANAGED, publicReadAccess: false, blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, + enforceSSL: true, + versioned: true }); const cfnSource = source.node.findChild('Resource') as s3.CfnBucket; cfnSource.cfnOptions.metadata = { @@ -84,6 +102,16 @@ export class VodFoundation extends cdk.Stack { }] } }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + source, + [ + { + id: 'AwsSolutions-S10', + reason: 'Bucket is private and is not using HTTP' + } + ] + ); /** * Destination S3 bucket to host the mediaconvert outputs */ @@ -95,13 +123,25 @@ export class VodFoundation extends cdk.Stack { blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, cors: [ { - maxAge: 3000, - allowedOrigins: ['*'], - allowedHeaders: ['*'], - allowedMethods: [HttpMethods.GET] + maxAge: 3000, + allowedOrigins: ['*'], + allowedHeaders: ['*'], + allowedMethods: [HttpMethods.GET] }, - ], + ], + enforceSSL: true, + versioned: true }); + //cdk_nag + NagSuppressions.addResourceSuppressions( + destination, + [ + { + id: 'AwsSolutions-S10', + reason: 'Bucket is private and is not using HTTP' + } + ] + ); /** * Solutions construct to create Cloudfrotnt with an s3 bucket as the origin * https://docs.aws.amazon.com/solutions/latest/constructs/aws-cloudfront-s3.html @@ -122,15 +162,44 @@ export class VodFoundation extends cdk.Stack { }, viewerProtocolPolicy: 'allow-all' }, - loggingConfig: { - bucket: logsBucket, - prefix: 'cloudfront-logs' - } - }, - cloudFrontLoggingBucketProps: { - objectOwnership: s3.ObjectOwnership.OBJECT_WRITER + logBucket: logsBucket, + logFilePrefix: 'cloudfront-logs/' } }); + //cdk_nag + NagSuppressions.addResourceSuppressions( + destination.policy!, + [ + { + id: 'AwsSolutions-S10', + reason: 'Bucket is private and is not using HTTP' + } + ] + ); + NagSuppressions.addResourceSuppressions( + cloudFront.cloudFrontWebDistribution, + [ + { + id: 'AwsSolutions-CFR1', + reason: 'Use case does not warrant CloudFront Geo restriction' + }, { + id: 'AwsSolutions-CFR2', + reason: 'Use case does not warrant CloudFront integration with AWS WAF' + }, { + id: 'AwsSolutions-CFR4', //same as cfn_nag rule W70 + reason: 'CloudFront automatically sets the security policy to TLSv1 when the distribution uses the CloudFront domain name' + } + ] + ); + NagSuppressions.addResourceSuppressions( + cloudFront.cloudFrontLoggingBucket!, + [ + { + id: 'AwsSolutions-S1', + reason: 'Used to store access logs for other buckets' + } + ] + ); /** * MediaConvert Service Role to grant Mediaconvert Access to the source and Destination Bucket, * API invoke * is also required for the services. @@ -151,29 +220,69 @@ export class VodFoundation extends cdk.Stack { ] }); mediaconvertPolicy.attachToRole(mediaconvertRole); + //cdk_nag + NagSuppressions.addResourceSuppressions( + mediaconvertPolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: '/* required to get/put objects to S3' + } + ] + ); /** * Custom Resource, Role and Policy. */ + const customResourceRole = new iam.Role(this, 'CustomResourceRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com') + }); + + const customResourcePolicy = new iam.Policy(this, 'CustomResourcePolicy', { + statements: [ + new iam.PolicyStatement({ + actions: ["s3:PutObject","s3:PutBucketNotification"], + resources: [source.bucketArn, `${source.bucketArn}/*`] + }), + new iam.PolicyStatement({ + actions: ["mediaconvert:DescribeEndpoints"], + resources: [`arn:aws:mediaconvert:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:*`], + }), + new iam.PolicyStatement({ + actions: [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + resources: ['*'], + }) + ] + }); + customResourcePolicy.attachToRole(customResourceRole); + + //cdk_nag + NagSuppressions.addResourceSuppressions( + customResourcePolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: 'Resource ARNs are not generated at the time of policy creation' + } + ] + ); + const customResourceLambda = new lambda.Function(this, 'CustomResource', { - runtime: lambda.Runtime.NODEJS_12_X, + runtime: lambda.Runtime.NODEJS_16_X, handler: 'index.handler', description: 'CFN Custom resource to copy assets to S3 and get the MediaConvert endpoint', environment: { - SOLUTION_IDENTIFIER: 'AwsSolution/SO0146/v1.1.0' + SOLUTION_IDENTIFIER: 'AwsSolution/SO0146/%%VERSION%%' }, code: lambda.Code.fromAsset('../custom-resource'), timeout: cdk.Duration.seconds(30), - initialPolicy: [ - new iam.PolicyStatement({ - actions: ["s3:PutObject","s3:PutBucketNotification"], - resources: [source.bucketArn, `${source.bucketArn}/*`] - }), - new iam.PolicyStatement({ - actions: ["mediaconvert:DescribeEndpoints"], - resources: [`arn:aws:mediaconvert:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:*`], - }) - ] + role: customResourceRole }); + customResourceLambda.node.addDependency(customResourcePolicy); + customResourceLambda.node.addDependency(customResourceRole); /** get the cfn resource for the role and attach cfn_nag rule */ const cfnCustomResource = customResourceLambda.node.findChild('Resource') as lambda.CfnFunction; cfnCustomResource.cfnOptions.metadata = { @@ -192,6 +301,16 @@ export class VodFoundation extends cdk.Stack { }] } }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + customResourceLambda, + [ + { + id: 'AwsSolutions-L1', + reason: 'nodejs 18 lambda runtime does not support javascript SDK v2' + } + ] + ); /** * Call the custom resource, this will return the MediaConvert endpoint and a UUID */ @@ -202,9 +321,40 @@ export class VodFoundation extends cdk.Stack { /** * Job submit Lambda function, triggered by S3 Put events in the source S3 bucket */ + const jobSubmitRole = new iam.Role(this, 'JobSubmitRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com') + }); + const jobSubmitPolicy = new iam.Policy(this, 'JobSubmitPolicy', { + statements: [ + new iam.PolicyStatement({ + actions: ["iam:PassRole"], + resources: [mediaconvertRole.roleArn] + }), + new iam.PolicyStatement({ + actions: ["mediaconvert:CreateJob"], + resources: [`arn:${cdk.Aws.PARTITION}:mediaconvert:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:*`] + }), + new iam.PolicyStatement({ + actions: ["s3:GetObject"], + resources: [source.bucketArn, `${source.bucketArn}/*`] + }) + ] + }); + jobSubmitPolicy.attachToRole(jobSubmitRole); + //cdk_nag + NagSuppressions.addResourceSuppressions( + jobSubmitPolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: 'Resource ARNs are not generated at the time of policy creation' + } + ] + ); + const jobSubmit = new lambda.Function(this, 'jobSubmit', { code: lambda.Code.fromAsset(`../job-submit`), - runtime: lambda.Runtime.NODEJS_12_X, + runtime: lambda.Runtime.NODEJS_16_X, handler: 'index.handler', timeout: cdk.Duration.seconds(30), retryAttempts:0, @@ -216,24 +366,14 @@ export class VodFoundation extends cdk.Stack { DESTINATION_BUCKET: destination.bucketName, SOLUTION_ID: 'SO0146', STACKNAME: cdk.Aws.STACK_NAME, - SOLUTION_IDENTIFIER: 'AwsSolution/SO0146/v1.1.0' + SOLUTION_IDENTIFIER: 'AwsSolution/SO0146/%%VERSION%%' /** SNS_TOPIC_ARN: added by the solution construct below */ }, - initialPolicy: [ - new iam.PolicyStatement({ - actions: ["iam:PassRole"], - resources: [mediaconvertRole.roleArn] - }), - new iam.PolicyStatement({ - actions: ["mediaconvert:CreateJob"], - resources: [`arn:${cdk.Aws.PARTITION}:mediaconvert:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:*`] - }), - new iam.PolicyStatement({ - actions: ["s3:GetObject"], - resources: [source.bucketArn, `${source.bucketArn}/*`] - }) - ] + role: jobSubmitRole }); + jobSubmit.node.addDependency(jobSubmitPolicy); + jobSubmit.node.addDependency(jobSubmitRole); + /** Give S3 permission to trigger the job submit lambda function */ jobSubmit.addPermission('S3Trigger', { principal: new iam.ServicePrincipal('s3.amazonaws.com'), @@ -258,6 +398,16 @@ export class VodFoundation extends cdk.Stack { }] } }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + jobSubmit, + [ + { + id: 'AwsSolutions-L1', + reason: 'nodejs 18 lambda runtime does not support javascript SDK v2' + } + ] + ); /** * Process outputs lambda function, invoked by CloudWatch events for MediaConvert. * Parses the CW event outputs, creates the CloudFront URLs for the outputs, updates @@ -265,9 +415,36 @@ export class VodFoundation extends cdk.Stack { * Enviroment variables for the destination bucket and SNS topic are added by the * solutions constructs */ + const jobCompleteRole = new iam.Role(this, 'JobCompleteRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com') + }); + const jobCompletePolicy = new iam.Policy(this, 'JobCompletePolicy', { + statements: [ + new iam.PolicyStatement({ + actions: ["mediaconvert:GetJob"], + resources: [`arn:${cdk.Aws.PARTITION}:mediaconvert:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:*`] + }), + new iam.PolicyStatement({ + actions: ["s3:GetObject", "s3:PutObject"], + resources: [`${source.bucketArn}/*`] + }) + ] + }); + jobCompletePolicy.attachToRole(jobCompleteRole); + //cdk_nag + NagSuppressions.addResourceSuppressions( + jobCompletePolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: 'Resource ARNs are not generated at the time of policy creation' + } + ] + ); + const jobComplete = new lambda.Function(this, 'JobComplete', { code: lambda.Code.fromAsset(`../job-complete`), - runtime: lambda.Runtime.NODEJS_12_X, + runtime: lambda.Runtime.NODEJS_16_X, handler: 'index.handler', timeout: cdk.Duration.seconds(30), retryAttempts:0, @@ -279,23 +456,17 @@ export class VodFoundation extends cdk.Stack { SOURCE_BUCKET: source.bucketName, JOB_MANIFEST: 'jobs-manifest.json', STACKNAME: cdk.Aws.STACK_NAME, - METRICS: cdk.Fn.findInMap('Send', 'AnonymousUsage', 'Data'), + METRICS: cdk.Fn.findInMap('Send', 'AnonymizedUsage', 'Data'), SOLUTION_ID:'SO0146', - VERSION:'1.1.0', + VERSION:'%%VERSION%%', UUID:customResourceEndpoint.getAttString('UUID'), - SOLUTION_IDENTIFIER: 'AwsSolution/SO0146/v1.1.0' + SOLUTION_IDENTIFIER: 'AwsSolution/SO0146/%%VERSION%%' }, - initialPolicy: [ - new iam.PolicyStatement({ - actions: ["mediaconvert:GetJob"], - resources: [`arn:${cdk.Aws.PARTITION}:mediaconvert:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:*`] - }), - new iam.PolicyStatement({ - actions: ["s3:GetObject", "s3:PutObject"], - resources: [`${source.bucketArn}/*`] - }) - ] + role: jobCompleteRole }); + jobComplete.node.addDependency(jobCompletePolicy); + jobComplete.node.addDependency(jobCompleteRole); + const cfnJobComplete = jobComplete.node.findChild('Resource') as lambda.CfnFunction; cfnJobComplete.cfnOptions.metadata = { cfn_nag: { @@ -313,6 +484,16 @@ export class VodFoundation extends cdk.Stack { }] } }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + jobComplete, + [ + { + id: 'AwsSolutions-L1', + reason: 'nodejs 18 lambda runtime does not support javascript SDK v2' + } + ] + ); /** * Custom resource to configure the source S3 bucket; upload default job-settings file and * enabble event notifications to trigger the job-submit lambda function @@ -328,7 +509,7 @@ export class VodFoundation extends cdk.Stack { * Solution constructs, creates a CloudWatch event rule to trigger the process * outputs lambda functions. */ - new EventsRuleToLambda(this, 'EventTrigger', { // NOSONAR + new EventbridgeToLambda(this, 'EventTrigger', { // NOSONAR existingLambdaObj: jobComplete, eventRuleProps: { enabled: true, @@ -370,9 +551,9 @@ export class VodFoundation extends cdk.Stack { /** * AppRegistry */ - const applicationName = `vod-foundation-${cdk.Aws.STACK_NAME}`; + const applicationName = `vod-foundation-${cdk.Aws.REGION}-${cdk.Aws.ACCOUNT_ID}-${cdk.Aws.STACK_NAME}`; const attributeGroup = new appreg.AttributeGroup(this, 'AppRegistryAttributeGroup', { - attributeGroupName: cdk.Aws.STACK_NAME, + attributeGroupName: `${cdk.Aws.REGION}-${cdk.Aws.STACK_NAME}`, description: "Attribute group for solution information.", attributes: { ApplicationType: 'AWS-Solutions', @@ -385,24 +566,13 @@ export class VodFoundation extends cdk.Stack { applicationName: applicationName, description: `Service Catalog application to track and manage all your resources. The SolutionId is ${solutionId} and SolutionVersion is %%VERSION%%.` }); - appRegistry.associateStack(this); - cdk.Tags.of(appRegistry).add('solutionId', solutionId); - cdk.Tags.of(appRegistry).add('SolutionName', solutionName); - cdk.Tags.of(appRegistry).add('SolutionDomain', 'CloudFoundations'); - cdk.Tags.of(appRegistry).add('SolutionVersion', '%%VERSION%%'); - cdk.Tags.of(appRegistry).add('appRegistryApplicationName', 'vod-foundation-stack'); - cdk.Tags.of(appRegistry).add('ApplicationType', 'AWS-Solutions'); - - appRegistry.node.addDependency(attributeGroup); - appRegistry.associateAttributeGroup(attributeGroup); + appRegistry.associateApplicationWithStack(this); + cdk.Tags.of(appRegistry).add('Solutions:SolutionID', solutionId); + cdk.Tags.of(appRegistry).add('Solutions:SolutionName', solutionName); + cdk.Tags.of(appRegistry).add('Solutions:SolutionVersion', '%%VERSION%%'); + cdk.Tags.of(appRegistry).add('Solutions:ApplicationType', 'AWS-Solutions'); - const appInsights = new applicationinsights.CfnApplication(this, 'ApplicationInsightsApp', { - resourceGroupName: `AWS_AppRegistry_Application-${applicationName}`, - autoConfigurationEnabled: true, - cweMonitorEnabled: true, - opsCenterEnabled: true - }); - appInsights.node.addDependency(appRegistry); + attributeGroup.associateWith(appRegistry); /** * Stack Outputs diff --git a/source/cdk/package.json b/source/cdk/package.json index 6bf0c68..f674312 100644 --- a/source/cdk/package.json +++ b/source/cdk/package.json @@ -1,5 +1,11 @@ { "name": "vod-foundation", + "description": "Synthesize templates for Video on Demand on AWS Foundation using AWS Cloud Development Kit (CDK).", + "license": "Apache-2.0", + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com/solutions" + }, "version": "1.1.0", "bin": { "vod-foundation": "bin/vod-foundation.js" @@ -8,29 +14,29 @@ "build": "tsc", "watch": "tsc -w", "test": "jest --coverage --updateSnapshot", - "cdk": "cdk" + "cdk": "cdk", + "synth": "cdk synth -q" }, "devDependencies": { - "@aws-cdk/assert": "1.173.0", - "@types/jest": "^26.0.10", - "@types/node": "10.17.27", - "aws-cdk": "^1.173.0", - "jest": "^26.6.3", - "ts-jest": "^26.5.6", - "ts-node": "^8.1.0", - "typescript": "^3.9.10" + "@aws-cdk/assert": "2.68.0", + "@types/jest": "^29.5.1", + "@types/node": "20.2.3", + "aws-cdk": "^2.77.0", + "aws-cdk-lib": "^2.77.0", + "constructs": "10.2.8", + "jest": "^29.5.0", + "ts-jest": "^29.1.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" }, "dependencies": { - "@aws-cdk/core": "1.173.0", - "@aws-cdk/aws-s3": "1.173.0", - "@aws-cdk/aws-iam": "1.173.0", - "@aws-cdk/aws-lambda": "1.173.0", - "@aws-cdk/aws-servicecatalogappregistry": "1.173.0", - "@aws-cdk/aws-applicationinsights": "1.173.0", - "@aws-cdk/aws-sns-subscriptions": "1.173.0", - "@aws-solutions-constructs/aws-cloudfront-s3": "1.173.0", - "@aws-solutions-constructs/aws-events-rule-lambda": "1.173.0", - "@aws-solutions-constructs/aws-lambda-sns": "1.173.0", + "aws-cdk-lib": "2.77.0", + "constructs": "10.2.8", + "@aws-cdk/aws-servicecatalogappregistry-alpha": "2.77.0-alpha.0", + "@aws-solutions-constructs/aws-cloudfront-s3": "2.39.0", + "@aws-solutions-constructs/aws-eventbridge-lambda": "2.39.0", + "@aws-solutions-constructs/aws-lambda-sns": "2.39.0", + "cdk-nag": "^2.26.7", "source-map-support": "^0.5.16" } } diff --git a/source/cdk/test/vod-foundation.test.ts b/source/cdk/test/vod-foundation.test.ts index f8b3bf0..ca45d2d 100644 --- a/source/cdk/test/vod-foundation.test.ts +++ b/source/cdk/test/vod-foundation.test.ts @@ -1,7 +1,27 @@ import { SynthUtils } from '@aws-cdk/assert'; -import { Stack } from '@aws-cdk/core'; +import { Stack } from 'aws-cdk-lib'; import * as VodStack from '../lib/vod-foundation-stack'; +expect.addSnapshotSerializer({ + test: (val) => typeof val === 'string', + print: (val) => { + const valueReplacements = [ + { + regex: /AssetParameters([A-Fa-f0-9]{64})(\w+)/, + replacementValue: 'AssetParameters[HASH REMOVED]' + }, + { + regex: /(\w+ for asset)\s?(version)?\s?"([A-Fa-f0-9]{64})"/, + replacementValue: '$1 [HASH REMOVED]' + } + ]; + return `${valueReplacements.reduce( + (output, replacement) => output.replace(replacement.regex, replacement.replacementValue), + val as string + )}`; + } +}); + test('VOD Foundation Stack Test', () => { const stack = new Stack(); new VodStack.VodFoundation(stack, 'VOD'); diff --git a/source/custom-resource/index.js b/source/custom-resource/index.js index 3f7e542..57061c6 100644 --- a/source/custom-resource/index.js +++ b/source/custom-resource/index.js @@ -2,7 +2,7 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ -const uuidv4 = require('uuid/v4'); +const { v4: uuidv4} = require('uuid'); const mediaconvert = require('./lib/mediaconvert'); const s3 = require('./lib/s3'); const cfn = require('./lib/cfn'); diff --git a/source/custom-resource/lib/s3/index.js b/source/custom-resource/lib/s3/index.js index 16dbc4a..f942051 100644 --- a/source/custom-resource/lib/s3/index.js +++ b/source/custom-resource/lib/s3/index.js @@ -33,307 +33,57 @@ const setDefaults = async (bucket) => { console.log('creating files complete') }; +/** + * + * Helper function to create S3 ObjectCreated event handler + * + * @param {string} lambdaArn - ARN of the Lambda function to be invoked + * @param {string} suffix - Object suffix to filter event + * @returns + */ +const getS3ObjectCreatedHandlerConfig = (lambdaArn, suffix) => ({ + Events: ['s3:ObjectCreated:*'], + LambdaFunctionArn: lambdaArn, + Filter: { + Key: { + FilterRules: [{ + Name: 'suffix', + Value: suffix + }] + } + } +}) + /** * Add event notifications to the source S3 bucket */ -const putNotification = async (bucket,lambdaArn) => { +const putNotification = async (bucket, lambdaArn) => { try { const s3 = new AWS.S3(options); + const suffixList = [ + '.mpg', + '.mp4', + '.m4v', + '.mov', + '.m2ts', + '.wmv', + '.mxf', + '.mkv', + '.m3u8', + '.mpeg', + '.webm', + '.h264', + ]; + await s3.putBucketNotificationConfiguration({ Bucket: bucket, NotificationConfiguration: { - LambdaFunctionConfigurations: [ - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.mpg' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.mp4' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.m4v' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.mov' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.m2ts' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.wmv' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.mxf' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.mkv' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.m3u8' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.mpeg' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.webm' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.h264' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.MPG' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.MP4' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.M4V' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.MOV' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.M2TS' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.WMV' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.MXF' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.MKV' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.M3U8' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.MPEG' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.WEBM' - }] - } - } - }, - { - Events: ['s3:ObjectCreated:*'], - LambdaFunctionArn: lambdaArn, - Filter: { - Key: { - FilterRules: [{ - Name: 'suffix', - Value: '.H264' - }] - } - } - } - ] + LambdaFunctionConfigurations: suffixList.map(suffix => ([ + getS3ObjectCreatedHandlerConfig(lambdaArn, suffix), + getS3ObjectCreatedHandlerConfig(lambdaArn, suffix.toUpperCase()), + ])).flat(), } }).promise(); } catch (err) { @@ -342,8 +92,7 @@ const putNotification = async (bucket,lambdaArn) => { } }; - module.exports = { setDefaults: setDefaults, putNotification:putNotification -}; \ No newline at end of file +}; diff --git a/source/custom-resource/package.json b/source/custom-resource/package.json index dc488eb..41d79c8 100644 --- a/source/custom-resource/package.json +++ b/source/custom-resource/package.json @@ -10,16 +10,17 @@ "test": "jest --coverage" }, "dependencies": { - "axios": "^0.21.1", - "uuid": "^3.4.0" + "axios": "^1.4.0", + "uuid": "^9.0.0" }, "devDependencies": { "aws-sdk": "^2.609.0", - "jest": "^26.6.3" + "jest": "^29.5.0" }, "private": true, "author": { - "name": "aws-solutions-builder" + "name": "Amazon Web Services", + "url": "https://aws.amazon.com/solutions" }, "license": "Apache-2.0" } diff --git a/source/job-complete/lib/utils.js b/source/job-complete/lib/utils.js index 9e2d034..6a50be1 100644 --- a/source/job-complete/lib/utils.js +++ b/source/job-complete/lib/utils.js @@ -58,11 +58,10 @@ const writeManifest = async (bucket, manifestFile,jobDetails) => { Body: JSON.stringify(manifest) }).promise(); } catch (err) { - throw { - Message:'Failed to update the jobs-manifest.json, please check its accessible in the root of the source S3 bucket', - Error: err, - Job: jobDetails - }; + const error = new Error('Failed to update the jobs-manifest.json, please check its accessible in the root of the source S3 bucket'); + error.Error = err; + error.Job = jobDetails; + throw error; } return results; }; diff --git a/source/job-complete/lib/utils.spec.js b/source/job-complete/lib/utils.spec.js index 342a7e9..de97ab2 100644 --- a/source/job-complete/lib/utils.spec.js +++ b/source/job-complete/lib/utils.spec.js @@ -94,6 +94,8 @@ describe("Utils WriteManifest", () => { }); await utils.writeManifest("bucket", "manifestFile", "data").catch((err) => { expect(err.Error.toString()).toEqual("GET FAILED"); + expect(err.message).toEqual('Failed to update the jobs-manifest.json, please check its accessible in the root of the source S3 bucket'); + expect(err.Job).toEqual('data');; }); }); }); diff --git a/source/job-complete/package.json b/source/job-complete/package.json index fd4dc12..33cfbc6 100644 --- a/source/job-complete/package.json +++ b/source/job-complete/package.json @@ -10,17 +10,18 @@ "test": "jest --coverage" }, "dependencies": { - "axios": "^0.21.1", + "axios": "^1.4.0", "moment": "^2.29.1", - "uuid": "^3.4.0" + "uuid": "^9.0.0" }, "devDependencies": { "aws-sdk": "^2.609.0", - "jest": "^26.6.3" + "jest": "^29.5.0" }, "private": true, "author": { - "name": "aws-solutions-builder" + "name": "Amazon Web Services", + "url": "https://aws.amazon.com/solutions" }, "license": "Apache-2.0" } diff --git a/source/job-submit/index.js b/source/job-submit/index.js index d804883..2b0b6a6 100644 --- a/source/job-submit/index.js +++ b/source/job-submit/index.js @@ -2,7 +2,7 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ -const uuidv4 = require('uuid/v4'); +const { v4: uuidv4} = require('uuid'); const utils = require('./lib/utils.js'); exports.handler = async (event,context) => { diff --git a/source/job-submit/lib/utils.js b/source/job-submit/lib/utils.js index c24684f..4e8f536 100644 --- a/source/job-submit/lib/utils.js +++ b/source/job-submit/lib/utils.js @@ -28,10 +28,9 @@ const getJobSettings = async (bucket, settingsFile) => { throw new Error('Invalid settings file in s3'); } } catch (err) { - throw { - Message:'Failed to download and validate the job-settings.json file. Please check its contents and location. Details on using custom settings: https://github.com/awslabs/video-on-demand-on-aws-foundations', - Error: err.toString() - }; + const error = new Error('Failed to download and validate the job-settings.json file. Please check its contents and location. Details on using custom settings: https://github.com/awslabs/video-on-demand-on-aws-foundations'); + error.Error = err.toString(); + throw error; } return settings; }; @@ -104,10 +103,9 @@ const updateJobSettings = async (job, inputPath, outputPath, metadata, role) => */ job.UserMetadata = {...job.UserMetadata, ...metadata}; } catch (err) { - throw { - Message:'Failed to update the job-settings.json file. Details on using custom settings: https://github.com/awslabs/video-on-demand-on-aws-foundations', - Error: err.toString() - }; + const error = new Error('Failed to update the job-settings.json file. Details on using custom settings: https://github.com/awslabs/video-on-demand-on-aws-foundations'); + error.Error = err.toString(); + throw error; } return job; }; diff --git a/source/job-submit/lib/utils.spec.js b/source/job-submit/lib/utils.spec.js index dfd756c..525f172 100644 --- a/source/job-submit/lib/utils.spec.js +++ b/source/job-submit/lib/utils.spec.js @@ -142,7 +142,11 @@ const validSettings = { const inValidSettings = { "Role": "", "Settings": { - "OutputGroups": [], + "OutputGroups": [{ + OutputGroupSettings: { + Type: "invalid", + } + }], "Inputs": [ { "FileInput": "s3://sourcebucket/assets01/test.mp4" @@ -189,6 +193,7 @@ describe('Utils GetJobSettings',() => { }); await utils.getJobSettings().catch(err => { expect(err.Error.toString()).toEqual('GET FAILED') + expect(err.message).toEqual('Failed to download and validate the job-settings.json file. Please check its contents and location. Details on using custom settings: https://github.com/awslabs/video-on-demand-on-aws-foundations'); }); }); }); @@ -200,7 +205,8 @@ describe('Utils UpdateJobSettings',() => { }); it('UpdateJobSettings Failed test', async () => { await utils.updateJobSettings(inValidSettings,'inputPath','outputPath',{},'role').catch(err => { - expect(err.toString()).toEqual("Error: 'OutputGroupSettings.Type is not a valid type. Please check/test your job settings file.'"); + expect(err.toString()).toEqual("Error: Failed to update the job-settings.json file. Details on using custom settings: https://github.com/awslabs/video-on-demand-on-aws-foundations"); + expect(err.Error).toEqual('Error: OutputGroupSettings.Type is not a valid type. Please check your job settings file.'); }); }); }); diff --git a/source/job-submit/package.json b/source/job-submit/package.json index 95164b4..8d48b8f 100644 --- a/source/job-submit/package.json +++ b/source/job-submit/package.json @@ -11,15 +11,16 @@ }, "dependencies": { "lodash": "*", - "uuid": "^3.4.0" + "uuid": "^9.0.0" }, "devDependencies": { "aws-sdk": "^2.609.0", - "jest": "^26.6.3" + "jest": "^29.5.0" }, "private": true, "author": { - "name": "aws-solutions-builder" + "name": "Amazon Web Services", + "url": "https://aws.amazon.com/solutions" }, "license": "Apache-2.0" } diff --git a/source/test/coverage-reports/jest/cdk/clover.xml b/source/test/coverage-reports/jest/cdk/clover.xml deleted file mode 100644 index 7f280ce..0000000 --- a/source/test/coverage-reports/jest/cdk/clover.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/source/test/coverage-reports/jest/cdk/coverage-final.json b/source/test/coverage-reports/jest/cdk/coverage-final.json deleted file mode 100644 index 0b8c809..0000000 --- a/source/test/coverage-reports/jest/cdk/coverage-final.json +++ /dev/null @@ -1,2 +0,0 @@ -{"/codebuild/output/src926407134/src/source/cdk/lib/vod-foundation-stack.ts": {"path":"/codebuild/output/src926407134/src/source/cdk/lib/vod-foundation-stack.ts","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":37}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":38}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":40}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":46}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":55}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":46}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":65}},"7":{"start":{"line":8,"column":0},"end":{"line":8,"column":72}},"8":{"start":{"line":12,"column":0},"end":{"line":12,"column":77}},"9":{"start":{"line":13,"column":0},"end":{"line":13,"column":86}},"10":{"start":{"line":14,"column":0},"end":{"line":14,"column":71}},"11":{"start":{"line":19,"column":8},"end":{"line":19,"column":32}},"12":{"start":{"line":23,"column":27},"end":{"line":23,"column":35}},"13":{"start":{"line":24,"column":29},"end":{"line":24,"column":64}},"14":{"start":{"line":25,"column":8},"end":{"line":25,"column":120}},"15":{"start":{"line":29,"column":8},"end":{"line":35,"column":11}},"16":{"start":{"line":39,"column":27},"end":{"line":43,"column":10}},"17":{"start":{"line":47,"column":27},"end":{"line":51,"column":10}},"18":{"start":{"line":55,"column":30},"end":{"line":55,"column":83}},"19":{"start":{"line":56,"column":8},"end":{"line":66,"column":10}},"20":{"start":{"line":70,"column":23},"end":{"line":76,"column":10}},"21":{"start":{"line":77,"column":26},"end":{"line":77,"column":75}},"22":{"start":{"line":78,"column":8},"end":{"line":85,"column":10}},"23":{"start":{"line":89,"column":28},"end":{"line":103,"column":10}},"24":{"start":{"line":109,"column":27},"end":{"line":129,"column":10}},"25":{"start":{"line":134,"column":33},"end":{"line":136,"column":10}},"26":{"start":{"line":137,"column":35},"end":{"line":148,"column":10}},"27":{"start":{"line":149,"column":8},"end":{"line":149,"column":58}},"28":{"start":{"line":153,"column":37},"end":{"line":172,"column":10}},"29":{"start":{"line":174,"column":34},"end":{"line":174,"column":103}},"30":{"start":{"line":175,"column":8},"end":{"line":190,"column":10}},"31":{"start":{"line":194,"column":39},"end":{"line":196,"column":10}},"32":{"start":{"line":201,"column":26},"end":{"line":232,"column":10}},"33":{"start":{"line":234,"column":8},"end":{"line":238,"column":11}},"34":{"start":{"line":240,"column":29},"end":{"line":240,"column":87}},"35":{"start":{"line":241,"column":8},"end":{"line":256,"column":10}},"36":{"start":{"line":264,"column":28},"end":{"line":294,"column":10}},"37":{"start":{"line":295,"column":31},"end":{"line":295,"column":91}},"38":{"start":{"line":296,"column":8},"end":{"line":311,"column":10}},"39":{"start":{"line":316,"column":8},"end":{"line":322,"column":11}},"40":{"start":{"line":327,"column":8},"end":{"line":348,"column":11}},"41":{"start":{"line":354,"column":25},"end":{"line":356,"column":10}},"42":{"start":{"line":357,"column":8},"end":{"line":360,"column":11}},"43":{"start":{"line":364,"column":8},"end":{"line":364,"column":null}},"44":{"start":{"line":369,"column":32},"end":{"line":369,"column":70}},"45":{"start":{"line":370,"column":31},"end":{"line":379,"column":10}},"46":{"start":{"line":380,"column":28},"end":{"line":383,"column":10}},"47":{"start":{"line":384,"column":8},"end":{"line":384,"column":41}},"48":{"start":{"line":385,"column":8},"end":{"line":385,"column":63}},"49":{"start":{"line":386,"column":8},"end":{"line":386,"column":67}},"50":{"start":{"line":387,"column":8},"end":{"line":387,"column":75}},"51":{"start":{"line":388,"column":8},"end":{"line":388,"column":71}},"52":{"start":{"line":389,"column":8},"end":{"line":389,"column":91}},"53":{"start":{"line":390,"column":8},"end":{"line":390,"column":73}},"54":{"start":{"line":392,"column":8},"end":{"line":392,"column":55}},"55":{"start":{"line":393,"column":8},"end":{"line":393,"column":60}},"56":{"start":{"line":395,"column":28},"end":{"line":400,"column":10}},"57":{"start":{"line":401,"column":8},"end":{"line":401,"column":52}},"58":{"start":{"line":406,"column":8},"end":{"line":410,"column":11}},"59":{"start":{"line":411,"column":8},"end":{"line":415,"column":11}},"60":{"start":{"line":416,"column":8},"end":{"line":420,"column":11}},"61":{"start":{"line":421,"column":8},"end":{"line":425,"column":11}},"62":{"start":{"line":17,"column":0},"end":{"line":17,"column":13}}},"fnMap":{"0":{"name":"(anonymous_6)","decl":{"start":{"line":18,"column":4},"end":{"line":18,"column":16}},"loc":{"start":{"line":18,"column":72},"end":{"line":426,"column":5}}}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"14":1,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1,"23":1,"24":1,"25":1,"26":1,"27":1,"28":1,"29":1,"30":1,"31":1,"32":1,"33":1,"34":1,"35":1,"36":1,"37":1,"38":1,"39":1,"40":1,"41":1,"42":1,"43":1,"44":1,"45":1,"46":1,"47":1,"48":1,"49":1,"50":1,"51":1,"52":1,"53":1,"54":1,"55":1,"56":1,"57":1,"58":1,"59":1,"60":1,"61":1,"62":1},"f":{"0":1},"b":{}} -} diff --git a/source/test/coverage-reports/jest/cdk/lcov.info b/source/test/coverage-reports/jest/cdk/lcov.info deleted file mode 100644 index 287fa78..0000000 --- a/source/test/coverage-reports/jest/cdk/lcov.info +++ /dev/null @@ -1,74 +0,0 @@ -TN: -SF:lib/vod-foundation-stack.ts -FN:18,(anonymous_6) -FNF:1 -FNH:1 -FNDA:1,(anonymous_6) -DA:1,1 -DA:2,1 -DA:3,1 -DA:4,1 -DA:5,1 -DA:6,1 -DA:7,1 -DA:8,1 -DA:12,1 -DA:13,1 -DA:14,1 -DA:17,1 -DA:19,1 -DA:23,1 -DA:24,1 -DA:25,1 -DA:29,1 -DA:39,1 -DA:47,1 -DA:55,1 -DA:56,1 -DA:70,1 -DA:77,1 -DA:78,1 -DA:89,1 -DA:109,1 -DA:134,1 -DA:137,1 -DA:149,1 -DA:153,1 -DA:174,1 -DA:175,1 -DA:194,1 -DA:201,1 -DA:234,1 -DA:240,1 -DA:241,1 -DA:264,1 -DA:295,1 -DA:296,1 -DA:316,1 -DA:327,1 -DA:354,1 -DA:357,1 -DA:364,1 -DA:369,1 -DA:370,1 -DA:380,1 -DA:384,1 -DA:385,1 -DA:386,1 -DA:387,1 -DA:388,1 -DA:389,1 -DA:390,1 -DA:392,1 -DA:393,1 -DA:395,1 -DA:401,1 -DA:406,1 -DA:411,1 -DA:416,1 -DA:421,1 -LF:63 -LH:63 -BRF:0 -BRH:0 -end_of_record diff --git a/source/test/coverage-reports/jest/custom-resource/clover.xml b/source/test/coverage-reports/jest/custom-resource/clover.xml deleted file mode 100644 index 7a741d2..0000000 --- a/source/test/coverage-reports/jest/custom-resource/clover.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/source/test/coverage-reports/jest/custom-resource/coverage-final.json b/source/test/coverage-reports/jest/custom-resource/coverage-final.json deleted file mode 100644 index da07d55..0000000 --- a/source/test/coverage-reports/jest/custom-resource/coverage-final.json +++ /dev/null @@ -1,4 +0,0 @@ -{"/codebuild/output/src926407134/src/source/custom-resource/lib/cfn/index.js": {"path":"/codebuild/output/src926407134/src/source/custom-resource/lib/cfn/index.js","statementMap":{"0":{"start":{"line":5,"column":14},"end":{"line":5,"column":30}},"1":{"start":{"line":10,"column":21},"end":{"line":42,"column":1}},"2":{"start":{"line":13,"column":2},"end":{"line":40,"column":5}},"3":{"start":{"line":14,"column":25},"end":{"line":23,"column":6}},"4":{"start":{"line":25,"column":19},"end":{"line":34,"column":5}},"5":{"start":{"line":36,"column":8},"end":{"line":36,"column":35}},"6":{"start":{"line":38,"column":8},"end":{"line":38,"column":27}},"7":{"start":{"line":39,"column":8},"end":{"line":39,"column":18}},"8":{"start":{"line":41,"column":4},"end":{"line":41,"column":23}},"9":{"start":{"line":44,"column":0},"end":{"line":46,"column":2}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":10,"column":21},"end":{"line":10,"column":22}},"loc":{"start":{"line":10,"column":77},"end":{"line":42,"column":1}},"line":10}},"branchMap":{},"s":{"0":1,"1":1,"2":2,"3":2,"4":2,"5":2,"6":1,"7":1,"8":1,"9":1},"f":{"0":2},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"63115191ac1a6207089c5ce70b9463313d97f279"} -,"/codebuild/output/src926407134/src/source/custom-resource/lib/mediaconvert/index.js": {"path":"/codebuild/output/src926407134/src/source/custom-resource/lib/mediaconvert/index.js","statementMap":{"0":{"start":{"line":7,"column":16},"end":{"line":7,"column":68}},"1":{"start":{"line":8,"column":12},"end":{"line":8,"column":30}},"2":{"start":{"line":14,"column":20},"end":{"line":25,"column":1}},"3":{"start":{"line":16,"column":4},"end":{"line":23,"column":5}},"4":{"start":{"line":17,"column":29},"end":{"line":17,"column":58}},"5":{"start":{"line":18,"column":21},"end":{"line":18,"column":69}},"6":{"start":{"line":19,"column":8},"end":{"line":19,"column":41}},"7":{"start":{"line":21,"column":8},"end":{"line":21,"column":27}},"8":{"start":{"line":22,"column":8},"end":{"line":22,"column":18}},"9":{"start":{"line":24,"column":4},"end":{"line":24,"column":20}},"10":{"start":{"line":28,"column":0},"end":{"line":30,"column":2}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":14,"column":20},"end":{"line":14,"column":21}},"loc":{"start":{"line":14,"column":32},"end":{"line":25,"column":1}},"line":14}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":2,"4":2,"5":2,"6":1,"7":1,"8":1,"9":1,"10":1},"f":{"0":2},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"09e6f7f8b614054a06fb49c4fac9baeee5bb7cd9"} -,"/codebuild/output/src926407134/src/source/custom-resource/lib/s3/index.js": {"path":"/codebuild/output/src926407134/src/source/custom-resource/lib/s3/index.js","statementMap":{"0":{"start":{"line":7,"column":16},"end":{"line":7,"column":68}},"1":{"start":{"line":8,"column":12},"end":{"line":8,"column":30}},"2":{"start":{"line":9,"column":11},"end":{"line":9,"column":24}},"3":{"start":{"line":15,"column":20},"end":{"line":34,"column":1}},"4":{"start":{"line":16,"column":4},"end":{"line":16,"column":33}},"5":{"start":{"line":17,"column":4},"end":{"line":32,"column":5}},"6":{"start":{"line":18,"column":19},"end":{"line":18,"column":38}},"7":{"start":{"line":19,"column":8},"end":{"line":23,"column":21}},"8":{"start":{"line":24,"column":8},"end":{"line":28,"column":21}},"9":{"start":{"line":30,"column":8},"end":{"line":30,"column":27}},"10":{"start":{"line":31,"column":8},"end":{"line":31,"column":18}},"11":{"start":{"line":33,"column":4},"end":{"line":33,"column":42}},"12":{"start":{"line":39,"column":24},"end":{"line":343,"column":1}},"13":{"start":{"line":41,"column":4},"end":{"line":342,"column":5}},"14":{"start":{"line":42,"column":19},"end":{"line":42,"column":38}},"15":{"start":{"line":44,"column":8},"end":{"line":338,"column":21}},"16":{"start":{"line":340,"column":8},"end":{"line":340,"column":27}},"17":{"start":{"line":341,"column":8},"end":{"line":341,"column":18}},"18":{"start":{"line":346,"column":0},"end":{"line":349,"column":2}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":15,"column":20},"end":{"line":15,"column":21}},"loc":{"start":{"line":15,"column":38},"end":{"line":34,"column":1}},"line":15},"1":{"name":"(anonymous_1)","decl":{"start":{"line":39,"column":24},"end":{"line":39,"column":25}},"loc":{"start":{"line":39,"column":52},"end":{"line":343,"column":1}},"line":39}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":2,"5":2,"6":2,"7":2,"8":1,"9":1,"10":1,"11":1,"12":1,"13":2,"14":2,"15":2,"16":1,"17":1,"18":1},"f":{"0":2,"1":2},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"2f28ec3fd46606cd8067113b51af4672ca95dccb"} -} diff --git a/source/test/coverage-reports/jest/custom-resource/lcov.info b/source/test/coverage-reports/jest/custom-resource/lcov.info deleted file mode 100644 index 6b8cb6f..0000000 --- a/source/test/coverage-reports/jest/custom-resource/lcov.info +++ /dev/null @@ -1,75 +0,0 @@ -TN: -SF:lib/cfn/index.js -FN:10,(anonymous_0) -FNF:1 -FNH:1 -FNDA:2,(anonymous_0) -DA:5,1 -DA:10,1 -DA:13,2 -DA:14,2 -DA:25,2 -DA:36,2 -DA:38,1 -DA:39,1 -DA:41,1 -DA:44,1 -LF:10 -LH:10 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/mediaconvert/index.js -FN:14,(anonymous_0) -FNF:1 -FNH:1 -FNDA:2,(anonymous_0) -DA:7,1 -DA:8,1 -DA:14,1 -DA:16,2 -DA:17,2 -DA:18,2 -DA:19,1 -DA:21,1 -DA:22,1 -DA:24,1 -DA:28,1 -LF:11 -LH:11 -BRF:0 -BRH:0 -end_of_record -TN: -SF:lib/s3/index.js -FN:15,(anonymous_0) -FN:39,(anonymous_1) -FNF:2 -FNH:2 -FNDA:2,(anonymous_0) -FNDA:2,(anonymous_1) -DA:7,1 -DA:8,1 -DA:9,1 -DA:15,1 -DA:16,2 -DA:17,2 -DA:18,2 -DA:19,2 -DA:24,1 -DA:30,1 -DA:31,1 -DA:33,1 -DA:39,1 -DA:41,2 -DA:42,2 -DA:44,2 -DA:340,1 -DA:341,1 -DA:346,1 -LF:19 -LH:19 -BRF:0 -BRH:0 -end_of_record diff --git a/source/test/coverage-reports/jest/job-complete/lcov.info b/source/test/coverage-reports/jest/job-complete/lcov.info deleted file mode 100644 index 9710f85..0000000 --- a/source/test/coverage-reports/jest/job-complete/lcov.info +++ /dev/null @@ -1,127 +0,0 @@ -TN: -SF:job-complete/lib/utils.js -FN:13,(anonymous_0) -FN:44,(anonymous_1) -FN:75,(anonymous_2) -FN:77,(anonymous_3) -FN:103,(anonymous_4) -FN:110,(anonymous_5) -FN:134,(anonymous_6) -FN:190,(anonymous_7) -FN:204,(anonymous_8) -FN:205,(anonymous_9) -FNF:10 -FNH:10 -FNDA:3,(anonymous_0) -FNDA:1,(anonymous_1) -FNDA:2,(anonymous_2) -FNDA:6,(anonymous_3) -FNDA:4,(anonymous_4) -FNDA:4,(anonymous_5) -FNDA:3,(anonymous_6) -FNDA:2,(anonymous_7) -FNDA:8,(anonymous_8) -FNDA:42,(anonymous_9) -DA:5,1 -DA:6,1 -DA:7,1 -DA:13,1 -DA:15,3 -DA:16,3 -DA:17,3 -DA:21,3 -DA:25,2 -DA:27,2 -DA:32,1 -DA:33,1 -DA:43,1 -DA:44,1 -DA:45,1 -DA:46,1 -DA:47,1 -DA:48,1 -DA:49,1 -DA:51,0 -DA:52,0 -DA:55,2 -DA:61,1 -DA:67,2 -DA:75,1 -DA:76,2 -DA:77,6 -DA:78,2 -DA:82,2 -DA:84,2 -DA:85,2 -DA:87,1 -DA:103,1 -DA:104,4 -DA:105,2 -DA:107,2 -DA:108,0 -DA:110,2 -DA:111,4 -DA:119,1 -DA:120,6 -DA:123,1 -DA:124,1 -DA:126,1 -DA:127,1 -DA:134,1 -DA:135,3 -DA:138,3 -DA:141,3 -DA:146,1 -DA:147,1 -DA:153,1 -DA:159,1 -DA:160,1 -DA:164,1 -DA:169,1 -DA:170,1 -DA:171,1 -DA:173,3 -DA:174,3 -DA:180,1 -DA:181,1 -DA:190,1 -DA:191,2 -DA:192,2 -DA:193,2 -DA:194,2 -DA:195,2 -DA:196,2 -DA:197,2 -DA:204,2 -DA:205,8 -DA:206,42 -DA:207,2 -DA:214,2 -DA:222,2 -DA:231,2 -DA:232,2 -DA:234,1 -DA:240,1 -LF:80 -LH:77 -BRDA:27,0,0,1 -BRDA:27,0,1,1 -BRDA:45,1,0,1 -BRDA:45,1,1,0 -BRDA:104,2,0,2 -BRDA:104,2,1,2 -BRDA:107,3,0,0 -BRDA:107,3,1,2 -BRDA:120,4,0,3 -BRDA:120,4,1,3 -BRDA:141,5,0,1 -BRDA:141,5,1,0 -BRDA:141,5,2,1 -BRDA:141,5,3,1 -BRDA:169,6,0,1 -BRDA:169,6,1,0 -BRDA:206,7,0,2 -BRDA:206,7,1,40 -BRF:18 -BRH:14 -end_of_record diff --git a/source/test/coverage-reports/jest/job-submit/lcov.info b/source/test/coverage-reports/jest/job-submit/lcov.info deleted file mode 100644 index c9a6fd1..0000000 --- a/source/test/coverage-reports/jest/job-submit/lcov.info +++ /dev/null @@ -1,104 +0,0 @@ -TN: -SF:job-submit/lib/utils.js -FN:10,(anonymous_0) -FN:44,(anonymous_1) -FN:46,(anonymous_2) -FN:118,(anonymous_3) -FN:136,(anonymous_4) -FNF:5 -FNH:5 -FNDA:3,(anonymous_0) -FNDA:2,(anonymous_1) -FNDA:10,(anonymous_2) -FNDA:2,(anonymous_3) -FNDA:2,(anonymous_4) -DA:5,1 -DA:10,1 -DA:11,3 -DA:13,3 -DA:17,3 -DA:18,3 -DA:22,2 -DA:27,2 -DA:28,1 -DA:31,2 -DA:36,1 -DA:44,1 -DA:45,2 -DA:46,2 -DA:47,10 -DA:48,10 -DA:49,10 -DA:50,5 -DA:52,5 -DA:54,10 -DA:56,0 -DA:59,2 -DA:60,2 -DA:61,2 -DA:62,2 -DA:63,2 -DA:64,2 -DA:65,2 -DA:66,2 -DA:67,2 -DA:68,10 -DA:70,2 -DA:71,2 -DA:73,2 -DA:74,2 -DA:76,2 -DA:77,2 -DA:79,2 -DA:80,2 -DA:82,2 -DA:83,2 -DA:85,0 -DA:91,2 -DA:92,2 -DA:94,2 -DA:98,2 -DA:99,0 -DA:105,2 -DA:107,0 -DA:112,2 -DA:118,1 -DA:119,2 -DA:123,2 -DA:124,2 -DA:125,1 -DA:127,1 -DA:128,1 -DA:136,1 -DA:137,2 -DA:138,2 -DA:141,2 -DA:142,2 -DA:146,2 -DA:152,1 -DA:153,1 -DA:158,1 -LF:66 -LH:62 -BRDA:27,0,0,1 -BRDA:27,0,1,1 -BRDA:27,1,0,2 -BRDA:27,1,1,1 -BRDA:27,1,2,0 -BRDA:49,2,0,5 -BRDA:49,2,1,5 -BRDA:68,3,0,2 -BRDA:68,3,1,2 -BRDA:68,3,2,2 -BRDA:68,3,3,2 -BRDA:68,3,4,2 -BRDA:68,3,5,0 -BRDA:91,4,0,2 -BRDA:91,4,1,0 -BRDA:98,5,0,0 -BRDA:98,5,1,2 -BRDA:98,6,0,2 -BRDA:98,6,1,0 -BRF:19 -BRH:14 -end_of_record