From 1ab9b265e9899ffcd093b3600d658c8a6519cc69 Mon Sep 17 00:00:00 2001 From: Yuto Osawa <7953650+tandfy@users.noreply.github.com> Date: Tue, 2 Nov 2021 09:03:29 +0900 Subject: [PATCH] feat(synthetics): add static cron method to schedule class (#17250) closes #16402 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-synthetics/README.md | 8 ++- .../@aws-cdk/aws-synthetics/lib/schedule.ts | 72 +++++++++++++++++++ .../aws-synthetics/test/canary.test.ts | 21 ++++++ .../aws-synthetics/test/schedule.test.ts | 26 +++++++ 4 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 packages/@aws-cdk/aws-synthetics/test/schedule.test.ts diff --git a/packages/@aws-cdk/aws-synthetics/README.md b/packages/@aws-cdk/aws-synthetics/README.md index 7f7665e02238c..013205a4ce3e4 100644 --- a/packages/@aws-cdk/aws-synthetics/README.md +++ b/packages/@aws-cdk/aws-synthetics/README.md @@ -97,13 +97,15 @@ object to the `schedule` property. Configure a run rate of up to 60 minutes with `Schedule.rate`: ```ts -Schedule.rate(Duration.minutes(5)), // Runs every 5 minutes. +Schedule.rate(Duration.minutes(5)) // Runs every 5 minutes. ``` -You can also specify a [cron expression](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_cron.html) via `Schedule.expression`: +You can also specify a [cron expression](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_cron.html) with `Schedule.cron`: ```ts -Schedule.expression('cron(0 0,8,16 * * ? *)'), // Run at 12am, 8am, 4pm UTC every day +Schedule.cron({ + hour: '0,8,16' // Run at 12am, 8am, 4pm UTC every day +}) ``` If you want the canary to run just once upon deployment, you can use `Schedule.once()`. diff --git a/packages/@aws-cdk/aws-synthetics/lib/schedule.ts b/packages/@aws-cdk/aws-synthetics/lib/schedule.ts index 3bd92c81b4d0b..248b44e4c59cb 100644 --- a/packages/@aws-cdk/aws-synthetics/lib/schedule.ts +++ b/packages/@aws-cdk/aws-synthetics/lib/schedule.ts @@ -42,9 +42,81 @@ export class Schedule { return new Schedule(`rate(${minutes} minutes)`); } + /** + * Create a schedule from a set of cron fields + */ + public static cron(options: CronOptions): Schedule { + if (options.weekDay !== undefined && options.day !== undefined) { + throw new Error('Cannot supply both \'day\' and \'weekDay\', use at most one'); + } + + const minute = fallback(options.minute, '*'); + const hour = fallback(options.hour, '*'); + const month = fallback(options.month, '*'); + + // Weekday defaults to '?' if not supplied. If it is supplied, day must become '?' + const day = fallback(options.day, options.weekDay !== undefined ? '?' : '*'); + const weekDay = fallback(options.weekDay, '?'); + + // '*' is only allowed in the year field + const year = '*'; + + return new Schedule(`cron(${minute} ${hour} ${day} ${month} ${weekDay} ${year})`); + } + private constructor( /** * The Schedule expression */ public readonly expressionString: string) {} } + + +/** + * Options to configure a cron expression + * + * All fields are strings so you can use complex expressions. Absence of + * a field implies '*' or '?', whichever one is appropriate. + * + * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_cron.html + */ +export interface CronOptions { + /** + * The minute to run this rule at + * + * @default - Every minute + */ + readonly minute?: string; + + /** + * The hour to run this rule at + * + * @default - Every hour + */ + readonly hour?: string; + + /** + * The day of the month to run this rule at + * + * @default - Every day of the month + */ + readonly day?: string; + + /** + * The month to run this rule at + * + * @default - Every month + */ + readonly month?: string; + + /** + * The day of the week to run this rule at + * + * @default - Any day of the week + */ + readonly weekDay?: string; +} + +function fallback(x: string | undefined, def: string): string { + return x ?? def; +} diff --git a/packages/@aws-cdk/aws-synthetics/test/canary.test.ts b/packages/@aws-cdk/aws-synthetics/test/canary.test.ts index 27491d01b2850..83ba173562834 100644 --- a/packages/@aws-cdk/aws-synthetics/test/canary.test.ts +++ b/packages/@aws-cdk/aws-synthetics/test/canary.test.ts @@ -292,6 +292,27 @@ test('Schedule can be set to 1 minute', () => { }); }); +test('Schedule can be set with Cron', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new synthetics.Canary(stack, 'Canary', { + schedule: synthetics.Schedule.cron({ minute: '30' }), + test: synthetics.Test.custom({ + handler: 'index.handler', + code: synthetics.Code.fromInline('/* Synthetics handler code */'), + }), + runtime: synthetics.Runtime.SYNTHETICS_NODEJS_PUPPETEER_3_3, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Synthetics::Canary', { + Schedule: Match.objectLike({ Expression: 'cron(30 * * * ? *)' }), + }); +}); + + test('Schedule can be set with Expression', () => { // GIVEN const stack = new Stack(); diff --git a/packages/@aws-cdk/aws-synthetics/test/schedule.test.ts b/packages/@aws-cdk/aws-synthetics/test/schedule.test.ts new file mode 100644 index 0000000000000..664f27774b61f --- /dev/null +++ b/packages/@aws-cdk/aws-synthetics/test/schedule.test.ts @@ -0,0 +1,26 @@ +import * as synthetics from '../lib'; + +describe('cron', () => { + test('day and weekDay are mutex: given week day', () => { + expect(synthetics.Schedule.cron({ + weekDay: 'MON-FRI', + }).expressionString).toEqual('cron(* * ? * MON-FRI *)'); + }); + + test('day and weekDay are mutex: given month day', () => { + expect(synthetics.Schedule.cron({ + day: '1', + }).expressionString).toEqual('cron(* * 1 * ? *)'); + }); + + test('day and weekDay are mutex: given neither', () => { + expect(synthetics.Schedule.cron({}).expressionString).toEqual('cron(* * * * ? *)'); + }); + + test('day and weekDay are mutex: throw if given both', () => { + expect(() => synthetics.Schedule.cron({ + day: '1', + weekDay: 'MON', + })).toThrow('Cannot supply both \'day\' and \'weekDay\', use at most one'); + }); +});