diff --git a/tests/aws/cdk_templates/Bookstore/BookstoreStack.json b/tests/aws/cdk_templates/Bookstore/BookstoreStack.json index 9bc23ec0dc0a6..fb8d7a7512c76 100644 --- a/tests/aws/cdk_templates/Bookstore/BookstoreStack.json +++ b/tests/aws/cdk_templates/Bookstore/BookstoreStack.json @@ -1,87 +1,6 @@ { "Resources": { - "Domain66AC69E0": { - "Type": "AWS::OpenSearchService::Domain", - "Properties": { - "AdvancedOptions": { - "rest.action.multi.allow_explicit_index": "false" - }, - "ClusterConfig": { - "DedicatedMasterEnabled": false, - "InstanceCount": 1, - "InstanceType": "r5.large.search", - "ZoneAwarenessEnabled": false - }, - "DomainEndpointOptions": { - "EnforceHTTPS": false, - "TLSSecurityPolicy": "Policy-Min-TLS-1-0-2019-07" - }, - "EBSOptions": { - "EBSEnabled": true, - "VolumeSize": 10, - "VolumeType": "gp2" - }, - "EncryptionAtRestOptions": { - "Enabled": false - }, - "EngineVersion": "OpenSearch_2.5", - "LogPublishingOptions": {}, - "NodeToNodeEncryptionOptions": { - "Enabled": false - } - }, - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "BooksTable9DF4AE31": { - "Type": "AWS::DynamoDB::Table", - "Properties": { - "AttributeDefinitions": [ - { - "AttributeName": "id", - "AttributeType": "S" - }, - { - "AttributeName": "category", - "AttributeType": "S" - } - ], - "GlobalSecondaryIndexes": [ - { - "IndexName": "category-index", - "KeySchema": [ - { - "AttributeName": "category", - "KeyType": "HASH" - } - ], - "Projection": { - "ProjectionType": "ALL" - }, - "ProvisionedThroughput": { - "ReadCapacityUnits": 1, - "WriteCapacityUnits": 1 - } - } - ], - "KeySchema": [ - { - "AttributeName": "id", - "KeyType": "HASH" - } - ], - "ProvisionedThroughput": { - "ReadCapacityUnits": 5, - "WriteCapacityUnits": 5 - }, - "StreamSpecification": { - "StreamViewType": "NEW_AND_OLD_IMAGES" - } - }, - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" - }, - "LoadBooksLambdaServiceRole5B377AF8": { + "BooksApiLambdaRole6305A178": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { @@ -105,18 +24,49 @@ { "Ref": "AWS::Partition" }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ":iam::aws:policy/AmazonS3FullAccess" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/AmazonDynamoDBFullAccess" ] ] } ] } }, - "LoadBooksLambdaServiceRoleDefaultPolicy3AB4051A": { + "BooksApiLambdaRoleDefaultPolicyCB8FFCFD": { "Type": "AWS::IAM::Policy", "Properties": { "PolicyDocument": { "Statement": [ + { + "Action": "dynamodb:ListStreams", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "dynamodb:DescribeStream", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "BooksTable9DF4AE31", + "StreamArn" + ] + } + }, { "Action": [ "dynamodb:BatchWriteItem", @@ -148,104 +98,7 @@ ] } ] - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "LoadBooksLambdaServiceRoleDefaultPolicy3AB4051A", - "Roles": [ - { - "Ref": "LoadBooksLambdaServiceRole5B377AF8" - } - ] - } - }, - "LoadBooksLambda89A2744D": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "ZipFile": "// source: https://github.com/aws-samples/aws-bookstore-demo-app/blob/master/functions/setup/uploadBooks.js\n\"use strict\";\n\nconst https = require(\"https\");\nconst url = require(\"url\");\n\nvar AWS = require(\"aws-sdk\");\nlet documentClient;\nlet s3Client;\nif (process.env.AWS_ENDPOINT_URL) {\n const localStackS3Config = {\n endpoint: process.env.AWS_ENDPOINT_URL,\n s3ForcePathStyle: true,\n accessKeyId: 'test',\n secretAccessKey: 'test',\n region: 'us-east-1',\n };\n s3Client = new AWS.S3(localStackS3Config);\n\n documentClient = new AWS.DynamoDB.DocumentClient({\n endpoint: process.env.AWS_ENDPOINT_URL,\n region: 'us-east-1', // Change the region as per your setup\n }\n );\n} else {\n // Use the default AWS configuration\n s3Client = new AWS.S3();\n documentClient = new AWS.DynamoDB.DocumentClient();\n}\n\n// UploadBooks - Upload sample set of books to DynamoDB\nexports.handler = function(event, context, callback) {\n getBooksData().then(function(data) {\n var booksString = data.Body.toString(\"utf-8\");\n console.log(\"received booksString\");\n var booksList = JSON.parse(booksString);\n console.log(\"parsing bookslist\");\n uploadBooksData(booksList);\n console.log(\"uploaded books\");\n }).catch(function(err) {\n console.log(err);\n var responseData = { Error: \"Upload books failed\" };\n console.log(responseData.Error);\n });\n\n return;\n};\nfunction uploadBooksData(book_items) {\n var items_array = [];\n for (var i in book_items) {\n var book = book_items[i];\n console.log(book.id)\n var item = {\n PutRequest: {\n Item: book\n }\n };\n items_array.push(item);\n }\n\n // Batch items into arrays of 25 for BatchWriteItem limit\n var split_arrays = [], size = 25;\n while (items_array.length > 0) {\n split_arrays.push(items_array.splice(0, size));\n }\n\n split_arrays.forEach( function(item_data) {\n putItem(item_data)\n });\n}\n\n// Retrieve sample books from aws-bookstore-demo S3 Bucket\nfunction getBooksData() {\n var params = {\n Bucket: process.env.S3_BUCKET, // aws-bookstore-demo\n Key: process.env.FILE_NAME // data/books.json\n };\n return s3Client.getObject(params).promise();\n}\n\n\nfunction putItem(items_array) {\n var tableName = process.env.TABLE_NAME;\n var params = {\n RequestItems: {\n [tableName]: items_array\n }\n };\n documentClient.batchWrite(params, function(err, data) {\n if (err) console.log(err);\n else console.log(data);\n });\n}\n" - }, - "Environment": { - "Variables": { - "TABLE_NAME": { - "Ref": "BooksTable9DF4AE31" }, - "S3_BUCKET": "book-init-data-store-scenario-test", - "FILE_NAME": "books.json" - } - }, - "Handler": "index.handler", - "Role": { - "Fn::GetAtt": [ - "LoadBooksLambdaServiceRole5B377AF8", - "Arn" - ] - }, - "Runtime": "nodejs16.x" - }, - "DependsOn": [ - "LoadBooksLambdaServiceRoleDefaultPolicy3AB4051A", - "LoadBooksLambdaServiceRole5B377AF8" - ] - }, - "BooksS3Policy90EAFBF2": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": "s3:GetObject", - "Effect": "Allow", - "Resource": "arn:aws:s3:::*/*" - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "BooksS3Policy90EAFBF2", - "Roles": [ - { - "Ref": "LoadBooksLambdaServiceRole5B377AF8" - } - ] - } - }, - "GetBookLambdaServiceRole2A5B6F70": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "ManagedPolicyArns": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] - ] - } - ] - } - }, - "GetBookLambdaServiceRoleDefaultPolicy0B493826": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ { "Action": [ "dynamodb:BatchGetItem", @@ -280,23 +133,166 @@ ] } ] + }, + { + "Action": [ + "es:ESHttpGet", + "es:ESHttpHead", + "es:ESHttpDelete", + "es:ESHttpPost", + "es:ESHttpPut", + "es:ESHttpPatch" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "Domain66AC69E0", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "Domain66AC69E0", + "Arn" + ] + }, + "/*" + ] + ] + } + ] } ], "Version": "2012-10-17" }, - "PolicyName": "GetBookLambdaServiceRoleDefaultPolicy0B493826", + "PolicyName": "BooksApiLambdaRoleDefaultPolicyCB8FFCFD", "Roles": [ { - "Ref": "GetBookLambdaServiceRole2A5B6F70" + "Ref": "BooksApiLambdaRole6305A178" } ] } }, + "Domain66AC69E0": { + "Type": "AWS::OpenSearchService::Domain", + "Properties": { + "AdvancedOptions": { + "rest.action.multi.allow_explicit_index": "false" + }, + "ClusterConfig": { + "DedicatedMasterEnabled": false, + "InstanceCount": 1, + "InstanceType": "r5.large.search", + "ZoneAwarenessEnabled": false + }, + "DomainEndpointOptions": { + "EnforceHTTPS": false, + "TLSSecurityPolicy": "Policy-Min-TLS-1-0-2019-07" + }, + "EBSOptions": { + "EBSEnabled": true, + "VolumeSize": 10, + "VolumeType": "gp2" + }, + "EncryptionAtRestOptions": { + "Enabled": false + }, + "EngineVersion": "OpenSearch_2.5", + "LogPublishingOptions": {}, + "NodeToNodeEncryptionOptions": { + "Enabled": false + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "BooksTable9DF4AE31": { + "Type": "AWS::DynamoDB::Table", + "Properties": { + "AttributeDefinitions": [ + { + "AttributeName": "id", + "AttributeType": "S" + }, + { + "AttributeName": "category", + "AttributeType": "S" + } + ], + "GlobalSecondaryIndexes": [ + { + "IndexName": "category-index", + "KeySchema": [ + { + "AttributeName": "category", + "KeyType": "HASH" + } + ], + "Projection": { + "ProjectionType": "ALL" + }, + "ProvisionedThroughput": { + "ReadCapacityUnits": 1, + "WriteCapacityUnits": 1 + } + } + ], + "KeySchema": [ + { + "AttributeName": "id", + "KeyType": "HASH" + } + ], + "ProvisionedThroughput": { + "ReadCapacityUnits": 5, + "WriteCapacityUnits": 5 + }, + "StreamSpecification": { + "StreamViewType": "NEW_AND_OLD_IMAGES" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "LoadBooksLambda89A2744D": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "// source: https://github.com/aws-samples/aws-bookstore-demo-app/blob/master/functions/setup/uploadBooks.js\n\n\"use strict\";\n\nconst https = require(\"https\");\nconst url = require(\"url\");\nconst AWS = require(\"aws-sdk\");\n\nvar config = {\n 's3ForcePathStyle': true,\n};\nif (process.env.AWS_ENDPOINT_URL) {\n config.endpoint = process.env.AWS_ENDPOINT_URL;\n}\n\nlet documentClient = new AWS.DynamoDB.DocumentClient(config);\nlet s3Client = new AWS.S3(config);\n\n// UploadBooks - Upload sample set of books to DynamoDB\nexports.handler = function(event, context, callback) {\n getBooksData().then(function(data) {\n var booksString = data.Body.toString(\"utf-8\");\n console.log(\"received booksString\");\n var booksList = JSON.parse(booksString);\n console.log(\"parsing bookslist\");\n uploadBooksData(booksList);\n console.log(\"uploaded books\");\n }).catch(function(err) {\n console.log(err);\n var responseData = { Error: \"Upload books failed\" };\n console.log(responseData.Error);\n });\n\n return;\n};\nfunction uploadBooksData(book_items) {\n var items_array = [];\n for (var i in book_items) {\n var book = book_items[i];\n console.log(book.id)\n var item = {\n PutRequest: {\n Item: book\n }\n };\n items_array.push(item);\n }\n\n // Batch items into arrays of 25 for BatchWriteItem limit\n var split_arrays = [], size = 25;\n while (items_array.length > 0) {\n split_arrays.push(items_array.splice(0, size));\n }\n\n split_arrays.forEach( function(item_data) {\n putItem(item_data)\n });\n}\n\n// Retrieve sample books from aws-bookstore-demo S3 Bucket\nfunction getBooksData() {\n var params = {\n Bucket: process.env.S3_BUCKET, // aws-bookstore-demo\n Key: process.env.FILE_NAME // data/books.json\n };\n return s3Client.getObject(params).promise();\n}\n\n\nfunction putItem(items_array) {\n var tableName = process.env.TABLE_NAME;\n var params = {\n RequestItems: {\n [tableName]: items_array\n }\n };\n documentClient.batchWrite(params, function(err, data) {\n if (err) console.log(err);\n else console.log(data);\n });\n}\n" + }, + "Environment": { + "Variables": { + "TABLE_NAME": { + "Ref": "BooksTable9DF4AE31" + }, + "S3_BUCKET": "book-init-data-store-scenario-test", + "FILE_NAME": "books.json" + } + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "BooksApiLambdaRole6305A178", + "Arn" + ] + }, + "Runtime": "nodejs16.x" + }, + "DependsOn": [ + "BooksApiLambdaRoleDefaultPolicyCB8FFCFD", + "BooksApiLambdaRole6305A178" + ] + }, "GetBookLambda6188143D": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "ZipFile": "// source adapted from https://github.com/aws-samples/aws-bookstore-demo-app\n\n\"use strict\";\n\nconst AWS = require(\"aws-sdk\");\nlet dynamoDb;\nif (process.env.AWS_ENDPOINT_URL) {\n dynamoDb = new AWS.DynamoDB.DocumentClient({\n endpoint: process.env.AWS_ENDPOINT_URL,\n region: 'us-east-1', // Change the region as per your setup\n }\n );\n} else {\n dynamoDb = new AWS.DynamoDB.DocumentClient();\n}\n// GetBook - Get book informaton for a given book id\nexports.handler = (event, context, callback) => {\n\n // Return immediately if being called by warmer\n if (event.source === \"warmer\") {\n return callback(null, \"Lambda is warm\");\n }\n\n const params = {\n TableName: process.env.TABLE_NAME, // [ProjectName]-Books\n // 'Key' defines the partition key of the item to be retrieved\n // - 'id': a unique identifier for the book (uuid)\n Key: {\n id: event.pathParameters.id\n }\n };\n dynamoDb.get(params, (error, data) => {\n // Set response headers to enable CORS (Cross-Origin Resource Sharing)\n const headers = {\n \"Access-Control-Allow-Origin\": \"*\",\n \"Access-Control-Allow-Credentials\" : true\n };\n\n // Return status code 500 on error\n if (error) {\n const response = {\n statusCode: 500,\n headers: headers,\n body: error\n };\n callback(null, response);\n return;\n }\n\n // Return status code 200 and the retrieved item on success\n const response = {\n statusCode: 200,\n headers: headers,\n body: JSON.stringify(data.Item)\n };\n callback(null, response);\n });\n}\n" + "ZipFile": "// source adapted from https://github.com/aws-samples/aws-bookstore-demo-app\n\n\"use strict\";\n\nconst AWS = require(\"aws-sdk\");\n\nvar config = {};\nif (process.env.AWS_ENDPOINT_URL) {\n config.endpoint = process.env.AWS_ENDPOINT_URL;\n}\n\nlet dynamoDb = new AWS.DynamoDB.DocumentClient(config);\n\n// GetBook - Get book informaton for a given book id\nexports.handler = (event, context, callback) => {\n\n // Return immediately if being called by warmer\n if (event.source === \"warmer\") {\n return callback(null, \"Lambda is warm\");\n }\n\n const params = {\n TableName: process.env.TABLE_NAME, // [ProjectName]-Books\n // 'Key' defines the partition key of the item to be retrieved\n // - 'id': a unique identifier for the book (uuid)\n Key: {\n id: event.pathParameters.id\n }\n };\n dynamoDb.get(params, (error, data) => {\n // Set response headers to enable CORS (Cross-Origin Resource Sharing)\n const headers = {\n \"Access-Control-Allow-Origin\": \"*\",\n \"Access-Control-Allow-Credentials\" : true\n };\n\n // Return status code 500 on error\n if (error) {\n const response = {\n statusCode: 500,\n headers: headers,\n body: error\n };\n callback(null, response);\n return;\n }\n\n // Return status code 200 and the retrieved item on success\n const response = {\n statusCode: 200,\n headers: headers,\n body: JSON.stringify(data.Item)\n };\n callback(null, response);\n });\n}\n" }, "Environment": { "Variables": { @@ -308,104 +304,22 @@ "Handler": "index.handler", "Role": { "Fn::GetAtt": [ - "GetBookLambdaServiceRole2A5B6F70", + "BooksApiLambdaRole6305A178", "Arn" ] }, "Runtime": "nodejs16.x" }, "DependsOn": [ - "GetBookLambdaServiceRoleDefaultPolicy0B493826", - "GetBookLambdaServiceRole2A5B6F70" + "BooksApiLambdaRoleDefaultPolicyCB8FFCFD", + "BooksApiLambdaRole6305A178" ] }, - "ListBooksLambdaServiceRoleB3A92402": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "ManagedPolicyArns": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] - ] - } - ] - } - }, - "ListBooksLambdaServiceRoleDefaultPolicyE7CEFA1B": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "dynamodb:BatchGetItem", - "dynamodb:GetRecords", - "dynamodb:GetShardIterator", - "dynamodb:Query", - "dynamodb:GetItem", - "dynamodb:Scan", - "dynamodb:ConditionCheckItem", - "dynamodb:DescribeTable" - ], - "Effect": "Allow", - "Resource": [ - { - "Fn::GetAtt": [ - "BooksTable9DF4AE31", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "BooksTable9DF4AE31", - "Arn" - ] - }, - "/index/*" - ] - ] - } - ] - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "ListBooksLambdaServiceRoleDefaultPolicyE7CEFA1B", - "Roles": [ - { - "Ref": "ListBooksLambdaServiceRoleB3A92402" - } - ] - } - }, "ListBooksLambda272803E4": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "ZipFile": "// source adapted from https://github.com/aws-samples/aws-bookstore-demo-app\n\n\"use strict\";\n\nconst AWS = require(\"aws-sdk\");\nlet dynamoDb;\nif (process.env.AWS_ENDPOINT_URL) {\n dynamoDb = new AWS.DynamoDB.DocumentClient({\n endpoint: process.env.AWS_ENDPOINT_URL,\n region: 'us-east-1', // Change the region as per your setup\n }\n );\n} else {\n dynamoDb = new AWS.DynamoDB.DocumentClient();\n}\n// ListBooks - List all books or list all books in a particular category\nexports.handler = (event, context, callback) => {\n\n // Return immediately if being called by warmer\n if (event.source === \"warmer\") {\n return callback(null, \"Lambda is warm\");\n }\n\n // Set response headers to enable CORS (Cross-Origin Resource Sharing)\n const headers = {\n \"Access-Control-Allow-Origin\": \"*\",\n \"Access-Control-Allow-Credentials\" : true\n };\n\n // Query books for a particular category\n if (event.queryStringParameters) {\n const params = {\n TableName: process.env.TABLE_NAME, // [ProjectName]-Books\n IndexName: \"category-index\",\n // 'KeyConditionExpression' defines the condition for the query\n // - 'category = :category': only return items with matching 'category' index\n // 'ExpressionAttributeValues' defines the value in the condition\n // - ':category': defines 'category' to be the query string parameter\n KeyConditionExpression: \"category = :category\",\n ExpressionAttributeValues: {\n \":category\": event.queryStringParameters.category\n }\n };\n dynamoDb.query(params, (error, data) => {\n // Return status code 500 on error\n if (error) {\n const response = {\n statusCode: 500,\n headers: headers,\n body: error\n };\n callback(null, response);\n return;\n }\n\n // Return status code 200 and the retrieved items on success\n const response = {\n statusCode: 200,\n headers: headers,\n body: JSON.stringify(data.Items)\n };\n callback(null, response);\n });\n }\n\n // List all books in bookstore\n else {\n const params = {\n TableName: process.env.TABLE_NAME // [ProjectName]-Books\n };\n\n dynamoDb.scan(params, (error, data) => {\n // Return status code 500 on error\n if (error) {\n const response = {\n statusCode: 500,\n headers: headers,\n body: error\n };\n callback(null, response);\n return;\n }\n\n // Return status code 200 and the retrieved items on success\n const response = {\n statusCode: 200,\n headers: headers,\n body: JSON.stringify(data.Items)\n };\n callback(null, response);\n });\n }\n}\n" + "ZipFile": "// source adapted from https://github.com/aws-samples/aws-bookstore-demo-app\n\n\"use strict\";\n\nconst AWS = require(\"aws-sdk\");\n\nvar config = {};\nif (process.env.AWS_ENDPOINT_URL) {\n config.endpoint = process.env.AWS_ENDPOINT_URL;\n}\n\nlet dynamoDb = new AWS.DynamoDB.DocumentClient(config);\n\n// ListBooks - List all books or list all books in a particular category\nexports.handler = (event, context, callback) => {\n\n // Return immediately if being called by warmer\n if (event.source === \"warmer\") {\n return callback(null, \"Lambda is warm\");\n }\n\n // Set response headers to enable CORS (Cross-Origin Resource Sharing)\n const headers = {\n \"Access-Control-Allow-Origin\": \"*\",\n \"Access-Control-Allow-Credentials\" : true\n };\n\n // Query books for a particular category\n if (event.queryStringParameters) {\n const params = {\n TableName: process.env.TABLE_NAME, // [ProjectName]-Books\n IndexName: \"category-index\",\n // 'KeyConditionExpression' defines the condition for the query\n // - 'category = :category': only return items with matching 'category' index\n // 'ExpressionAttributeValues' defines the value in the condition\n // - ':category': defines 'category' to be the query string parameter\n KeyConditionExpression: \"category = :category\",\n ExpressionAttributeValues: {\n \":category\": event.queryStringParameters.category\n }\n };\n dynamoDb.query(params, (error, data) => {\n // Return status code 500 on error\n if (error) {\n const response = {\n statusCode: 500,\n headers: headers,\n body: error\n };\n callback(null, response);\n return;\n }\n\n // Return status code 200 and the retrieved items on success\n const response = {\n statusCode: 200,\n headers: headers,\n body: JSON.stringify(data.Items)\n };\n callback(null, response);\n });\n }\n\n // List all books in bookstore\n else {\n const params = {\n TableName: process.env.TABLE_NAME // [ProjectName]-Books\n };\n\n dynamoDb.scan(params, (error, data) => {\n // Return status code 500 on error\n if (error) {\n const response = {\n statusCode: 500,\n headers: headers,\n body: error\n };\n callback(null, response);\n return;\n }\n\n // Return status code 200 and the retrieved items on success\n const response = {\n statusCode: 200,\n headers: headers,\n body: JSON.stringify(data.Items)\n };\n callback(null, response);\n });\n }\n}\n" }, "Environment": { "Variables": { @@ -417,97 +331,17 @@ "Handler": "index.handler", "Role": { "Fn::GetAtt": [ - "ListBooksLambdaServiceRoleB3A92402", + "BooksApiLambdaRole6305A178", "Arn" ] }, "Runtime": "nodejs16.x" }, "DependsOn": [ - "ListBooksLambdaServiceRoleDefaultPolicyE7CEFA1B", - "ListBooksLambdaServiceRoleB3A92402" + "BooksApiLambdaRoleDefaultPolicyCB8FFCFD", + "BooksApiLambdaRole6305A178" ] }, - "SearchBookLambdaServiceRole5AD030B7": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "ManagedPolicyArns": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] - ] - } - ] - } - }, - "SearchBookLambdaServiceRoleDefaultPolicy68B2E5F3": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "es:ESHttpGet", - "es:ESHttpHead", - "es:ESHttpDelete", - "es:ESHttpPost", - "es:ESHttpPut", - "es:ESHttpPatch" - ], - "Effect": "Allow", - "Resource": [ - { - "Fn::GetAtt": [ - "Domain66AC69E0", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "Domain66AC69E0", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "SearchBookLambdaServiceRoleDefaultPolicy68B2E5F3", - "Roles": [ - { - "Ref": "SearchBookLambdaServiceRole5AD030B7" - } - ] - } - }, "SearchBookLambdaC4A03CAC": { "Type": "AWS::Lambda::Function", "Properties": { @@ -535,125 +369,23 @@ "Domain66AC69E0", "DomainEndpoint" ] - }, - "REGION": { - "Ref": "AWS::Region" } } }, "Handler": "index.handler", "Role": { "Fn::GetAtt": [ - "SearchBookLambdaServiceRole5AD030B7", + "BooksApiLambdaRole6305A178", "Arn" ] }, "Runtime": "python3.10" }, "DependsOn": [ - "SearchBookLambdaServiceRoleDefaultPolicy68B2E5F3", - "SearchBookLambdaServiceRole5AD030B7" + "BooksApiLambdaRoleDefaultPolicyCB8FFCFD", + "BooksApiLambdaRole6305A178" ] }, - "UpdateSearchLambdaServiceRole50DCABB8": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "ManagedPolicyArns": [ - { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" - ] - ] - } - ] - } - }, - "UpdateSearchLambdaServiceRoleDefaultPolicy3F121BF7": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": "dynamodb:ListStreams", - "Effect": "Allow", - "Resource": "*" - }, - { - "Action": [ - "dynamodb:DescribeStream", - "dynamodb:GetRecords", - "dynamodb:GetShardIterator" - ], - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "BooksTable9DF4AE31", - "StreamArn" - ] - } - }, - { - "Action": [ - "es:ESHttpGet", - "es:ESHttpHead", - "es:ESHttpDelete", - "es:ESHttpPost", - "es:ESHttpPut", - "es:ESHttpPatch" - ], - "Effect": "Allow", - "Resource": [ - { - "Fn::GetAtt": [ - "Domain66AC69E0", - "Arn" - ] - }, - { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "Domain66AC69E0", - "Arn" - ] - }, - "/*" - ] - ] - } - ] - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "UpdateSearchLambdaServiceRoleDefaultPolicy3F121BF7", - "Roles": [ - { - "Ref": "UpdateSearchLambdaServiceRole50DCABB8" - } - ] - } - }, "UpdateSearchLambda87F5CBC4": { "Type": "AWS::Lambda::Function", "Properties": { @@ -681,24 +413,21 @@ "Domain66AC69E0", "DomainEndpoint" ] - }, - "REGION": { - "Ref": "AWS::Region" } } }, "Handler": "index.handler", "Role": { "Fn::GetAtt": [ - "UpdateSearchLambdaServiceRole50DCABB8", + "BooksApiLambdaRole6305A178", "Arn" ] }, "Runtime": "python3.10" }, "DependsOn": [ - "UpdateSearchLambdaServiceRoleDefaultPolicy3F121BF7", - "UpdateSearchLambdaServiceRole50DCABB8" + "BooksApiLambdaRoleDefaultPolicyCB8FFCFD", + "BooksApiLambdaRole6305A178" ] }, "UpdateSearchLambdaDynamoDBEventSourceBookstoreStackBooksTable5CD0B9B677451E3B": { diff --git a/tests/aws/scenario/bookstore/functions/getBook.js b/tests/aws/scenario/bookstore/functions/getBook.js index ec74d2a3d9c75..b84fd50f4f98f 100644 --- a/tests/aws/scenario/bookstore/functions/getBook.js +++ b/tests/aws/scenario/bookstore/functions/getBook.js @@ -3,16 +3,14 @@ "use strict"; const AWS = require("aws-sdk"); -let dynamoDb; + +var config = {}; if (process.env.AWS_ENDPOINT_URL) { - dynamoDb = new AWS.DynamoDB.DocumentClient({ - endpoint: process.env.AWS_ENDPOINT_URL, - region: 'us-east-1', // Change the region as per your setup - } - ); -} else { - dynamoDb = new AWS.DynamoDB.DocumentClient(); + config.endpoint = process.env.AWS_ENDPOINT_URL; } + +let dynamoDb = new AWS.DynamoDB.DocumentClient(config); + // GetBook - Get book informaton for a given book id exports.handler = (event, context, callback) => { diff --git a/tests/aws/scenario/bookstore/functions/listBooks.js b/tests/aws/scenario/bookstore/functions/listBooks.js index 932a965602b83..5321612388ea6 100644 --- a/tests/aws/scenario/bookstore/functions/listBooks.js +++ b/tests/aws/scenario/bookstore/functions/listBooks.js @@ -3,16 +3,14 @@ "use strict"; const AWS = require("aws-sdk"); -let dynamoDb; + +var config = {}; if (process.env.AWS_ENDPOINT_URL) { - dynamoDb = new AWS.DynamoDB.DocumentClient({ - endpoint: process.env.AWS_ENDPOINT_URL, - region: 'us-east-1', // Change the region as per your setup - } - ); -} else { - dynamoDb = new AWS.DynamoDB.DocumentClient(); + config.endpoint = process.env.AWS_ENDPOINT_URL; } + +let dynamoDb = new AWS.DynamoDB.DocumentClient(config); + // ListBooks - List all books or list all books in a particular category exports.handler = (event, context, callback) => { diff --git a/tests/aws/scenario/bookstore/functions/loadBooksHelper.js b/tests/aws/scenario/bookstore/functions/loadBooksHelper.js index cd185083f80f4..98bb6d485c8ed 100644 --- a/tests/aws/scenario/bookstore/functions/loadBooksHelper.js +++ b/tests/aws/scenario/bookstore/functions/loadBooksHelper.js @@ -1,33 +1,21 @@ // source: https://github.com/aws-samples/aws-bookstore-demo-app/blob/master/functions/setup/uploadBooks.js + "use strict"; const https = require("https"); const url = require("url"); +const AWS = require("aws-sdk"); -var AWS = require("aws-sdk"); -let documentClient; -let s3Client; +var config = { + 's3ForcePathStyle': true, +}; if (process.env.AWS_ENDPOINT_URL) { - const localStackS3Config = { - endpoint: process.env.AWS_ENDPOINT_URL, - s3ForcePathStyle: true, - accessKeyId: 'test', - secretAccessKey: 'test', - region: 'us-east-1', - }; - s3Client = new AWS.S3(localStackS3Config); - - documentClient = new AWS.DynamoDB.DocumentClient({ - endpoint: process.env.AWS_ENDPOINT_URL, - region: 'us-east-1', // Change the region as per your setup - } - ); -} else { - // Use the default AWS configuration - s3Client = new AWS.S3(); - documentClient = new AWS.DynamoDB.DocumentClient(); + config.endpoint = process.env.AWS_ENDPOINT_URL; } +let documentClient = new AWS.DynamoDB.DocumentClient(config); +let s3Client = new AWS.S3(config); + // UploadBooks - Upload sample set of books to DynamoDB exports.handler = function(event, context, callback) { getBooksData().then(function(data) { diff --git a/tests/aws/scenario/bookstore/functions/search.py b/tests/aws/scenario/bookstore/functions/search.py index 4b47dccaab1f7..8ab02291d7aaf 100644 --- a/tests/aws/scenario/bookstore/functions/search.py +++ b/tests/aws/scenario/bookstore/functions/search.py @@ -6,7 +6,7 @@ import requests from requests_aws4auth import AWS4Auth -region = os.environ["REGION"] +region = os.environ["AWS_REGION"] service = "es" credentials = boto3.Session().get_credentials() awsauth = AWS4Auth( diff --git a/tests/aws/scenario/bookstore/functions/update_search_cluster.py b/tests/aws/scenario/bookstore/functions/update_search_cluster.py index a504e72fc3b63..923d09bc556a1 100644 --- a/tests/aws/scenario/bookstore/functions/update_search_cluster.py +++ b/tests/aws/scenario/bookstore/functions/update_search_cluster.py @@ -6,7 +6,7 @@ import requests from requests_aws4auth import AWS4Auth -region = os.environ["REGION"] +region = os.environ["AWS_REGION"] service = "es" credentials = boto3.Session().get_credentials() awsauth = AWS4Auth( diff --git a/tests/aws/scenario/bookstore/test_bookstore.py b/tests/aws/scenario/bookstore/test_bookstore.py index b2829bb7d98e9..af0eb0c8af092 100644 --- a/tests/aws/scenario/bookstore/test_bookstore.py +++ b/tests/aws/scenario/bookstore/test_bookstore.py @@ -10,12 +10,14 @@ import aws_cdk.aws_opensearchservice as opensearch import pytest from aws_cdk.aws_lambda_event_sources import DynamoEventSource +from botocore.exceptions import ClientError from constructs import Construct from localstack.testing.pytest import markers from localstack.testing.scenario.cdk_lambda_helper import load_python_lambda_to_s3 from localstack.testing.scenario.provisioning import InfraProvisioner, cleanup_s3_bucket from localstack.testing.snapshots.transformer import GenericTransformer, KeyValueBasedTransformer +from localstack.utils.aws.resources import create_s3_bucket from localstack.utils.files import load_file from localstack.utils.strings import to_bytes, to_str from localstack.utils.sync import retry @@ -115,7 +117,11 @@ def test_setup(self, aws_client, infrastructure, snapshot, cleanups): # pre-fill dynamodb # json-data is from https://aws-bookstore-demo.s3.amazonaws.com/data/books.json - aws_client.s3.create_bucket(Bucket=S3_BUCKET_BOOKS_INIT) + try: + create_s3_bucket(bucket_name=S3_BUCKET_BOOKS_INIT, s3_client=aws_client.s3) + except ClientError as exc: + if exc.response["Error"]["Code"] != "BucketAlreadyOwnedByYou": + raise exc cleanups.append( lambda: cleanup_s3_bucket( aws_client.s3, bucket_name=S3_BUCKET_BOOKS_INIT, delete_bucket=True @@ -410,6 +416,16 @@ def __init__( projection_type=dynamodb.ProjectionType.ALL, ) + self.lambda_role = iam.Role( + self, "LambdaRole", assumed_by=iam.ServicePrincipal("lambda.amazonaws.com") + ) + self.lambda_role.add_managed_policy( + iam.ManagedPolicy.from_aws_managed_policy_name("AmazonS3FullAccess") + ) + self.lambda_role.add_managed_policy( + iam.ManagedPolicy.from_aws_managed_policy_name("AmazonDynamoDBFullAccess") + ) + # lambda for pre-filling the dynamodb self.load_books_helper_fn = awslambda.Function( stack, @@ -422,16 +438,9 @@ def __init__( "S3_BUCKET": S3_BUCKET_BOOKS_INIT, "FILE_NAME": S3_KEY_BOOKS_INIT, }, + role=self.lambda_role, ) - self.load_books_helper_fn.role.attach_inline_policy( - iam.Policy( - stack, - "BooksS3Policy", - statements=[ - iam.PolicyStatement(resources=["arn:aws:s3:::*/*"], actions=["s3:GetObject"]) - ], - ) - ) + # lambdas to get and list books self.get_book_fn = awslambda.Function( stack, @@ -439,7 +448,10 @@ def __init__( handler="index.handler", code=awslambda.InlineCode(code=load_file(self.GET_BOOK_PATH)), runtime=awslambda.Runtime.NODEJS_16_X, - environment={"TABLE_NAME": self.books_table.table_name}, + environment={ + "TABLE_NAME": self.books_table.table_name, + }, + role=self.lambda_role, ) self.list_books_fn = awslambda.Function( @@ -448,7 +460,10 @@ def __init__( handler="index.handler", code=awslambda.InlineCode(code=load_file(self.LIST_BOOKS_PATH)), runtime=awslambda.Runtime.NODEJS_16_X, - environment={"TABLE_NAME": self.books_table.table_name}, + environment={ + "TABLE_NAME": self.books_table.table_name, + }, + role=self.lambda_role, ) # lambda to search for book @@ -465,8 +480,8 @@ def __init__( runtime=awslambda.Runtime.PYTHON_3_10, environment={ "ESENDPOINT": self.opensearch_domain.domain_endpoint, - "REGION": stack.region, }, + role=self.lambda_role, ) # lambda to update search cluster @@ -478,8 +493,8 @@ def __init__( runtime=awslambda.Runtime.PYTHON_3_10, environment={ "ESENDPOINT": self.opensearch_domain.domain_endpoint, - "REGION": stack.region, }, + role=self.lambda_role, ) event_source = DynamoEventSource( diff --git a/tests/aws/scenario/bookstore/test_bookstore.validation.json b/tests/aws/scenario/bookstore/test_bookstore.validation.json index f7e53321db70d..24091929b8acf 100644 --- a/tests/aws/scenario/bookstore/test_bookstore.validation.json +++ b/tests/aws/scenario/bookstore/test_bookstore.validation.json @@ -1,14 +1,14 @@ { "tests/aws/scenario/bookstore/test_bookstore.py::TestBookstoreApplication::test_lambda_dynamodb": { - "last_validated_date": "2023-08-30T11:23:16+00:00" + "last_validated_date": "2024-01-31T13:47:03+00:00" }, "tests/aws/scenario/bookstore/test_bookstore.py::TestBookstoreApplication::test_opensearch_crud": { "last_validated_date": "2023-09-07T10:02:59+00:00" }, "tests/aws/scenario/bookstore/test_bookstore.py::TestBookstoreApplication::test_search_books": { - "last_validated_date": "2023-08-31T11:39:41+00:00" + "last_validated_date": "2024-01-31T13:47:48+00:00" }, "tests/aws/scenario/bookstore/test_bookstore.py::TestBookstoreApplication::test_setup": { - "last_validated_date": "2023-08-29T15:20:44+00:00" + "last_validated_date": "2024-01-31T13:46:54+00:00" } }