本节包含对 CloudWatch Synthetics 金丝雀脚本的一些可能的函数进行说明的代码示例。
Node.js 和 Puppeteer 示例
设置 Cookie
网站依赖 Cookie 来提供自定义功能或跟踪用户。通过在 CloudWatch Synthetics 脚本中设置 Cookie,您可以模拟此自定义行为并对其进行验证。
例如,某个网站可能会对再次访问该网站的用户显示登录链接,而不是注册链接。
var synthetics = require('Synthetics');
const log = require('SyntheticsLogger');
const pageLoadBlueprint = async function () {
let url = "http://smile.amazon.com/";
let page = await synthetics.getPage();
// Set cookies. I found that name, value, and either url or domain are required fields.
const cookies = [{
'name': 'cookie1',
'value': 'val1',
'url': url
},{
'name': 'cookie2',
'value': 'val2',
'url': url
},{
'name': 'cookie3',
'value': 'val3',
'url': url
}];
await page.setCookie(...cookies);
// Navigate to the url
await synthetics.executeStep('pageLoaded_home', async function (timeoutInMillis = 30000) {
var response = await page.goto(url, {waitUntil: ['load', 'networkidle0'], timeout: timeoutInMillis});
// Log cookies for this page and this url
const cookiesSet = await page.cookies(url);
log.info("Cookies for url: " + url + " are set to: " + JSON.stringify(cookiesSet));
});
};
exports.handler = async () => {
return await pageLoadBlueprint();
};
设备仿真
您可以编写模拟各种设备的脚本,以便粗略估计页面在这些设备上的外观和行为。
以下示例模拟的是 iPhone 6 设备。有关仿真的更多信息,请参阅 Puppeteer 文档中的 page.emulate(options)
var synthetics = require('Synthetics');
const log = require('SyntheticsLogger');
const puppeteer = require('puppeteer-core');
const pageLoadBlueprint = async function () {
const iPhone = puppeteer.devices['iPhone 6'];
// INSERT URL here
const URL = "https://amazon.com";
let page = await synthetics.getPage();
await page.emulate(iPhone);
//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!";
}
await page.waitFor(15000);
await synthetics.takeScreenshot('loaded', 'loaded');
//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 pageLoadBlueprint();
};
多步骤 API 金丝雀
此示例代码演示了一个具有两个 HTTP 步骤的 API 金丝雀:对正面和负面测试用例测试同一个 API。传递步骤配置以启用请求/响应标头报告。此外,其会隐藏 Authorization(授权)标头和 X-Amz-Security-Token,因为它们二者包含用户凭证。
当此脚本用作金丝雀时,您可以查看有关每个步骤和相关 HTTP 请求的详细信息,例如步骤通过/失败、持续时间和性能指标(如 DNS 查找时间和第一字节时间)。也可以查看金丝雀运行的 2xx、4xx 和 5xx 的数量。
var synthetics = require('Synthetics');
const log = require('SyntheticsLogger');
const apiCanaryBlueprint = async function () {
// Handle validation for positive scenario
const validatePositiveCase = async function(res) {
return new Promise((resolve, reject) => {
if (res.statusCode < 200 || res.statusCode > 299) {
throw res.statusCode + ' ' + res.statusMessage;
}
let responseBody = '';
res.on('data', (d) => {
responseBody += d;
});
res.on('end', () => {
// Add validation on 'responseBody' here if required. For ex, your status code is 200 but data might be empty
resolve();
});
});
};
// Handle validation for negative scenario
const validateNegativeCase = async function(res) {
return new Promise((resolve, reject) => {
if (res.statusCode < 400) {
throw res.statusCode + ' ' + res.statusMessage;
}
resolve();
});
};
let requestOptionsStep1 = {
'hostname': 'myproductsEndpoint.com',
'method': 'GET',
'path': '/test/product/validProductName',
'port': 443,
'protocol': 'https:'
};
let headers = {};
headers['User-Agent'] = [synthetics.getCanaryUserAgentString(), headers['User-Agent']].join(' ');
requestOptionsStep1['headers'] = headers;
// By default headers, post data and response body are not included in the report for security reasons.
// Change the configuration at global level or add as step configuration for individual steps
let stepConfig = {
includeRequestHeaders: true,
includeResponseHeaders: true,
restrictedHeaders: ['X-Amz-Security-Token', 'Authorization'], // Restricted header values do not appear in report generated.
includeRequestBody: true,
includeResponseBody: true
};
await synthetics.executeHttpStep('Verify GET products API with valid name', requestOptionsStep1, validatePositiveCase, stepConfig);
let requestOptionsStep2 = {
'hostname': 'myproductsEndpoint.com',
'method': 'GET',
'path': '/test/canary/InvalidName(',
'port': 443,
'protocol': 'https:'
};
headers = {};
headers['User-Agent'] = [synthetics.getCanaryUserAgentString(), headers['User-Agent']].join(' ');
requestOptionsStep2['headers'] = headers;
// By default headers, post data and response body are not included in the report for security reasons.
// Change the configuration at global level or add as step configuration for individual steps
stepConfig = {
includeRequestHeaders: true,
includeResponseHeaders: true,
restrictedHeaders: ['X-Amz-Security-Token', 'Authorization'], // Restricted header values do not appear in report generated.
includeRequestBody: true,
includeResponseBody: true
};
await synthetics.executeHttpStep('Verify GET products API with invalid name', requestOptionsStep2, validateNegativeCase, stepConfig);
};
exports.handler = async () => {
return await apiCanaryBlueprint();
};
Python 和 Selenium 示例
以下示例 Selenium 代码是一个金丝雀,当未加载目标元素时,此金丝雀会失败并显示自定义错误消息。
from aws_synthetics.selenium import synthetics_webdriver as webdriver
from aws_synthetics.common import synthetics_logger as logger
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
def custom_selenium_script():
# create a browser instance
browser = webdriver.Chrome()
browser.get('https://www.example.com/')
logger.info('navigated to home page')
# set cookie
browser.add_cookie({'name': 'foo', 'value': 'bar'})
browser.get('https://www.example.com/')
# save screenshot
browser.save_screenshot('signed.png')
# expected status of an element
button_condition = EC.element_to_be_clickable((By.CSS_SELECTOR, '.submit-button'))
# add custom error message on failure
WebDriverWait(browser, 5).until(button_condition, message='Submit button failed to load').click()
logger.info('Submit button loaded successfully')
# browser will be quit automatically at the end of canary run,
# quit action is not necessary in the canary script
browser.quit()
# entry point for the canary
def handler(event, context):
return custom_selenium_script()