in services/onboarding-service/src/main/java/com/amazon/aws/partners/saasfactory/saasboost/OnboardingService.java [1122:1288]
public APIGatewayProxyResponseEvent updateProvisionedTenant(Map<String, Object> event, Context context) {
if (Utils.isBlank(API_GATEWAY_HOST)) {
throw new IllegalStateException("Missing required environment variable API_GATEWAY_HOST");
}
if (Utils.isBlank(API_GATEWAY_STAGE)) {
throw new IllegalStateException("Missing required environment variable API_GATEWAY_STAGE");
}
if (Utils.isBlank(API_TRUST_ROLE)) {
throw new IllegalStateException("Missing required environment variable API_TRUST_ROLE");
}
long startTimeMillis = System.currentTimeMillis();
LOGGER.info("OnboardingService::updateProvisionedTenant");
Utils.logRequestEvent(event);
APIGatewayProxyResponseEvent response = null;
// Unlike when we initially provision a tenant and compare the "global" settings
// to the potentially overriden per-tenant settings, here we're expecting to be
// told what to set the compute parameters to and assume the proceeding code that
// called us has save those values globally or per-tenant as appropriate.
Map<String, Object> tenant = Utils.fromJson((String) event.get("body"), Map.class);
Onboarding onboarding = dal.getOnboardingByTenantId((String) tenant.get("id"));
if (onboarding == null) {
response = new APIGatewayProxyResponseEvent()
.withStatusCode(400)
.withHeaders(CORS)
.withBody("{\"message\": \"No onboarding record for tenant id " + tenant.get("id") + "\"}");
} else {
UUID tenantId = onboarding.getTenantId();
String stackId = onboarding.getStackId();
Integer taskMemory = (Integer) tenant.get("memory");
Integer taskCpu = (Integer) tenant.get("cpu");
Integer taskCount = (Integer) tenant.get("minCount");
Integer maxCount = (Integer) tenant.get("maxCount");
String billingPlan = (String) tenant.get("planId");
String subdomain = (String) tenant.get("subdomain");
// We have an inconsistency with how the Lambda source folder is managed.
// If you update an existing SaaS Boost installation with the installer script
// it will create a new S3 "folder" for the Lambda code packages to force
// CloudFormation to update the functions. We are now saving this change as
// part of the global settings, but we'll need to go fetch it here because it's
// not part of the onboarding request data nor is it part of the tenant data.
Map<String, Object> settings = fetchSettingsForTenantUpdate(context);
String lambdaSourceFolder = (String) settings.get("SAAS_BOOST_LAMBDAS_FOLDER");
String templateUrl = "https://" + settings.get("SAAS_BOOST_BUCKET") + ".s3.amazonaws.com/" + settings.get("ONBOARDING_TEMPLATE");
List<Parameter> templateParameters = new ArrayList<>();
templateParameters.add(Parameter.builder().parameterKey("TenantId").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("Environment").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("SaaSBoostBucket").usePreviousValue(Boolean.TRUE).build());
if (Utils.isNotBlank(lambdaSourceFolder)) {
LOGGER.info("Overriding previous template parameter LambdaSourceFolder to {}", lambdaSourceFolder);
templateParameters.add(Parameter.builder().parameterKey("LambdaSourceFolder").parameterValue(lambdaSourceFolder).build());
} else {
templateParameters.add(Parameter.builder().parameterKey("LambdaSourceFolder").usePreviousValue(Boolean.TRUE).build());
}
templateParameters.add(Parameter.builder().parameterKey("DockerHostOS").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("DockerHostInstanceType").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("ContainerRepository").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("ContainerPort").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("ContainerHealthCheckPath").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("CodePipelineRoleArn").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("ArtifactBucket").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("TransitGateway").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("TenantTransitGatewayRouteTable").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("EgressTransitGatewayRouteTable").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("CidrPrefix").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("DomainName").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("SSLCertArnParam").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("HostedZoneId").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("UseEFS").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("MountPoint").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("EncryptEFS").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("EFSLifecyclePolicy").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("UseRDS").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("RDSInstanceClass").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("RDSEngine").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("RDSEngineVersion").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("RDSParameterGroupFamily").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("RDSMasterUsername").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("RDSMasterPasswordParam").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("RDSPort").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("RDSDatabase").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("RDSBootstrap").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("MetricsStream").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("ALBAccessLogsBucket").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("EventBus").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("UseFSx").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("FSxWindowsMountDrive").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("FSxDailyBackupTime").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("FSxBackupRetention").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("FSxThroughputCapacity").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("FSxStorageCapacity").usePreviousValue(Boolean.TRUE).build());
templateParameters.add(Parameter.builder().parameterKey("FSxWeeklyMaintenanceTime").usePreviousValue(Boolean.TRUE).build());
if (taskMemory != null) {
LOGGER.info("Overriding previous template parameter TaskMemory to {}", taskMemory);
templateParameters.add(Parameter.builder().parameterKey("TaskMemory").parameterValue(taskMemory.toString()).build());
} else {
templateParameters.add(Parameter.builder().parameterKey("TaskMemory").usePreviousValue(Boolean.TRUE).build());
}
if (taskCpu != null) {
LOGGER.info("Overriding previous template parameter TaskCPU to {}", taskCpu);
templateParameters.add(Parameter.builder().parameterKey("TaskCPU").parameterValue(taskCpu.toString()).build());
} else {
templateParameters.add(Parameter.builder().parameterKey("TaskCPU").usePreviousValue(Boolean.TRUE).build());
}
if (taskCount != null) {
LOGGER.info("Overriding previous template parameter TaskCount to {}", taskCount);
templateParameters.add(Parameter.builder().parameterKey("TaskCount").parameterValue(taskCount.toString()).build());
} else {
templateParameters.add(Parameter.builder().parameterKey("TaskCount").usePreviousValue(Boolean.TRUE).build());
}
if (maxCount != null) {
LOGGER.info("Overriding previous template parameter MaxTaskCount to {}", maxCount);
templateParameters.add(Parameter.builder().parameterKey("MaxTaskCount").parameterValue(maxCount.toString()).build());
} else {
templateParameters.add(Parameter.builder().parameterKey("MaxTaskCount").usePreviousValue(Boolean.TRUE).build());
}
if (billingPlan != null) {
LOGGER.info("Overriding previous template parameter BillingPlan to {}", Utils.isBlank(billingPlan) ? "''" : billingPlan);
templateParameters.add(Parameter.builder().parameterKey("BillingPlan").parameterValue(billingPlan).build());
} else {
templateParameters.add(Parameter.builder().parameterKey("BillingPlan").usePreviousValue(Boolean.TRUE).build());
}
// Pass in the subdomain each time because a blank value
// means delete the Route53 record set
if (subdomain == null) {
subdomain = "";
}
LOGGER.info("Setting template parameter TenantSubDomain to {}", subdomain);
templateParameters.add(Parameter.builder().parameterKey("TenantSubDomain").parameterValue(subdomain).build());
try {
UpdateStackResponse cfnResponse = cfn.updateStack(UpdateStackRequest.builder()
.stackName(stackId)
.usePreviousTemplate(Boolean.FALSE)
.templateURL(templateUrl)
.capabilitiesWithStrings("CAPABILITY_NAMED_IAM", "CAPABILITY_AUTO_EXPAND")
.parameters(templateParameters)
.build()
);
stackId = cfnResponse.stackId();
dal.updateStatus(onboarding.getId(), OnboardingStatus.updating);
LOGGER.info("OnboardingService::updateProvisionedTenant stack id " + stackId);
} catch (SdkServiceException cfnError) {
// CloudFormation throws a 400 error if it doesn't detect any resources in a stack
// need to be updated. Swallow this error.
if (cfnError.getMessage().contains("No updates are to be performed")) {
LOGGER.warn("cloudformation::updateStack error {}", cfnError.getMessage());
} else {
LOGGER.error("cloudformation::updateStack failed {}", cfnError.getMessage());
LOGGER.error(Utils.getFullStackTrace(cfnError));
dal.updateStatus(onboarding.getId(), OnboardingStatus.failed);
throw cfnError;
}
}
response = new APIGatewayProxyResponseEvent()
.withStatusCode(200)
.withHeaders(CORS)
.withBody("{\"stackId\": \"" + stackId + "\"}");
}
long totalTimeMillis = System.currentTimeMillis() - startTimeMillis;
LOGGER.info("OnboardingService::updateProvisionedTenant exec " + totalTimeMillis);
return response;
}