1

I'm trying to create a CloudWatch Synthetics Canary in AWS using terraform, and I'm able to create it fine, but it instantly fails with this message: Error: Cannot find module '/opt/nodejs/node_modules/exports'

I'm new to Synthetics, Node.js and terraform, so it's a perfect storm, but I feel i'm missing some knowledge. I read that the node.js file needs to be in a folder structure of my_canary_folder > nodejs > node_modules > my_nodejs_zipfile.zip but when I created that as instructed , it was empty and didn't work, instead producing that error.

I presumed I had to install nodejs on my MacOS, so I ran npm install which created the package.json file but didn't create the node_modules folder. I've tried a number of different combinations of npm init, sudo npm init, sudo npm install but I can't get the node_modules folder to create so the canary keeps failing. I think I expected that installing nodejs would add some content to the node_modules folder, but nothing has happened

Does anyone know what I need to do here to get it to run here please?

Here is my main.tf snippet in case this is useful:

resource "aws_synthetics_canary" "portal_test" {
  name                 = "portal_load"
  artifact_s3_location = "${var.artifact_location}"
  execution_role_arn   = "${aws_iam_role.cloudwatch_synthetics_role.arn}"
  handler              = "exports.handler"
  runtime_version      = "syn-nodejs-puppeteer-3.6"
  zip_file             = "nodejs/node_modules/portal_load.zip"
  success_retention_period = 31
  start_canary         = true
}

I've followed a lot of the answers in this link, but nothing seems to work: npm install doesn't create node_modules directory

When I run npm install node in the application root directory, even with sudo, it creates the package.json, but not the node_modules folder. I've seen talk of the node_modules relating to the dependencies section of the package.json, but I can't find anything that relates to what is required for AWS CloudWatch. I've tried npm i as well, but the message coming back says that everything has installed ok, but yet still no node_modules folder

e-on
  • 1,547
  • 8
  • 32
  • 65
  • Ok, I've now managed to populate a `node_modules` folder by running `npm install express --save`. It has created a number of packages but doesn't include `exports`. I really feel like I'm missing the correct instructions here, i can't find what I'm looking for – e-on Aug 02 '22 at 11:00

1 Answers1

1

I am running into the same problem. Looking at your zipfile, I notice the path to the zip is nodejs/node_modules.

Based on the docs, I think the path to the canary script within the zipfile needs to include the path, not the path to the zipfile.

I don't actually have this working yet, but what I've managed to glean so far is that there's a generic canary lambda (hence the runtimes we can choose from). The canary scripts are then loaded as lambda layers. So, the zipfile contents needs to follow the same pattern as node layers like this blog.

I will update back here if I figure out how to actually get this working.


Update: The missing piece is the handler. Full implementation below.

The handler needs to target the js file under nodejs/node_modules/<FILE_NAME> and the corresponding exported function (i.e., <FILE_NAME>.handler).

With the following canary script:

const log = require('SyntheticsLogger')
const synthetics = require('Synthetics')

const synCfg = synthetics.getConfiguration()
synCfg.disableStepScreenshots()

const entryPoint = async () => {
    // TODO: Use synthetics.executeStep
    url = 'https://<TARGET_URL>';
    const page = await synthetics.getPage();
    log.info(`Navigating to ${url}`)
    const res = await page.goto(url);

    if (page.url().includes('<REDIRECT_URL>')) {
        log.info('Url redirected')
    } else {
        throw 'URL does not redirect'
    }
};

moudle.exports = {
    handler: async () => {
        return await entryPoint();
    }
};

This bash script deploys and updates the canary:

#!/bin/bash

set -eux

tmp_dir=$(mktemp -d .canaries.$$.XXXXXXX)
trap 'rm -r ${tmp_dir}; exit' SIGINT EXIT

for canary in ./canaries/*.js; do
    canary_name="$(basename "$canary" .js)"
    mkdir -p "$tmp_dir/$canary_name/nodejs/node_modules"
    cp "$canary" "${tmp_dir}/${canary_name}/nodejs/node_modules/${canary_name}.js" 
    pushd "$tmp_dir/$canary_name" || { echo 'Failed to cd into tmp directory.' >&2; exit 1; }
    zip -r "$canary_name.zip" 'nodejs'
    aws s3 cp "$canary_name.zip" "s3://$CANARY_BUCKET/canaries/${canary_name}.zip"
    aws synthetics update-canary --name "$canary_name" \
        --code "S3Bucket=$CANARY_BUCKET,S3Key=canaries/${canary_name}.zip,Handler=${canary_name}.handler"
    popd || { echo 'Failed to popd' >&2; exit 1; }
done

BadAsstronaut
  • 86
  • 2
  • 7
  • You're absolutely right. I had actually tried that and got the same error, but I eventually got it working by creating the folder path within the zip file and then made the terraform entry: ```zip_file = "portal_load.zip"```. It turned out my script also didn't work - I'd tried to just use the record screen function and use that script in full, but the upload didn't like that. I had to edit the standard blueprint file from the example in the docs and then paste a section of my recorded script within that. Very convoluted, but I got there in the end. Hope AWS changes it soon – e-on Sep 22 '22 at 07:35