in addons/addon-environment-sc-api/packages/environment-sc-workflow-steps/lib/steps/terminate-launch-dependency/terminate-launch-dependency.js [71:201]
async start() {
const [requestContext, envId, externalId] = await Promise.all([
this.payloadOrConfig.object(inPayloadKeys.requestContext),
this.payloadOrConfig.string(inPayloadKeys.envId),
this.payloadOrConfig.string(inPayloadKeys.externalId),
]);
const [albService, environmentScService, lockService, environmentScCidrService] = await this.mustFindServices([
'albService',
'environmentScService',
'lockService',
'environmentScCidrService',
]);
const environment = await environmentScService.mustFind(requestContext, { id: envId });
const projectId = environment.projectId;
// Setting project id to use while polling for status
this.state.setKey('PROJECT_ID', projectId);
// creating resolvedvars object with the necessary Metadata
const resolvedVars = {
projectId,
externalId,
};
// convert output array to object. Return {} if no outputs found
const environmentOutputs = await this.cfnOutputsArrayToObject(_.get(environment, 'outputs', []));
const connectionType = _.get(environmentOutputs, 'MetaConnection1Type', '');
// Clean up listener rule and Route53 record before deleting ALB and Workspace
if (connectionType.toLowerCase() === 'rstudiov2') {
const [environmentDnsService] = await this.mustFindServices(['environmentDnsService']);
const albExists = await albService.checkAlbExists(requestContext, projectId);
const deploymentItem = await albService.getAlbDetails(requestContext, projectId);
const deploymentValue = JSON.parse(deploymentItem.value);
const dnsName = deploymentValue.albDnsName;
if (albExists) {
try {
const isAppStreamEnabled = this.checkIfAppStreamEnabled();
if (isAppStreamEnabled) {
const memberAccount = await environmentScService.getMemberAccount(requestContext, environment);
const albHostedZoneId = await albService.getAlbHostedZoneID(
requestContext,
resolvedVars,
deploymentValue.albArn,
);
await environmentDnsService.deletePrivateRecordForDNS(
requestContext,
'rstudio',
envId,
albHostedZoneId,
dnsName,
memberAccount.route53HostedZone,
);
} else {
await environmentDnsService.deleteRecord('rstudio', envId, dnsName);
}
this.print({
msg: 'Route53 record deleted successfully',
});
} catch (error) {
// Don't fail the termination if record deletion failed
this.print({
msg: `Record deletion failed with error - ${error.message}`,
});
}
// Revoke EC2 security group rule with ALB security group ID
try {
const albSecurityGroup = deploymentValue.albSecurityGroup;
const instanceSecurityGroup = _.get(environmentOutputs, 'InstanceSecurityGroupId', '');
const updateRule = {
fromPort: 443,
toPort: 443,
protocol: 'tcp',
groupId: albSecurityGroup,
};
await environmentScCidrService.revokeIngressRuleWithSecurityGroup(
requestContext,
envId,
updateRule,
instanceSecurityGroup,
);
} catch (error) {
// Don't fail the termination if revoke fails
this.print({
msg: `Security group rule revoke failed with error - ${error.message}`,
});
}
}
const ruleArn = _.get(environmentOutputs, 'ListenerRuleARN', null);
// Skipping rule deletion for the cases where the product provisioing failed before creating rule
// Termination should not be affected in such scenarios
if (!_.isEmpty(ruleArn)) {
try {
await lockService.tryWriteLockAndRun({ id: `alb-rule-${deploymentItem.id}` }, async () => {
const listenerArn = deploymentValue.listenerArn;
await albService.deleteListenerRule(requestContext, resolvedVars, ruleArn, listenerArn);
});
this.print({
msg: 'Listener rule deleted successfully',
});
} catch (error) {
// Don't fail the termination if rule deletion failed
this.print({
msg: `Rule deletion failed with error - ${error.message}`,
});
}
}
}
// Get Template outputs to check NeedsALB flag. Not reading template outputs from DB
// Because failed products will not have outputs stored
const templateOutputs = await this.getTemplateOutputs(requestContext, environment.envTypeId);
const needsAlb = _.get(templateOutputs.NeedsALB, 'Value', false);
if (!needsAlb) return null;
const awsAccountId = await albService.findAwsAccountId(requestContext, projectId);
// Locking the ALB termination to avoid race conditions on parallel provisioning.
// expiresIn is set to 10 minutes. attemptsCount is set to 1200 to retry after 1 seconds for 20 minutes
const lock = await lockService.tryWriteLock(
{ id: `alb-update-${awsAccountId}`, expiresIn: 1200 },
{ attemptsCount: 1200 },
);
if (_.isUndefined(lock)) throw new Error('Could not obtain a lock');
this.print({
msg: `obtained lock - ${lock}`,
});
this.state.setKey('ALB_LOCK', lock);
// eslint-disable-next-line no-return-await
return await this.checkAndTerminateAlb(requestContext, projectId, externalId);
}