

# Escritura de un script de canario de Node.js mediante el tiempo de ejecución de Puppeteer
<a name="CloudWatch_Synthetics_Canaries_WritingCanary_Nodejs_Pup"></a>

**Topics**
+ [Creación de un valor controlado de CloudWatch Synthetics desde cero](#CloudWatch_Synthetics_Canaries_write_from_scratch)
+ [Empaquetado de los archivos de valores controlados de Node.js](#CloudWatch_Synthetics_Canaries_package)
+ [Cambio de un script de Puppeteer existente para usarlo como valor controlado de Synthetics](#CloudWatch_Synthetics_Canaries_modify_puppeteer_script)
+ [Variables de entorno](#CloudWatch_Synthetics_Environment_Variables)
+ [Integración del valor controlado con otros servicios de AWS](#CloudWatch_Synthetics_Canaries_AWS_integrate)
+ [Forzar al valor controlado para que utilice una dirección IP estática](#CloudWatch_Synthetics_Canaries_staticIP)

## Creación de un valor controlado de CloudWatch Synthetics desde cero
<a name="CloudWatch_Synthetics_Canaries_write_from_scratch"></a>

Aquí hay un ejemplo de script mínimo de valor controlado de Synthetics. Este script pasa como una ejecución correcta y devuelve una cadena. Para ver el aspecto de un valor controlado erróneo, cambie `let fail = false;` a `let fail = true;`. 

Debe definir una función de punto de entrada para el script de valor controlado. Para ver cómo se cargan los archivos en la ubicación de Amazon S3 especificada como `ArtifactS3Location` del canario, cree estos archivos en la carpeta `/tmp`. Todos los artefactos de canarios deben almacenarse en el directorio `/tmp`, ya que es el único en el que se puede escribir. Asegúrese de que la ruta de la captura de pantalla esté establecida en `/tmp` para todas las capturas de pantalla u otros archivos creados por el script. Synthetics carga los archivos automáticamente de ` /tmp` en un bucket de S3.

```
/tmp/<name>
```

Después de que el script se ejecuta, el estado superado o no superado y las métricas de duración se ejecutan en CloudWatch y los archivos bajo `/tmp` se cargan en un bucket de S3.

```
const basicCustomEntryPoint = async function () {

    // Insert your code here

    // Perform multi-step pass/fail check

    // Log decisions made and results to /tmp

    // Be sure to wait for all your code paths to complete 
    // before returning control back to Synthetics.
    // In that way, your canary will not finish and report success
    // before your code has finished executing

    // Throw to fail, return to succeed
    let fail = false;
    if (fail) {
        throw "Failed basicCanary check.";
    }

    return "Successfully completed basicCanary checks.";
};

exports.handler = async () => {
    return await basicCustomEntryPoint();
};
```

A continuación, expandiremos el script para usar el registro de Synthetics y realizar una llamada usando el SDK de AWS. A modo de demostración, este script creará un cliente de Amazon DynamoDB y realizará una llamada a las listTables DynamoDB de la API. Registra la respuesta a la solicitud y los registros se superan o no en función de si la solicitud se realizó correctamente.

```
const log = require('@aws/synthetics-logger');
const AWS = require('aws-sdk');
// Require any dependencies that your script needs
// Bundle additional files and dependencies into a .zip file with folder structure
// nodejs/node_modules/additional files and folders

const basicCustomEntryPoint = async function () {

    log.info("Starting DynamoDB:listTables canary.");
    
    let dynamodb = new AWS.DynamoDB();
    var params = {};
    let request = await dynamodb.listTables(params);
    try {
        let response = await request.promise();
        log.info("listTables response: " + JSON.stringify(response));
    } catch (err) {
        log.error("listTables error: " + JSON.stringify(err), err.stack);
        throw err;
    }

    return "Successfully completed DynamoDB:listTables canary.";
};

exports.handler = async () => {
    return await basicCustomEntryPoint();
};
```

## Empaquetado de los archivos de valores controlados de Node.js
<a name="CloudWatch_Synthetics_Canaries_package"></a>

 **Para syn-nodejs-puppeteer-11.0 y versiones posteriores** 

 La estructura de empaquetado anterior (para syn-nodejs-puppeteer-10.0 y versiones anteriores) sigue siendo compatible con las versiones más recientes.

Cree un script utilizando una de las siguientes opciones:
+ Archivo .js (sintaxis de CommonJS)
+ Archivo .mjs (sintaxis de los módulos de ES)

Para los módulos de ES, utilice una de las siguientes opciones:
+ Archivo .js (sintaxis de CommonJS)
+ Archivo .mjs (sintaxis de los módulos de ES)

La estructura de los paquetes se define a continuación:
+ Archivo de controlador a nivel raíz (index.js/index.mjs)
+ Archivo de configuración opcional (synthetics.json)
+ Dependencias adicionales en node\$1modules (de ser necesarias)

Ejemplo de estructura de empaquetado:

```
  my_function/
├── index.mjs
├── synthetics.json
├── helper-utils.mjs
└── node_modules/
    └── dependencies
```

Para empaquetar, siga los pasos que se indican a continuación:

1. Instale las dependencias (si las hay).

   ```
   npm install
   ```

1. Cree un paquete .zip.

   ```
   zip -r my_deployment_package.zip
   ```

 **Para syn-nodejs-puppeteer-11.0 y versiones anteriores** 

Cuando se usa Amazon S3, se requiere la siguiente estructura:

```
  nodejs/
└── node_modules/
    └── myCanaryFilename.js
```

 **Para añadir compatibilidad opcional para subcarpetas en syn-nodejs-puppeteer-3.4\$1:** 

```
nodejs/
└── node_modules/
    └── myFolder/
        └── myCanaryFilename.js
```

**nota**  
La ruta del controlador en la configuración debe coincidir con la ubicación del archivo.

 **Nombre del controlador** 

Asegúrese de establecer el punto de entrada del script (controlador) del valor controlado como ` myCanaryFilename.functionName` para que coincida con el nombre de archivo del punto de entrada del script. Si utiliza un tiempo de ejecución anterior a `syn-nodejs-puppeteer-3.4`, el `functionName` debe ser `handler`. Si utiliza ` syn-nodejs-puppeteer-3.4` o uno posterior, puede elegir cualquier nombre de función como el controlador. Si utiliza `syn-nodejs-puppeteer-3.4` o uno posterior, también puede almacenar el valor controlado en una carpeta independiente, como ` nodejs/node_modules/myFolder/my_canary_filename`. Si lo almacena en una carpeta independiente, especifique esa ruta en el punto de entrada del script, como ` myFolder/my_canary_filename.functionName`.

## Cambio de un script de Puppeteer existente para usarlo como valor controlado de Synthetics
<a name="CloudWatch_Synthetics_Canaries_modify_puppeteer_script"></a>

En esta sección se explica cómo tomar scripts de Puppeteer y modificarlos para que se ejecuten como scripts de valor controlado de Synthetics. Para obtener más información acerca de Puppeteer, consulte [Puppeteer API v1.14.0](https://github.com/puppeteer/puppeteer/blob/v1.14.0/docs/api.md). 

Comenzaremos con este ejemplo de script de Puppeteer:

```
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com');
  await page.screenshot({path: 'example.png'});

  await browser.close();
})();
```

Los pasos de conversión son los siguientes:
+ Crear y exportar una función de `handler`. El controlador es la función de punto de entrada para el script. Si utiliza un tiempo de ejecución anterior a ` syn-nodejs-puppeteer-3.4`, la función del controlador debe denominarse `handler`. Si utiliza `syn-nodejs-puppeteer-3.4` o uno posterior, la función puede tener cualquier nombre, pero debe ser el mismo nombre que se usa en el script. Además, si utiliza `syn-nodejs-puppeteer-3.4` o uno posterior, puede almacenar los scripts en cualquier carpeta y especificar dicha carpeta como parte del nombre del controlador.

  ```
  const basicPuppeteerExample = async function () {};
  
  exports.handler = async () => {
      return await basicPuppeteerExample();
  };
  ```
+ Use la dependencia de `Synthetics`.

  ```
  var synthetics = require('@aws/synthetics-puppeteer');
  ```
+ Utilice la función de `Synthetics.getPage` para obtener un objeto `Page` de Puppeteer.

  ```
  const page = await synthetics.getPage();
  ```

  El objeto de página devuelto por la función Synthetics.getPage tiene instrumentados para el registro los eventos `request`, `response` y ` requestfailed` de **page.on**. Synthetics también configura la generación de archivos HAR para las solicitudes y respuestas en la página y agrega el ARN del valor controlado a los encabezados del agente de usuario de las solicitudes salientes en la página.

El script ya está listo para ser ejecutado como un valor controlado de Synthetics. Aquí está el script actualizado:

```
var synthetics = require('@aws/synthetics-puppeteer');  // Synthetics dependency

const basicPuppeteerExample = async function () {
    const page = await synthetics.getPage(); // Get instrumented page from Synthetics
    await page.goto('https://example.com');
    await page.screenshot({path: '/tmp/example.png'}); // Write screenshot to /tmp folder
};

exports.handler = async () => {  // Exported handler function 
    return await basicPuppeteerExample();
};
```

## Variables de entorno
<a name="CloudWatch_Synthetics_Environment_Variables"></a>

Puede utilizar variables de entorno al crear canaries. Esto le permite escribir un único script de valor controlado y luego usar ese script con diferentes valores para crear rápidamente varios valores controlados que tengan una tarea similar.

Suponga, por ejemplo, que su organización tiene puntos de enlaces como `prod`, ` dev`, y `pre-release` para las diferentes etapas del desarrollo del software, y que necesita crear canaries para probar cada uno de estos puntos de enlace. Puede escribir un único script de valor controlado que pruebe el software y, a continuación, especificar los valores diferentes para la variable de entorno de punto de conexión cuando cree cada uno de los tres valores controlados. A continuación, cuando se crea un valor controlado, se especifica el script y los valores que se van a utilizar para las variables de entorno.

Los nombres de las variables de entorno pueden contener letras, números y guiones bajos. Deben comenzar con una letra y tener al menos dos caracteres. El tamaño total de las variables de entorno no puede superar los 4 KB. No es posible especificar variables de entorno reservadas de Lambda como claves para sus variables de entorno. Para obtener más información acerca de las variables de entorno reservadas, consulte [Runtime environment variables](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime) (Variables de entorno en tiempo de ejecución).

**importante**  
Las claves y valores de las variables de entorno se cifran en reposo utilizando claves AWS KMS propiedad de AWS. Sin embargo, las variables de entorno no se cifran del lado del cliente. No guarde información confidencial en ellos.

En el siguiente ejemplo el script utiliza dos variables de entorno. Este script es para un valor controlado que verifica si hay una página web disponible. Utiliza variables de entorno para parametrizar tanto la URL que verifica como el nivel de registro de CloudWatch Synthetics que utiliza. 

La siguiente función establece `LogLevel` al valor de la variable de entorno ` LOG_LEVEL`.

```
 synthetics.setLogLevel(process.env.LOG_LEVEL);
```

La función establece `URL` al valor de la variable de entorno `URL`.

```
const URL = process.env.URL;
```

Este es el script completo. Cuando se crea un valor controlado con este script, se especifican los valores para las variables de entorno `LOG_LEVEL` y `URL`.

```
var synthetics = require('@aws/synthetics-puppeteer');
const log = require('@aws/synthetics-logger');

const pageLoadEnvironmentVariable = async function () {

    // Setting the log level (0-3)
    synthetics.setLogLevel(process.env.LOG_LEVEL);
    // INSERT URL here
    const URL = process.env.URL;

    let page = await synthetics.getPage();
    //You can customize the wait condition here. For instance,
    //using 'networkidle2' may be less restrictive.
    const response = await page.goto(URL, {waitUntil: 'domcontentloaded', timeout: 30000});
    if (!response) {
        throw "Failed to load page!";
    }
    //Wait for page to render.
    //Increase or decrease wait time based on endpoint being monitored.
    await page.waitFor(15000);
    await synthetics.takeScreenshot('loaded', 'loaded');
    let pageTitle = await page.title();
    log.info('Page title: ' + pageTitle);
    log.debug('Environment variable:' + process.env.URL);

    //If the response status code is not a 2xx success code
    if (response.status() < 200 || response.status() > 299) {
        throw "Failed to load page!";
    }
};

exports.handler = async () => {
    return await pageLoadEnvironmentVariable();
};
```

### Traspaso de las variables de entorno al script
<a name="CloudWatch_Synthetics_Canaries_pass_variables"></a>

Para pasar variables de entorno al script cuando cree un valor controlado en la consola, especifique las claves y los valores de las variables de entorno en la sección **Variables de entorno** en la consola. Para obtener más información, consulte [Creación de un valor controlado](CloudWatch_Synthetics_Canaries_Create.md).

Para pasar variables de entorno a través de la API o AWS CLI, utilice el parámetro ` EnvironmentVariables` en la sección `RunConfig`. A continuación, se observa un ejemplo del comando de AWS CLI que crea un valor controlado que utiliza dos variables de entorno con claves de `Environment` y `Region`.

```
aws synthetics create-canary --cli-input-json '{
   "Name":"nameofCanary",
   "ExecutionRoleArn":"roleArn",
   "ArtifactS3Location":"s3://amzn-s3-demo-bucket-123456789012-us-west-2",
   "Schedule":{
      "Expression":"rate(0 minute)",
      "DurationInSeconds":604800
   },
   "Code":{
      "S3Bucket": "canarycreation",
      "S3Key": "cwsyn-mycanaryheartbeat-12345678-d1bd-1234-abcd-123456789012-12345678-6a1f-47c3-b291-123456789012.zip",
      "Handler":"pageLoadBlueprint.handler"
   },
   "RunConfig": {
      "TimeoutInSeconds":60,
      "EnvironmentVariables": {
         "Environment":"Production",
         "Region": "us-west-1"
      }
   },
   "SuccessRetentionPeriodInDays":13,
   "FailureRetentionPeriodInDays":13,
   "RuntimeVersion":"syn-nodejs-2.0"
}'
```

## Integración del valor controlado con otros servicios de AWS
<a name="CloudWatch_Synthetics_Canaries_AWS_integrate"></a>

Todos los canaries pueden utilizar la biblioteca de AWS SDK. Puede utilizar esta biblioteca cuando escriba su valor controlado para integrarlo con otros servicios de AWS.

Para ello, debe agregar el siguiente código al valor controlado. Para estos ejemplos, AWS Secrets Manager se utiliza como servicio para la integración del valor controlado.
+ Importar el SDK de AWS.

  ```
  const AWS = require('aws-sdk');
  ```
+ Cree un cliente para el servicio de AWS con el que se está integrando.

  ```
  const secretsManager = new AWS.SecretsManager();
  ```
+ Use el cliente para realizar llamadas a la API a ese servicio.

  ```
  var params = {
    SecretId: secretName
  };
  return await secretsManager.getSecretValue(params).promise();
  ```

El siguiente fragmento de código de script de valor controlado muestra un ejemplo de integración con Secrets Manager con más detalle.

```
var synthetics = require('@aws/synthetics-puppeteer');
const log = require('@aws/synthetics-logger');
 
const AWS = require('aws-sdk');
const secretsManager = new AWS.SecretsManager();
 
const getSecrets = async (secretName) => {
    var params = {
        SecretId: secretName
    };
    return await secretsManager.getSecretValue(params).promise();
}
 
const secretsExample = async function () {
    let URL = "<URL>";
    let page = await synthetics.getPage();
    
    log.info(`Navigating to URL: ${URL}`);
    const response = await page.goto(URL, {waitUntil: 'domcontentloaded', timeout: 30000});
    
    // Fetch secrets
    let secrets = await getSecrets("secretname")
   
    /**
    * Use secrets to login. 
    *
    * Assuming secrets are stored in a JSON format like:
    * {
    *   "username": "<USERNAME>",
    *   "password": "<PASSWORD>"
    * }
    **/
    let secretsObj = JSON.parse(secrets.SecretString);
    await synthetics.executeStep('login', async function () {
        await page.type(">USERNAME-INPUT-SELECTOR<", secretsObj.username);
        await page.type(">PASSWORD-INPUT-SELECTOR<", secretsObj.password);
        
        await Promise.all([
          page.waitForNavigation({ timeout: 30000 }),
          await page.click(">SUBMIT-BUTTON-SELECTOR<")
        ]);
    });
   
    // Verify login was successful
    await synthetics.executeStep('verify', async function () {
        await page.waitForXPath(">SELECTOR<", { timeout: 30000 });
    });
};

exports.handler = async () => {
    return await secretsExample();
};
```

## Forzar al valor controlado para que utilice una dirección IP estática
<a name="CloudWatch_Synthetics_Canaries_staticIP"></a>

Se puede configurar un valor controlado para que utilice una dirección IP estática.

**Para forzar a un valor controlado a utilizar una dirección IP estática**

1. Cree una nueva VPC Para obtener más información, consulte [Utilización de DNS con su VPC](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-dns.html).

1. Cree una gateway de Internet. Para obtener más información, consulte [Adding an internet gateway to your VPC](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html#working-with-igw) (Cómo añadir una gateway de Internet a la VPC).

1. Cree una subred pública en la nueva VPC.

1. Agregue una nueva tabla de enrutamiento a la VPC.

1. Agregue una ruta en la nueva tabla de enrutamiento, que va desde `0.0.0.0/0` a la gateway de Internet.

1. Asocie la nueva tabla de enrutamiento con la subred pública.

1. Cree una dirección IP elástica Para obtener más información, consulte [Elastic IP addresses](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html) (Direcciones IP elásticas).

1. Cree una nueva gateway NAT y asígnela a la subred pública y a la dirección IP elástica.

1. Cree las subredes privadas en la VPC

1. Agregue una ruta a la tabla de enrutamiento predeterminada de la VPC, que va desde `0.0.0.0/0` a la gateway NAT

1. Cree el valor controlado. 