async start()

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);
  }