in DeliveryApi/cdk/src/main/java/com/ilmlf/delivery/db/DbStack.java [107:253]
public DbStack(final Construct scope, final String id, final DbStackProps props) {
super(scope, id, props);
this.dbUsername = props.getDbUsername();
this.dbPort = props.getDbPort();
/**
* #################
* Network resources
* #################
*
* Create a VPC (Virtual Private Cloud), used for network partitioning.
*
* The VPC contains multiple "Subnets" that could be either Internet-public or private.
* Each Subnet is placed in different AZ (Availability Zones). Each AZ is in a different location
* within the region. In production, you should place your database and its replica in multiple AZ
* in case of failover. By default this stack deploys a database instance and its replica to different AZs.
*/
// The `Vpc` construct creates subnets for you automatically
// See https://docs.aws.amazon.com/cdk/api/latest/docs/aws-ec2-readme.html#vpc for details
this.vpc = new Vpc(this, "farmer-Vpc");
// Security group acts as a virtual firewall to control inbound and outbound traffic
this.securityGroup =
new SecurityGroup(
this,
"FarmerDeliverySG",
SecurityGroupProps.builder()
.vpc(vpc)
.description("Shared SG for database and proxy")
.allowAllOutbound(true)
.build());
/**
* #################
* ### DB Instance #
* #################
* Creates a MYSQL RDS instance.
*
* This construct also creates a secret store in AWS Secrets Manager. You can retrieve
* reference to the secret store by calling farmerDB.getSecret()
*
* The secret store contains the admin username, password and other DB information for connecting to the DB
*
* For production, consider using `DatabaseCluster` to create multiple instances in different AZs.
* This costs more but you will have higher availability.
*
* See https://docs.aws.amazon.com/cdk/api/latest/docs/aws-rds-readme.html for details.
*/
String dbName = "FarmerDB";
List<ISubnet> subnets;
if (props.isPublicSubnetDb) {
subnets = vpc.getPublicSubnets();
} else {
subnets = vpc.getPrivateSubnets();
}
DatabaseInstance farmerDb =
new DatabaseInstance(
this,
dbName,
DatabaseInstanceProps.builder()
.vpc(vpc)
.securityGroups(List.of(securityGroup))
// Using MySQL engine
.engine(
DatabaseInstanceEngine.mysql(
MySqlInstanceEngineProps.builder()
.version(MysqlEngineVersion.VER_5_7_31)
.build()))
.instanceType(InstanceType.of(InstanceClass.BURSTABLE2, InstanceSize.SMALL))
.vpcSubnets(SubnetSelection.builder().subnets(subnets).build())
.storageEncrypted(true)
.multiAz(true)
.autoMinorVersionUpgrade(true)
.allocatedStorage(25)
.publiclyAccessible(true)
.storageType(StorageType.GP2)
.backupRetention(Duration.days(7))
.deletionProtection(false)
// Create an admin credential for connectin to database. This credential will
// be stored in a Secret Manager store.
.credentials(Credentials.fromGeneratedSecret(dbName + "admin"))
.databaseName(dbName)
.port(this.dbPort)
.build());
/**
* Creates a username/password that will be used for IAM authentication.
*
* This user will be created in the database in the ApiStack (see PopulateFarmDb.java and dbinit.sql).
* The user will be used by API Lambda handlers to access the database via DB Proxy (with IAM Authentication).
*/
this.userSecret = new Secret(this, "DbUserSecret",
SecretProps.builder()
.description("Db Username and password")
.generateSecretString(
SecretStringGenerator.builder()
.secretStringTemplate("{\"username\": \"" + this.dbUsername + "\"}")
.generateStringKey("password")
.passwordLength(16)
.excludePunctuation(true)
.build()
)
.build()
);
/**
* #################
* ### RDS Proxy ###
* #################
* Lambda functions can scale to a large number and exhaust available database connections.
*
* We use RDS Proxy to prevent that. The proxy allows Lambda functions to share connections
* instead of opening a new one every time.
*
* See https://aws.amazon.com/rds/proxy/ for details
*/
// Role for RDS Proxy to access the DB
Role rdsProxyRole = new Role(this, "RdsProxyRole",
RoleProps.builder().assumedBy(new ServicePrincipal("rds.amazonaws.com")).build());
DatabaseProxy dbProxy =
new DatabaseProxy(
this,
"FarmerDbProxy",
DatabaseProxyProps.builder()
.borrowTimeout(Duration.seconds(30))
.maxConnectionsPercent(50)
// Proxy uses these secrets to connect to database
.secrets(List.of(farmerDb.getSecret(), userSecret))
.role(rdsProxyRole)
.proxyTarget(ProxyTarget.fromInstance(farmerDb))
.vpc(vpc)
.securityGroups(List.of(securityGroup))
.iamAuth(true)
.requireTls(true)
.build());
this.proxyEndpoint = dbProxy.getEndpoint();
this.proxyArn = dbProxy.getDbProxyArn();
this.instanceEndpoint = farmerDb.getDbInstanceEndpointAddress() + ":" + farmerDb.getDbInstanceEndpointPort();
this.adminSecret = farmerDb.getSecret();
}