canary 스크립트 샘플 코드
이 단원에는 CloudWatch Synthetics canary 스크립트에 사용할 수 있는 몇 가지 함수를 보여 주는 코드 샘플이 포함되어 있습니다.
Node.js 및 Playwright의 샘플
여러 단계로 된 Playwright 카나리
아래의 스크립트는 여러 단계로 된 Node.js Playwright 카나리의 예제입니다.
import { synthetics } from '@amzn/synthetics-playwright'; export async function handler(event, context) { try { console.log('Running Synthetics Playwright canary'); const browser = await synthetics.launch(); const browserContext = await browser.newContext(); const page = await synthetics.getPage(browserContext); // Add steps // Step 1 await synthetics.executeStep("home-page", async () => { console.log("Verify home page loads") await page.goto('https://www.amazon.com', {waitUntil: "load"}); await new Promise(r => setTimeout(r, 5000)); }); // Step 2 await synthetics.executeStep("search", async () => { console.log("Searching for a product") const searchInput = page.getByPlaceholder("Search Amazon").first(); await searchInput.click() await searchInput.fill('Amazon echo'); const btn = page.getByRole('button', { name: 'Go' }).first() await btn.click({ timeout: 15000 }) console.log("Clicked search button") }); // Step 3 await synthetics.executeStep("search-results", async () => { console.log("Verifying search results") const resultsHeading = page.getByText("Results", {exact: true}).first() await resultsHeading.highlight(); await new Promise(r => setTimeout(r, 5000)); }); } finally { // Close all browser contexts and browser await synthetics.close(); } }
Playwright 카나리 설정 쿠키
아래의 스크립트는 여러 단계로 된 Node.js Playwright 카나리 설정인 3가지 쿠키의 예제입니다.
import { synthetics } from '@amzn/synthetics-playwright'; export const handler = async (event, context) => { try { let url = "http://smile.amazon.com/"; const browser = await synthetics.launch(); const page = await synthetics.getPage(browser); const cookies = [{ 'name': 'cookie1', 'value': 'val1', 'url': url }, { 'name': 'cookie2', 'value': 'val2', 'url': url }, { 'name': 'cookie3', 'value': 'val3', 'url': url } ]; await page.context().addCookies(cookies); await page.goto(url, {waitUntil: 'load', timeout: 30000}); await page.screenshot({ path: '/tmp/smile.png' }); } finally { await synthetics.close(); } };
Node.js 및 Puppeteer 샘플
쿠키 설정
웹 사이트는 사용자 지정 기능을 제공하거나 사용자를 추적하기 위해 쿠키를 사용합니다. CloudWatch Synthetics 스크립트에서 쿠키를 설정함으로써 이 사용자 지정 동작을 모방하고 검증할 수 있습니다.
예를 들어 웹 사이트는 재방문 사용자에 대해 [등록(Register)] 링크 대신 [로그인(Login)] 링크를 표시할 수 있습니다.
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 canary
이 샘플 코드는 양성 및 음성 테스트 사례에 대해 동일한 API를 테스트하는 두 HTTP 단계로 API canary를 보여 줍니다. 단계 구성이 전달되어 요청 또는 응답 헤더의 보고를 사용 설정합니다. 또한 X-Amz-Security-Token 및 Authorization 헤더를 숨깁니다. 이러한 헤더에 사용자 자격 증명이 포함되어 있기 때문입니다.
이 스크립트를 canary로 사용하면 각 단계 및 관련 HTTP 요청에 관한 세부 정보(예: 단계 통과 또는 실패, 지속 시간 그리고 DNS 조회 시간 및 첫 번째 바이트 시간과 같은 성능 지표)를 볼 수 있습니다. canary 실행에 대한 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 샘플 코드는 대상 요소가 로드되지 않을 때 사용자 지정 오류 메시지를 표시하며 실패하는 canary입니다.
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()