public async handler()

in packages/aws-rfdk/lib/lambdas/nodejs/lib/custom-resource/simple-resource.ts [76:143]


  public async handler(event: CfnRequestEvent, context: LambdaContext): Promise<string> {
    let status: CfnResponseStatus = CfnResponseStatus.SUCCESS;
    let failReason: string | undefined;
    let cfnData: object | undefined;

    console.log(`Handling event: ${JSON.stringify(event)}`);
    const resourceProperties: object = event.ResourceProperties ?? {};
    const physicalId: string = calculateSha256Hash(resourceProperties);

    try {
      const timeout = (prom: any, time: number, exception: any) => {
        let timer: any;
        return Promise.race([
          prom,
          new Promise((_r, rej) => timer = setTimeout(rej, time, exception)),
        ]).finally(() => clearTimeout(timer));
      };

      // We want to always notify CloudFormation about the success/failure of the Lambda at all times.
      // If function execution time is longer than Lambda's timeout, then the function is just stopped
      // and CloudFormation is not notified at all. This would result in a hang-up during deployment.
      // Thus, we want to stop the execution by ourselves before the Lambda timeout and reserve some time
      // for notifying a CloudFormation about a failed deployment because of the timeout.
      // 3 seconds should be enough to resolve the request that signals success/failure of the custom resource,
      // but if Lambda timeout is too small, we would reserve 20% of the remaining time and still try to notify the CF.
      // Check the logs during the development to see if you allocated enough time for your Lambda.
      const defaultReserveTimeMs = 3000;
      const remainingTimeMs = context.getRemainingTimeInMillis();
      let reserveTimeMs = Math.min(0.2 * remainingTimeMs, defaultReserveTimeMs);
      if (reserveTimeMs < defaultReserveTimeMs) {
        console.debug(`The remaining Lambda execution time of ${reserveTimeMs} ` +
        `ms might not be sufficient to send a CloudFormation response. At least ${defaultReserveTimeMs} ms is required. ` +
        'Please increase the Lambda timeout.');
      }

      cfnData = await timeout(
        this.handleEvent(event, context, resourceProperties, physicalId),
        remainingTimeMs - reserveTimeMs,
        new Error('Timeout error'),
      );
    } catch (e) {
      // We want to always catch the exception for a CfnCustomResource CloudFormation
      // must be notified about the success/failure of the lambda at all times;
      // failure to notify results in a stuck stack that takes at least an hour to
      // timeout.
      status = CfnResponseStatus.FAILED;
      if (types.isNativeError(e)) {
        failReason = `${e.message}\n${e.stack}`;
      } else {
        failReason = String(e);
      }
    } finally {
      // Always send a response to CloudFormation, signal success or
      // failure based on whether or not we had an exception.
      await sendCfnResponse({
        event,
        context,
        status,
        reason: failReason,
        physicalId,
        data: cfnData,
      });
    }

    const response: string = `${status}` + (failReason ?? '');
    console.log(`Result: ${response}`);
    return response;
  }