|
1 | 1 | package com.pm.stack; |
2 | 2 |
|
| 3 | +import java.util.HashMap; |
3 | 4 | import java.util.List; |
4 | 5 | import java.util.Map; |
5 | 6 | import java.util.stream.Collectors; |
|
20 | 21 | import software.amazon.awscdk.services.ec2.InstanceSize; |
21 | 22 | import software.amazon.awscdk.services.ec2.InstanceType; |
22 | 23 | import software.amazon.awscdk.services.ec2.Vpc; |
| 24 | +import software.amazon.awscdk.services.ecs.AwsLogDriverProps; |
23 | 25 | import software.amazon.awscdk.services.ecs.CloudMapNamespaceOptions; |
24 | 26 | import software.amazon.awscdk.services.ecs.Cluster; |
| 27 | +import software.amazon.awscdk.services.ecs.ContainerDefinitionOptions; |
| 28 | +import software.amazon.awscdk.services.ecs.ContainerImage; |
25 | 29 | import software.amazon.awscdk.services.ecs.FargateService; |
| 30 | +import software.amazon.awscdk.services.ecs.FargateTaskDefinition; |
| 31 | +import software.amazon.awscdk.services.ecs.LogDriver; |
| 32 | +import software.amazon.awscdk.services.ecs.PortMapping; |
| 33 | +import software.amazon.awscdk.services.ecs.Protocol; |
26 | 34 | import software.amazon.awscdk.services.rds.Credentials; |
27 | 35 | import software.amazon.awscdk.services.rds.DatabaseInstance; |
28 | 36 | import software.amazon.awscdk.services.rds.DatabaseInstanceEngine; |
29 | 37 | import software.amazon.awscdk.services.rds.PostgresEngineVersion; |
30 | 38 | import software.amazon.awscdk.services.rds.PostgresInstanceEngineProps; |
31 | 39 | import software.amazon.awscdk.services.route53.CfnHealthCheck; |
| 40 | +import software.amazon.awscdk.services.logs.LogGroup; |
| 41 | +import software.amazon.awscdk.services.logs.RetentionDays; |
32 | 42 |
|
33 | 43 | public class LocalStack extends Stack { |
34 | 44 | private final Vpc vpc; |
@@ -94,27 +104,93 @@ private CfnHealthCheck CfnHealthCheckForDb(DatabaseInstance databaseInstance, St |
94 | 104 |
|
95 | 105 | private CfnCluster CfnClusterKafka() { |
96 | 106 | return CfnCluster.Builder.create(this, "MskCluster") |
97 | | - .clusterName("kafka-cluster-for-patients") |
98 | | - .kafkaVersion("2.8.0") |
99 | | - .numberOfBrokerNodes(1) // connects vpn to broker node |
100 | | - .brokerNodeGroupInfo(CfnCluster.BrokerNodeGroupInfoProperty.builder().instanceType("kafka.m5.xlarge") // specifying size of machine to run on - more compute power |
101 | | - .clientSubnets(vpc.getPrivateSubnets().stream().map(ISubnet::getSubnetId).collect(Collectors.toList())) |
102 | | - .brokerAzDistribution("DEFAULT").build()).build(); |
| 107 | + .clusterName("kafka-cluster-for-patients") |
| 108 | + .kafkaVersion("2.8.0") |
| 109 | + .numberOfBrokerNodes(1) // connects vpn to broker node |
| 110 | + .brokerNodeGroupInfo(CfnCluster.BrokerNodeGroupInfoProperty.builder().instanceType("kafka.m5.xlarge") // specifying |
| 111 | + // size |
| 112 | + // of |
| 113 | + // machine |
| 114 | + // to |
| 115 | + // run |
| 116 | + // on |
| 117 | + // - |
| 118 | + // more |
| 119 | + // compute |
| 120 | + // power |
| 121 | + .clientSubnets( |
| 122 | + vpc.getPrivateSubnets().stream().map(ISubnet::getSubnetId).collect(Collectors.toList())) |
| 123 | + .brokerAzDistribution("DEFAULT").build()) |
| 124 | + .build(); |
103 | 125 | } |
104 | 126 |
|
105 | | - // so to find a specific service add the name of the service (container name) and the name of the namespace |
| 127 | + // so to find a specific service add the name of the service (container name) |
| 128 | + // and the name of the namespace |
106 | 129 | // eg. auth-service.patient-management.local |
107 | 130 | private Cluster createEcsCluster() { |
108 | | - // added the service discovery namespace to this cluster, makes it easy to find this service and read about it |
109 | | - // does not run well on localstack since it runs on localhost but this works for an actual amazon aws system |
110 | | - return Cluster.Builder.create(this, "patientManagementCluster").vpc(vpc).defaultCloudMapNamespace(CloudMapNamespaceOptions.builder().name("patient-management.local").build()).build(); |
| 131 | + // added the service discovery namespace to this cluster, makes it easy to find |
| 132 | + // this service and read about it |
| 133 | + // does not run well on localstack since it runs on localhost but this works for |
| 134 | + // an actual amazon aws system |
| 135 | + return Cluster.Builder.create(this, "patientManagementCluster").vpc(vpc) |
| 136 | + .defaultCloudMapNamespace(CloudMapNamespaceOptions.builder().name("patient-management.local").build()) |
| 137 | + .build(); |
111 | 138 | } |
112 | 139 |
|
113 | | - // create service using the FargateService launch type, makes it easy to start, stop, scale ecs tasks that run the containers |
| 140 | + // create service using the FargateService launch type, makes it easy to start, |
| 141 | + // stop, scale ecs tasks that run the containers |
114 | 142 | // the last argument is a map of key value pairs |
115 | | - // private FargateService creatFargateService(String id, String imageName, List<Integer> ports, DatabaseInstance dbName, Map<String, String> addEnvVars) { |
| 143 | + private FargateService creatFargateService(String id, String imageName, List<Integer> ports, |
| 144 | + DatabaseInstance dbName, Map<String, String> addEnvVars) { |
| 145 | + // specify the resources being used for a container |
| 146 | + FargateTaskDefinition taskDefinition = FargateTaskDefinition.Builder.create(this, id + "Task") |
| 147 | + .cpu(256) // 256 cpu units |
| 148 | + .memoryLimitMiB(512) |
| 149 | + .build(); |
116 | 150 |
|
117 | | - // } |
| 151 | + // adding the image name to run the container |
| 152 | + ContainerDefinitionOptions containerOptions = ContainerDefinitionOptions.builder() |
| 153 | + .image(ContainerImage.fromRegistry(imageName)) |
| 154 | + .portMappings(ports.stream().map( // port mapping to expose the ports of the container |
| 155 | + port -> PortMapping.builder().containerPort(port).hostPort(port).protocol(Protocol.TCP).build()) |
| 156 | + .toList()) |
| 157 | + .logging(LogDriver.awsLogs(AwsLogDriverProps.builder() |
| 158 | + .logGroup(LogGroup.Builder.create(this, id + "LogGroup") |
| 159 | + .logGroupName("/ecs/" + imageName) |
| 160 | + .removalPolicy(RemovalPolicy.DESTROY) |
| 161 | + .retention(RetentionDays.ONE_DAY) // keeping logs can be expensive, reduce it |
| 162 | + .build()) |
| 163 | + .build())) |
| 164 | + .build(); |
| 165 | + |
| 166 | + Map<String, String> envVars = new HashMap<>(); |
| 167 | + // local addresses where aws can set the kafka servers on - patient service and |
| 168 | + // analytics service can use these |
| 169 | + envVars.put("SPRING_KAFKA_BOOTSTRAP_SERVERS", |
| 170 | + "localhost.localstack.cloud:4511, localhost.localstack.cloud:4512"); |
| 171 | + |
| 172 | + if (addEnvVars != null) { |
| 173 | + envVars.putAll(addEnvVars); |
| 174 | + } |
| 175 | + |
| 176 | + if (dbName != null) { |
| 177 | + // the way this works jdbc:postgresql://%s:%s/%s-db => each %s is replaced with |
| 178 | + // endpoint address, port, and image name |
| 179 | + envVars.put("SPRING_DATASOURCE_URL", "jdbc:postgresql://%s:%s/%s-db" |
| 180 | + .formatted(dbName.getDbInstanceEndpointAddress(), dbName.getDbInstanceEndpointPort(), imageName)); |
| 181 | + |
| 182 | + envVars.put("SPRING_DATASOURCE_USERNAME", "admin"); |
| 183 | + // secret manager creates a password for you behind the scenes, all you have to |
| 184 | + // do is to grab it |
| 185 | + envVars.put("SPRING_DATASOURCE_PASSWORD", dbName.getSecret().secretValueFromJson("password").toString()); |
| 186 | + // same list of patients to use for testing, for production this should be |
| 187 | + // changed |
| 188 | + envVars.put("SPRING_JPA_HIBERNATE_DDL_AUTO", "update"); |
| 189 | + envVars.put("SPRING_SQL_INIT_MODE", "always"); |
| 190 | + envVars.put("SPRING_DATASOURCE_HIKARI_INITIALIZATION_FAIL_TIMEOUT", "60000"); |
| 191 | + } |
| 192 | + |
| 193 | + } |
118 | 194 |
|
119 | 195 | public static void main(final String[] args) { |
120 | 196 | // creating a new cdk app and defining where the output should be |
|
0 commit comments