diff --git a/src/main/java/org/sagebionetworks/template/Constants.java b/src/main/java/org/sagebionetworks/template/Constants.java index f4406ecd4..0e0a6816d 100644 --- a/src/main/java/org/sagebionetworks/template/Constants.java +++ b/src/main/java/org/sagebionetworks/template/Constants.java @@ -123,10 +123,14 @@ public class Constants { public static final String PROPERTY_KEY_RDS_REPO_SNAPSHOT_IDENTIFIER = "org.sagebionetworks.repo.snapshot.identifier"; public static final String PROPERTY_KEY_RDS_TABLES_SNAPSHOT_IDENTIFIERS = "org.sagebionetworks.tables.snapshot.identifiers"; + public static final String NOSNAPSHOT = "NOSNAPSHOT"; // value to indicate a snapshot is not used to init a stack + + public static final String PROPERTY_KEY_LAMBDA_ARTIFACT_BUCKET = "org.sagebionetworks.lambda.artifact.bucket"; public static final String PROPERTY_KEY_LAMBDA_VIRUS_SCANNER_ARTIFACT_URL = "org.sagebionetworks.lambda.virusscanner.artifactUrl"; public static final String PROPERTY_KEY_LAMBDA_MARKDOWNIT_ARTIFACT_URL = "org.sagebionetworks.lambda.markdownit.artifactUrl"; - public static final String PROPERTY_KEY_LAMBDA_ARTIFACT_BUCKET = "org.sagebionetworks.lambda.artifact.bucket"; - public static final String NOSNAPSHOT = "NOSNAPSHOT"; // value to indicate a snapshot is not used to init a stack + public static final String PROPERTY_KEY_LAMBDA_MARKDOWNIT_ARTIFACT_VERSION = "org.sagebionetworks.lambda.markdownit.artifact.version"; + public static final String PROPERTY_KEY_LAMBDA_MARKDOWNIT_VPC = "org.sagebionetworks.lambda.markdownit.vpc"; + public static final String PROPERTY_KEY_LAMBDA_MARKDOWNIT_SUBNETS = "org.sagebionetworks.lambda.markdownit.vpc.subnets"; public static final String PROPERTY_KEY_ENABLE_RDS_ENHANCED_MONITORING = "org.sagebionetworks.enable.rds.enhanced.monitoring"; diff --git a/src/main/java/org/sagebionetworks/template/markdownit/MarkDownItLambdaBuilderImpl.java b/src/main/java/org/sagebionetworks/template/markdownit/MarkDownItLambdaBuilderImpl.java index c63923c32..0fcfb3f27 100644 --- a/src/main/java/org/sagebionetworks/template/markdownit/MarkDownItLambdaBuilderImpl.java +++ b/src/main/java/org/sagebionetworks/template/markdownit/MarkDownItLambdaBuilderImpl.java @@ -24,6 +24,9 @@ import static org.sagebionetworks.template.Constants.CAPABILITY_NAMED_IAM; import static org.sagebionetworks.template.Constants.PROPERTY_KEY_LAMBDA_ARTIFACT_BUCKET; import static org.sagebionetworks.template.Constants.PROPERTY_KEY_LAMBDA_MARKDOWNIT_ARTIFACT_URL; +import static org.sagebionetworks.template.Constants.PROPERTY_KEY_LAMBDA_MARKDOWNIT_ARTIFACT_VERSION; +import static org.sagebionetworks.template.Constants.PROPERTY_KEY_LAMBDA_MARKDOWNIT_SUBNETS; +import static org.sagebionetworks.template.Constants.PROPERTY_KEY_LAMBDA_MARKDOWNIT_VPC; import static org.sagebionetworks.template.Constants.PROPERTY_KEY_STACK; public class MarkDownItLambdaBuilderImpl implements MarkDownItLambdaBuilder { @@ -61,10 +64,11 @@ public void buildMarkDownItLambda() { String stack = config.getProperty(PROPERTY_KEY_STACK); String artifactBucket = config.getProperty(PROPERTY_KEY_LAMBDA_ARTIFACT_BUCKET); - String lambdaSourceArtifactUrl = config.getProperty(PROPERTY_KEY_LAMBDA_MARKDOWNIT_ARTIFACT_URL); - String lambdaArtifactKey = String.format("artifacts/markdown-it/%s", FilenameUtils.getName(lambdaSourceArtifactUrl)); + String version = config.getProperty(PROPERTY_KEY_LAMBDA_MARKDOWNIT_ARTIFACT_VERSION); + String lambdaSourceArtifactUrl = getSourceArtifactUrl(version); + String lambdaArtifactKey = String.format("artifacts/markdown-it/%s/markdown-it.zip", version); - // Download from jfrog and upload to S3 + // Download from Github and upload to S3 File artifact = downloader.downloadFile(lambdaSourceArtifactUrl); try { s3Client.putObject(artifactBucket, lambdaArtifactKey, artifact); @@ -76,14 +80,26 @@ public void buildMarkDownItLambda() { } + private String getSourceArtifactUrl(String version) { + final String LAMBDA_SOURCE_ARTIFACT_URL_FORMAT = "https://github.com/Sage-Bionetworks/synapse-markdown-it-lambda/releases/download/%s/markdown-it.zip"; + String lambdaSourceArtifactUrl = String.format(LAMBDA_SOURCE_ARTIFACT_URL_FORMAT, version); + return lambdaSourceArtifactUrl; + } + private Optional buildMarkDownItLambdaStack(String stack, String artifactBucket, String artifactKey) { - String stackName = String.format("%s-markdown-it-function", stack); + String stackName = String.format("%s-markdown-it-function-direct", stack); + + String vpcId = config.getProperty(PROPERTY_KEY_LAMBDA_MARKDOWNIT_VPC); + String[] subnetIds = config.getCommaSeparatedProperty(PROPERTY_KEY_LAMBDA_MARKDOWNIT_SUBNETS); // Setup context VelocityContext context = new VelocityContext(); + context.put("stack", stack); context.put("lambdaArtifactBucket", artifactBucket); context.put("lambdaArtifactKey", artifactKey); + context.put("vpcId", vpcId); + context.put("subnetIds", subnetIds); // Generate template Template template = velocityEngine.getTemplate(Constants.TEMPLATE_MARKDOWNIT_API_VTP); diff --git a/src/main/resources/templates/markdownit/markdown-it-api.json.vtp b/src/main/resources/templates/markdownit/markdown-it-api.json.vtp index 17128c9dc..7cf3e9640 100644 --- a/src/main/resources/templates/markdownit/markdown-it-api.json.vtp +++ b/src/main/resources/templates/markdownit/markdown-it-api.json.vtp @@ -1,5 +1,20 @@ { "Resources": { + "mdlambdaSecurityGroup": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "Security group for markdown lambda", + "VpcId": "${vpcId}", + "SecurityGroupIngress": [], + "SecurityGroupEgress": [], + "Tags": [ + { + "Key": "Name", + "Value": "markdown-lambda-sg" + } + ] + } + }, "mdlambdaServiceRole": { "Type": "AWS::IAM::Role", "Properties": { @@ -16,7 +31,8 @@ "Version": "2012-10-17" }, "ManagedPolicyArns": [ - #[[{ "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" }]]# + #[[{ "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" }]]#, + #[[{ "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" }]]# ] } }, @@ -27,38 +43,45 @@ "S3Bucket": "${lambdaArtifactBucket}", "S3Key": "${lambdaArtifactKey}" }, + "FunctionName": "${stack}-markdownit", "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "mdlambdaServiceRole", "Arn" ] }, "Runtime": "nodejs20.x", - "Timeout": 10 + "Timeout": 10, + "MemorySize": 256, + "VpcConfig": { + "SecurityGroupIds": [{ "Ref": "mdlambdaSecurityGroup" }], + "SubnetIds": [ + #foreach($subnetId in $subnetIds) + "${subnetId}"#if($foreach.hasNext),#end + #end + ] + } } }, - "mdlambdaFunctionUrl": { - "Type": "AWS::Lambda::Url", + "mdlambdaVersion": { + "Type": "AWS::Lambda::Version", "Properties": { - "AuthType": "NONE", - "TargetFunctionArn": { "Ref": "mdlambda" }, - "Cors": { - "AllowOrigins": ["*"], - "AllowMethods": ["GET", "POST"] - } + "FunctionName": { "Ref": "mdlambda" } } }, - "mdlambdaFunctionUrlPermission": { - "Type": "AWS::Lambda::Permission", + "mdlambdaAliasProd": { + "Type": "AWS::Lambda::Alias", "Properties": { - "Action": "lambda:InvokeFunctionUrl", + "Name": "prod", "FunctionName": { "Ref": "mdlambda" }, - "Principal": "*", - "FunctionUrlAuthType": "NONE" + "FunctionVersion": { "Fn::GetAtt": [ "mdlambdaVersion", "Version" ] } } } }, "Outputs": { - "LambdaFunctionUrl": { - "Value": { "Ref": "mdlambdaFunctionUrl" }, - "Description": "The URL endpoint for the Lambda function" + "LambdaFunctionArn": { + "Value": { "Fn::GetAtt": ["mdlambda", "Arn"] }, + "Description": "The ARN of the Lambda function" + }, + "LambdaAliasArn": { + "Value": { "Ref": "mdlambdaAliasProd" }, + "Description": "Stable ARN for invoking the prod alias" } } - } diff --git a/src/test/java/org/sagebionetworks/template/markdownit/MarkDownItLambdaBuilderImplTest.java b/src/test/java/org/sagebionetworks/template/markdownit/MarkDownItLambdaBuilderImplTest.java index 8bb2b433e..9cf394cce 100644 --- a/src/test/java/org/sagebionetworks/template/markdownit/MarkDownItLambdaBuilderImplTest.java +++ b/src/test/java/org/sagebionetworks/template/markdownit/MarkDownItLambdaBuilderImplTest.java @@ -23,6 +23,7 @@ import java.util.Optional; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -32,6 +33,9 @@ import static org.sagebionetworks.template.Constants.CAPABILITY_NAMED_IAM; import static org.sagebionetworks.template.Constants.PROPERTY_KEY_LAMBDA_ARTIFACT_BUCKET; import static org.sagebionetworks.template.Constants.PROPERTY_KEY_LAMBDA_MARKDOWNIT_ARTIFACT_URL; +import static org.sagebionetworks.template.Constants.PROPERTY_KEY_LAMBDA_MARKDOWNIT_ARTIFACT_VERSION; +import static org.sagebionetworks.template.Constants.PROPERTY_KEY_LAMBDA_MARKDOWNIT_SUBNETS; +import static org.sagebionetworks.template.Constants.PROPERTY_KEY_LAMBDA_MARKDOWNIT_VPC; import static org.sagebionetworks.template.Constants.PROPERTY_KEY_STACK; @ExtendWith(MockitoExtension.class) @@ -64,7 +68,9 @@ public void before() { stack = "dev"; when(mockConfig.getProperty(PROPERTY_KEY_STACK)).thenReturn(stack); when(mockConfig.getProperty(PROPERTY_KEY_LAMBDA_ARTIFACT_BUCKET)).thenReturn("lambda.sagebase.org"); - when(mockConfig.getProperty(PROPERTY_KEY_LAMBDA_MARKDOWNIT_ARTIFACT_URL)).thenReturn("https://sagebionetworks.jfrog.io/lambda/org/sagebase/markdownit/markdownit.zip"); + when(mockConfig.getProperty(PROPERTY_KEY_LAMBDA_MARKDOWNIT_ARTIFACT_VERSION)).thenReturn("v1.0.0"); + when(mockConfig.getProperty(PROPERTY_KEY_LAMBDA_MARKDOWNIT_VPC)).thenReturn("vpc-12345"); + when(mockConfig.getCommaSeparatedProperty(PROPERTY_KEY_LAMBDA_MARKDOWNIT_SUBNETS)).thenReturn(new String[]{"subnet-12345", "subnet-12346"}); } @Test @@ -90,12 +96,12 @@ public void testBuildMarkDownItLambda() throws Exception { when(mockCloudFormationClientWrapper.describeStack(any())).thenReturn(Optional.of(markdownItLambdaStack)); String expectedBucket = "lambda.sagebase.org"; - String expectedKey = "artifacts/markdown-it/markdownit.zip"; + String expectedKey = "artifacts/markdown-it/v1.0.0/markdown-it.zip"; // call under test builder.buildMarkDownItLambda(); - verify(mockDownloader).downloadFile("https://sagebionetworks.jfrog.io/lambda/org/sagebase/markdownit/markdownit.zip"); + verify(mockDownloader).downloadFile("https://github.com/Sage-Bionetwors/synapse-markdown-it-lambda/releases/download/v1.0.0/markdown-it.zip"); verify(mockS3Client).putObject(expectedBucket, expectedKey, mockFile); verify(mockFile).delete(); @@ -109,7 +115,7 @@ public void testBuildMarkDownItLambda() throws Exception { verify(mockCloudFormationClientWrapper, times(1)).describeStack(argCaptorDescribeStack.capture()); CreateOrUpdateStackRequest request = argCaptorCreateOrUpdateStack.getValue(); - assertEquals("dev-markdown-it-function", request.getStackName()); + assertEquals("dev-markdown-it-function-direct", request.getStackName()); assertTrue(request.getTags().isEmpty()); assertEquals(1, request.getCapabilities().length); assertEquals(Capability.CAPABILITY_NAMED_IAM, request.getCapabilities()[0]); @@ -122,8 +128,7 @@ public void testBuildMarkDownItLambda() throws Exception { assertTrue(resources.has("mdlambda")); assertTrue(resources.has("mdlambdaFunctionUrl")); - assertEquals("dev-markdown-it-function", argCaptorWaitForStack.getValue()); - assertEquals("dev-markdown-it-function", argCaptorDescribeStack.getValue()); + assertEquals("dev-markdown-it-function-direct", argCaptorWaitForStack.getValue()); }