in resources/custom-resources/rds-bootstrap/src/main/java/com/amazon/aws/partners/saasfactory/saasboost/RdsBootstrap.java [77:209]
public Object handleRequest(Map<String, Object> event, Context context) {
try {
LOGGER.info(new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(event));
} catch (JsonProcessingException e) {
LOGGER.error("Could not log input");
}
final String requestType = (String) event.get("RequestType");
final Map<String, Object> resourceProperties = (Map<String, Object>) event.get("ResourceProperties");
final String host = (String) resourceProperties.get("Host");
final String port = (String) resourceProperties.get("Port");
final String database = (String) resourceProperties.get("Database");
final String username = (String) resourceProperties.get("User");
final String passwordParam = (String) resourceProperties.get("Password");
final String bootstrapFileBucket = (String) resourceProperties.get("BootstrapFileBucket");
final String bootstrapFileKey = (String) resourceProperties.get("BootstrapFileKey");
final String driverClassName = driverClassNameFromPort(port);
final String type = typeFromPort(port);
final boolean createAndBootstrap = (bootstrapFileBucket != null && !bootstrapFileBucket.isBlank()
&& bootstrapFileKey != null && !bootstrapFileKey.isBlank());
ExecutorService service = Executors.newSingleThreadExecutor();
ObjectNode responseData = JsonNodeFactory.instance.objectNode();
try {
Runnable r = () -> {
if ("Create".equalsIgnoreCase(requestType)) {
LOGGER.info("CREATE");
LOGGER.info("Getting database password secret from Parameter Store");
String password = null;
try {
password = ssm.getParameter(request -> request.withDecryption(true).name(passwordParam)).parameter().value();
} catch (SdkServiceException ssmError) {
LOGGER.error("ssm:GetParameter error", ssmError.getMessage());
throw ssmError;
}
if (password == null) {
throw new RuntimeException("Password is null");
}
// We need a connection that doesn't specify the database name since we may be creating it right now
String dbCheck = null;
// Unlike MySQL/MariaDB, you have to specify a database name to get a connection to postgres...
// And you should connect to dbo.master in SQL Server to check for a database
if (type.equals("postgresql")) {
dbCheck = "template1";
} else if (type.equals("sqlserver")) {
dbCheck = "master";
}
// Create the database if it doesn't exist - this is helpful
// for SQL Server because RDS/CloudFormation won't create a
// database when you bring up an instance.
LOGGER.info("Checking if database {} exists", database);
try (Connection dbCheckConn = DriverManager.getConnection(jdbcUrl(type, driverClassName, host, port, dbCheck), username, password)) {
String engine = dbCheckConn.getMetaData().getDatabaseProductName().toLowerCase();
if (!databaseExists(dbCheckConn, engine, database)) {
createdb(dbCheckConn, engine, database);
} else {
LOGGER.info("Database {} exists", database);
}
} catch (SQLException e) {
LOGGER.error("Can't connect to database host", e.getMessage());
throw new RuntimeException(e);
}
if (createAndBootstrap) {
LOGGER.info("Getting SQL file from S3 s3://{}/{}", bootstrapFileBucket, bootstrapFileKey);
InputStream bootstrapSQL = null;
try {
ResponseBytes<GetObjectResponse> responseBytes = s3.getObjectAsBytes(request -> request
.bucket(bootstrapFileBucket)
.key(bootstrapFileKey)
);
bootstrapSQL = responseBytes.asInputStream();
} catch (SdkServiceException s3Error) {
LOGGER.error("s3:GetObject error", s3Error.getMessage());
throw s3Error;
}
// We have a database. Execute the SQL commands in the bootstrap file stored in S3.
LOGGER.info("Executing bootstrap SQL");
try (Connection conn = DriverManager.getConnection(jdbcUrl(type, driverClassName, host, port, database), username, password);
Statement sql = conn.createStatement()) {
conn.setAutoCommit(false);
Scanner sqlScanner = new Scanner(bootstrapSQL, "UTF-8");
sqlScanner.useDelimiter(";");
int batch = 0;
while (sqlScanner.hasNext()) {
String ddl = sqlScanner.next().trim();
if (!ddl.isEmpty()) {
//LOGGER.info(String.format("%02d %s", ++batch, ddl));
sql.addBatch(ddl);
}
}
sql.executeBatch();
conn.commit();
LOGGER.info("Finished initializing database");
sendResponse(event, context, "SUCCESS", responseData);
} catch (SQLException e) {
LOGGER.error("Error executing bootstrap SQL", e.getMessage());
throw new RuntimeException(e);
}
} else {
// We were just creating a database and there isn't a SQL file to execute
sendResponse(event, context, "SUCCESS", responseData);
}
} else if ("Update".equalsIgnoreCase(requestType)) {
LOGGER.info("UPDATE");
sendResponse(event, context, "SUCCESS", responseData);
} else if ("Delete".equalsIgnoreCase(requestType)) {
LOGGER.info("DELETE");
sendResponse(event, context, "SUCCESS", responseData);
} else {
LOGGER.error("FAILED unknown requestType " + requestType);
responseData.put("Reason", "Unknown RequestType " + requestType);
sendResponse(event, context, "FAILED", responseData);
}
};
Future<?> f = service.submit(r);
f.get(context.getRemainingTimeInMillis() - 1000, TimeUnit.MILLISECONDS);
} catch (final TimeoutException | InterruptedException | ExecutionException e) {
// Timed out
LOGGER.error("FAILED unexpected error or request timed out " + e.getMessage());
LOGGER.error(getFullStackTrace(e));
responseData.put("Reason", e.getMessage());
sendResponse(event, context, "FAILED", responseData);
} finally {
service.shutdown();
}
return null;
}