Skip to content

Commit

Permalink
feat(synthetics): Synthetics L2 Support (#8824)
Browse files Browse the repository at this point in the history
This PR specifies an implementation for a CloudWatch Canary. It will be implemented in milestones specified below. 

Closes #7687.

Currently, this PR implements up to **Milestone 2**. 

**Milestone 1**: This implementation allows customers to specify a canary with these properties: 

- [x]   `artifactBucket: IBucket`
- [x]   `role: IRole`
- [x]   `timeToLive: Duration`
- [x]   `schedule: Schedule`
- [x]   `startAfterCreation: boolean`
- [x]   `successRetentionPeriod: Duration`
- [x]   `failureRetentionPeriod: Duration`
- [x]   `canaryName: string`

And: 
- [x] generate `name` for users if not specified
- [x] metric methods that reference those emitted by CloudWatch

**Milestone 2**: This is the next step needed to allow customers to specify their own canary scripts:

- [x] `test:Test`
    - [x] `Test` class with static method `Test.custom()`
    - [x] `Code` class with static method `Code.inline()`
- [x] `Code.fromAsset()`
    - [x] validate correct folder structure for assets `nodejs/node_modules`
    - [x] validate correct file name
- [x] `Code.fromBucket()`

**Milestone 3**: These are features that make a more robust Canary API:

- [ ] static method `Test.apiEndpoint()`
- [ ] additional static method templates for `Test`
- [ ] support runConfig properties - `memorySize` and `timeout`

[README Rendered version](https://github.com/aws/aws-cdk/blob/feca976ed2dbd0dc1e2416210cc967fa53a4309a/packages/%40aws-cdk/aws-synthetics/README.md)

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
kaizencc committed Aug 14, 2020
1 parent 3178961 commit 691b349
Show file tree
Hide file tree
Showing 18 changed files with 1,925 additions and 11 deletions.
136 changes: 134 additions & 2 deletions packages/@aws-cdk/aws-synthetics/README.md
@@ -1,16 +1,148 @@
## AWS::Synthetics Construct Library
## Amazon CloudWatch Synthetics Construct Library

<!--BEGIN STABILITY BANNER-->
---

![cfn-resources: Stable](https://img.shields.io/badge/cfn--resources-stable-success.svg?style=for-the-badge)

> All classes with the `Cfn` prefix in this module ([CFN Resources](https://docs.aws.amazon.com/cdk/latest/guide/constructs.html#constructs_lib)) are always stable and safe to use.
![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge)

> The APIs of higher level constructs in this module are experimental and under active development. They are subject to non-backward compatible changes or removal in any future version. These are not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be announced in the release notes. This means that while you may use them, you may need to update your source code when upgrading to a newer version of this package.
---
<!--END STABILITY BANNER-->

This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project.
Amazon CloudWatch Synthetics allow you to monitor your application by generating **synthetic** traffic. The traffic is produced by a **canary**: a configurable script that runs on a schedule. You configure the canary script to follow the same routes and perform the same actions as a user, which allows you to continually verify your user experience even when you don't have any traffic on your applications.

## Canary

To illustrate how to use a canary, assume your application defines the following endpoint:

```bash
% curl "https://api.example.com/user/books/topbook/"
The Hitchhikers Guide to the Galaxy

```

The below code defines a canary that will hit the `books/topbook` endpoint every 5 minutes:

```ts
import * as synthetics from '@aws-cdk/aws-synthetics';

const canary = new synthetics.Canary(this, 'MyCanary', {
schedule: synthetics.Schedule.rate(Duration.minutes(5)),
test: Test.custom({
code: Code.fromAsset(path.join(__dirname, 'canary'))),
handler: 'index.handler',
}),
});
```

The following is an example of an `index.js` file which exports the `handler` function:

```js
var synthetics = require('Synthetics');
const log = require('SyntheticsLogger');

const pageLoadBlueprint = async function () {

// INSERT URL here
const URL = "https://api.example.com/user/books/topbook/";

let page = await synthetics.getPage();
const response = await page.goto(URL, {waitUntil: 'domcontentloaded', timeout: 30000});
//Wait for page to render.
//Increase or decrease wait time based on endpoint being monitored.
await page.waitFor(15000);
// This will take a screenshot that will be included in test output artifacts
await synthetics.takeScreenshot('loaded', 'loaded');
let pageTitle = await page.title();
log.info('Page title: ' + pageTitle);
if (response.status() !== 200) {
throw "Failed to load page!";
}
};

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

> **Note:** The function **must** be called `handler`.
The canary will automatically produce a CloudWatch Dashboard:

![UI Screenshot](images/ui-screenshot.png)

The Canary code will be executed in a lambda function created by Synthetics on your behalf. The Lambda function includes a custom [runtime](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_Library.html) provided by Synthetics. The provided runtime includes a variety of handy tools such as [Puppeteer](https://www.npmjs.com/package/puppeteer-core) and Chromium. To learn more about Synthetics capabilities, check out the [docs](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries.html).

### Configuring the Canary Script

To configure the script the canary executes, use the `test` property. The `test` property accepts a `Test` instance that can be initialized by the `Test` class static methods. Currently, the only implemented method is `Test.custom()`, which allows you to bring your own code. In the future, other methods will be added. `Test.custom()` accepts `code` and `handler` properties -- both are required by Synthetics to create a lambda function on your behalf.

The `synthetics.Code` class exposes static methods to bundle your code artifacts:

- `code.fromInline(code)` - specify an inline script.
- `code.fromAsset(path)` - specify a .zip file or a directory in the local filesystem which will be zipped and uploaded to S3 on deployment. See the above Note for directory structure.
- `code.fromBucket(bucket, key[, objectVersion])` - specify an S3 object that contains the .zip file of your runtime code. See the above Note for directory structure.

Using the `Code` class static initializers:

```ts
// To supply the code inline:
const canary = new Canary(this, 'MyCanary', {
test: Test.custom({
code: Code.fromInline('/* Synthetics handler code */'),
handler: 'index.handler', // must be 'index.handler'
}),
});

// To supply the code from your local filesystem:
const canary = new Canary(this, 'MyCanary', {
test: Test.custom({
code: Code.fromAsset(path.join(__dirname, 'canary')),
handler: 'index.handler', // must end with '.handler'
}),
});

// To supply the code from a S3 bucket:
const canary = new Canary(this, 'MyCanary', {
test: Test.custom({
code: Code.fromBucket(bucket, 'canary.zip'),
handler: 'index.handler', // must end with '.handler'
}),
});
```

> **Note:** For `code.fromAsset()` and `code.fromBucket()`, the canary resource requires the following folder structure:
>```
>canary/
>├── nodejs/
> ├── node_modules/
> ├── <filename>.js
>```
> See Synthetics [docs](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_WritingCanary.html).
### Alarms

You can configure a CloudWatch Alarm on a canary metric. Metrics are emitted by CloudWatch automatically and can be accessed by the following APIs:
- `canary.metricSuccessPercent()` - percentage of successful canary runs over a given time
- `canary.metricDuration()` - how much time each canary run takes, in seconds.
- `canary.metricFailed()` - number of failed canary runs over a given time

Create an alarm that tracks the canary metric:

```ts
new cloudwatch.Alarm(this, 'CanaryAlarm', {
metric: canary.metricSuccessPercent(),
evaluationPeriods: 2,
threshold: 90,
comparisonOperator: cloudwatch.ComparisonOperator.LESS_THAN_THRESHOLD,
});
```

### Future Work

- Add blueprints to the Test class [#9613](https://github.com/aws/aws-cdk/issues/9613#issue-677134857).
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 691b349

Please sign in to comment.