diff --git a/java/alb-multi-rule-response/.gitignore b/java/alb-multi-rule-response/.gitignore new file mode 100644 index 000000000..1db21f162 --- /dev/null +++ b/java/alb-multi-rule-response/.gitignore @@ -0,0 +1,13 @@ +.classpath.txt +target +.classpath +.project +.idea +.settings +.vscode +*.iml + +# CDK asset staging directory +.cdk.staging +cdk.out + diff --git a/java/alb-multi-rule-response/README.md b/java/alb-multi-rule-response/README.md new file mode 100644 index 000000000..a07d0094a --- /dev/null +++ b/java/alb-multi-rule-response/README.md @@ -0,0 +1,55 @@ + +--- + +![Stability: Stable](https://img.shields.io/badge/stability-Stable-success.svg?style=for-the-badge) + +> **This is a stable example. It should successfully build out of the box** +> +> This examples does is built on Construct Libraries marked "Stable" and does not have any infrastructure prerequisites to build. + +--- + + +# ALB using a combination of host and path based routing with Java AWS-CDK +The ALB is backed by an EC2 instance acting as frontend client and built-in ALB response rules that simulates mobile and application api + +## Testing the responses +### testing mobile produciton api +``` bash +curl -H "Host: mobile.mydomain.com" [ALB-DNS-FQDN]/production +``` +Resonse received +```json +{"status":"succes","apiversion":"prod_mobile_v1"} +``` + +### Testing frontend production api +``` bash +curl -H "Host: api.mydomain.com" [ALB-DNS-FQDN]/production +``` + +```json +{"status":"succes","apiversion":"prod_api_v1"} +``` + +## List of ALB rules +![alt text](imgs/screenshot.png "screenshot of resulting rule") + + +It is a Maven-based project, so you can open this directory with any Maven-compatible Java IDE, and you should be able to build and run tests from your IDE. + +You should explore the contents of this template. It demonstrates a CDK app to create a multi-rule ALB, based on path and host headers. +More information on ALB and Path routing is available here https://docs.aws.amazon.com/elasticloadbalancing/latest/application/tutorial-load-balancer-routing.html + +The cdk.json file tells the CDK Toolkit how to execute your app. This example relies on maven to do that. + +## Useful commands + + * `mvn package` compile and run tests + * `cdk ls` list all stacks in the app + * `cdk synth` emits the synthesized CloudFormation template + * `cdk deploy` deploy this stack to your default AWS account/region + * `cdk diff` compare deployed stack with current state + * `cdk docs` open CDK documentation + +Enjoy! diff --git a/java/alb-multi-rule-response/cdk.json b/java/alb-multi-rule-response/cdk.json new file mode 100644 index 000000000..8d02402c6 --- /dev/null +++ b/java/alb-multi-rule-response/cdk.json @@ -0,0 +1,3 @@ +{ + "app": "mvn -e -q exec:java" +} diff --git a/java/alb-multi-rule-response/imgs/screenshot.png b/java/alb-multi-rule-response/imgs/screenshot.png new file mode 100644 index 000000000..37aa0d36a Binary files /dev/null and b/java/alb-multi-rule-response/imgs/screenshot.png differ diff --git a/java/alb-multi-rule-response/pom.xml b/java/alb-multi-rule-response/pom.xml new file mode 100644 index 000000000..cf990518e --- /dev/null +++ b/java/alb-multi-rule-response/pom.xml @@ -0,0 +1,69 @@ + + + 4.0.0 + + com.myorg + albcdkproject + 0.1 + + + UTF-8 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + com.myorg.ALBProjectApp + + + + + + + + + software.amazon.awscdk + core + [1.26.0, 2) + + + + software.amazon.awscdk + elasticloadbalancingv2 + [1.26.0, 2) + + + software.amazon.awscdk + ec2 + [1.26.0, 2) + + + software.amazon.awscdk + autoscaling + [1.26.0,2) + + + + + junit + junit + 4.12 + test + + + diff --git a/java/alb-multi-rule-response/src/main/java/com/myorg/ALBProjectApp.java b/java/alb-multi-rule-response/src/main/java/com/myorg/ALBProjectApp.java new file mode 100644 index 000000000..6a2d639f5 --- /dev/null +++ b/java/alb-multi-rule-response/src/main/java/com/myorg/ALBProjectApp.java @@ -0,0 +1,15 @@ +package com.myorg; + +import software.amazon.awscdk.core.App; +import software.amazon.awscdk.core.Environment; +import software.amazon.awscdk.core.StackProps; + +public class ALBProjectApp { + public static void main(final String[] args) { + App app = new App(); + + new ALBProjectStack( + app, "ALBProjectStack", StackProps.builder().env(Environment.builder().build()).build()); + app.synth(); + } +} diff --git a/java/alb-multi-rule-response/src/main/java/com/myorg/ALBProjectStack.java b/java/alb-multi-rule-response/src/main/java/com/myorg/ALBProjectStack.java new file mode 100644 index 000000000..cc80847b0 --- /dev/null +++ b/java/alb-multi-rule-response/src/main/java/com/myorg/ALBProjectStack.java @@ -0,0 +1,146 @@ +package com.myorg; + +import com.myorg.utils.PropertyLoader; +import java.util.ArrayList; +import java.util.List; +import software.amazon.awscdk.core.Construct; +import software.amazon.awscdk.core.Stack; +import software.amazon.awscdk.core.StackProps; +import software.amazon.awscdk.services.autoscaling.AutoScalingGroup; +import software.amazon.awscdk.services.ec2.AmazonLinuxImage; +import software.amazon.awscdk.services.ec2.InstanceClass; +import software.amazon.awscdk.services.ec2.InstanceSize; +import software.amazon.awscdk.services.ec2.InstanceType; +import software.amazon.awscdk.services.ec2.Vpc; +import software.amazon.awscdk.services.elasticloadbalancingv2.ApplicationListener; +import software.amazon.awscdk.services.elasticloadbalancingv2.ApplicationListenerRule; +import software.amazon.awscdk.services.elasticloadbalancingv2.ApplicationLoadBalancer; +import software.amazon.awscdk.services.elasticloadbalancingv2.ApplicationProtocol; +import software.amazon.awscdk.services.elasticloadbalancingv2.ApplicationTargetGroup; +import software.amazon.awscdk.services.elasticloadbalancingv2.ApplicationTargetGroupProps; +import software.amazon.awscdk.services.elasticloadbalancingv2.ContentType; +import software.amazon.awscdk.services.elasticloadbalancingv2.FixedResponse; +import software.amazon.awscdk.services.elasticloadbalancingv2.IApplicationLoadBalancerTarget; +import software.amazon.awscdk.services.elasticloadbalancingv2.IApplicationTargetGroup; +import software.amazon.awscdk.services.elasticloadbalancingv2.TargetType; + +public class ALBProjectStack extends Stack { + + public ALBProjectStack(final Construct scope, final String id) { + this(scope, id, null); + } + + public ALBProjectStack(final Construct scope, final String id, final StackProps props) { + super(scope, id, props); + + // property loader + PropertyLoader propertyLoad = new PropertyLoader(); + + // create ALB and all anciliarry services + Vpc vpc = Vpc.Builder.create(this, "VPC").build(); + AutoScalingGroup asg = + AutoScalingGroup.Builder.create(this, "ASG") + .vpc(vpc) + .instanceType(InstanceType.of(InstanceClass.BURSTABLE2, InstanceSize.MICRO)) + .userData(propertyLoad.getUserData()) + .machineImage(new AmazonLinuxImage()) + .build(); + + ApplicationLoadBalancer lb = + ApplicationLoadBalancer.Builder.create(this, "LB") + .vpc(vpc) + .internetFacing(Boolean.TRUE) + .loadBalancerName("myalb") + .build(); + + List targets = new ArrayList(); + targets.add(asg); + + ApplicationTargetGroup webTargetGroup = + new ApplicationTargetGroup( + this, + "MyTargetGroup", + ApplicationTargetGroupProps.builder() + .vpc(vpc) + .targetType(TargetType.INSTANCE) + .targets(targets) + .port(80) + .protocol(ApplicationProtocol.HTTP) + .build()); + + List targetGroups = new ArrayList(); + targetGroups.add(webTargetGroup); + + // default listener + ApplicationListener http = + ApplicationListener.Builder.create(this, "HTTP") + .port(80) + .protocol(ApplicationProtocol.HTTP) + .open(true) + .loadBalancer(lb) + .defaultTargetGroups(targetGroups) + .build(); + + // adding application listern rules + ApplicationListenerRule alrProdApi = + ApplicationListenerRule.Builder.create(this, "prodApi") + .pathPattern("/production") + .priority(1) + .listener(http) + .hostHeader(propertyLoad.getRestAPIHostHeader()) + .build(); + + ApplicationListenerRule alrProdM = + ApplicationListenerRule.Builder.create(this, "prodMobile") + .pathPattern("/production") + .priority(2) + .listener(http) + .hostHeader(propertyLoad.getRestMobileHostHeader()) + .build(); + + ApplicationListenerRule alrSandboxApi = + ApplicationListenerRule.Builder.create(this, "sandboxApi") + .pathPattern("/sandbox") + .priority(3) + .listener(http) + .hostHeader(propertyLoad.getRestAPIHostHeader()) + .build(); + + ApplicationListenerRule alrSandboxM = + ApplicationListenerRule.Builder.create(this, "sandboxMobile") + .pathPattern("/sandbox") + .priority(4) + .listener(http) + .hostHeader(propertyLoad.getRestMobileHostHeader()) + .build(); + + // adding fixed responses + alrProdApi.addFixedResponse( + FixedResponse.builder() + .statusCode("200") + .contentType(ContentType.APPLICATION_JSON) + .messageBody(propertyLoad.getProdApiMessageBody()) + .build()); + + alrProdM.addFixedResponse( + FixedResponse.builder() + .statusCode("200") + .contentType(ContentType.APPLICATION_JSON) + .messageBody(propertyLoad.getProdMobileMessageBody()) + .build()); + + alrSandboxApi.addFixedResponse( + FixedResponse.builder() + .statusCode("200") + .contentType(ContentType.APPLICATION_JSON) + .messageBody(propertyLoad.getSandboxApiMessageBody()) + .build()); + + alrSandboxM.addFixedResponse( + FixedResponse.builder() + .statusCode("200") + .contentType(ContentType.APPLICATION_JSON) + .messageBody(propertyLoad.getSandboxMobileMessageBody()) + .build()); + } +} diff --git a/java/alb-multi-rule-response/src/main/java/com/myorg/utils/PropertyLoader.java b/java/alb-multi-rule-response/src/main/java/com/myorg/utils/PropertyLoader.java new file mode 100644 index 000000000..de0e6bb5d --- /dev/null +++ b/java/alb-multi-rule-response/src/main/java/com/myorg/utils/PropertyLoader.java @@ -0,0 +1,83 @@ +package com.myorg.utils; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import software.amazon.awscdk.services.ec2.UserData; + +public class PropertyLoader { + + private final UserData userData; + private String prodMobileMessageBody; + private String prodApiMessageBody; + private String sandboxApiMessageBody; + private String sandboxMobileMessageBody; + private String restAPIHostHeader; + private String restMobileHostHeader; + + public PropertyLoader() { + userData = UserData.forLinux(); + load(); + } + + public UserData getUserData() { + return userData; + } + + public String getProdMobileMessageBody() { + return prodMobileMessageBody; + } + + public String getProdApiMessageBody() { + return prodApiMessageBody; + } + + public String getSandboxApiMessageBody() { + return sandboxApiMessageBody; + } + + public String getSandboxMobileMessageBody() { + return sandboxMobileMessageBody; + } + + public String getRestAPIHostHeader() { + return restAPIHostHeader; + } + + public String getRestMobileHostHeader() { + return restMobileHostHeader; + } + + private void load() { + // start loading data from the properties files + try (InputStream input = + PropertyLoader.class.getClassLoader().getResourceAsStream("mydata.properties")) { + + Properties prop = new Properties(); + if (input == null) { + System.out.println("unable to find mydata.properties"); + return; + } + + // get the values from the properties file + prop.load(input); + + // start populating values + prodMobileMessageBody = prop.getProperty("response.prodMobileV1"); + prodApiMessageBody = prop.getProperty("response.prodApiV1"); + sandboxApiMessageBody = prop.getProperty("response.sandboxApiV1"); + sandboxMobileMessageBody = prop.getProperty("response.sandboxMobileV1"); + restAPIHostHeader = prop.getProperty("rest.api"); + restMobileHostHeader = prop.getProperty("rest.mobile"); + + String[] cmds = prop.getProperty("userData.commands").split("\n"); + if (cmds.length > 0) { + for (String cmd : cmds) { + userData.addCommands(cmd); + } + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } +} diff --git a/java/alb-multi-rule-response/src/main/resources/mydata.properties b/java/alb-multi-rule-response/src/main/resources/mydata.properties new file mode 100644 index 000000000..7f794c764 --- /dev/null +++ b/java/alb-multi-rule-response/src/main/resources/mydata.properties @@ -0,0 +1,10 @@ +response.prodMobileV1 = {\"status\":\"succes\",\"apiversion\":\"prod_mobile_v1\"} +response.prodApiV1 = {\"status\":\"succes\",\"apiversion\":\"prod_api_v1\"} +response.sandboxApiV1 = {\"status\":\"succes\",\"apiversion\":\"sandbox_api_v1\"} +response.sandboxMobileV1 = {\"status\":\"succes\",\"apiversion\":\"sandbox_mobile_v1\"} +rest.mobile = mobile.mydomain.com +rest.api = api.mydomain.com +userData.commands = yum update -y \n \ + yum install -y httpd \n \ + echo "hello from cdk" > /var/www/html/index.html \n \ + service httpd start \ No newline at end of file