Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(scheduler-alpha): target properties override #27603

Merged
merged 9 commits into from
Oct 26, 2023
Merged
16 changes: 15 additions & 1 deletion packages/@aws-cdk/aws-scheduler-alpha/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,22 @@ const target = new targets.LambdaInvoke(fn, {

## Overriding Target Properties

TODO: Not yet implemented. See section in [L2 Event Bridge Scheduler RFC](https://github.com/aws/aws-cdk-rfcs/blob/master/text/0474-event-bridge-scheduler-l2.md)
If you wish to reuse the same target in multiple schedules, you can override target properties like `input`,
`retryAttempts` and `maxEventAge` when creating a Schedule using the `targetOverrides` parameter:

```ts
declare const target: targets.LambdaInvoke;

const oneTimeSchedule = new Schedule(this, 'Schedule', {
schedule: ScheduleExpression.rate(cdk.Duration.hours(12)),
target,
targetOverrides: {
input: ScheduleTargetInput.fromText('Overriding Target Input'),
maxEventAge: Duration.seconds(180),
retryAttempts: 5,
},
});
```

## Monitoring

Expand Down
72 changes: 69 additions & 3 deletions packages/@aws-cdk/aws-scheduler-alpha/lib/schedule.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { IResource, Resource } from 'aws-cdk-lib';
import { Duration, IResource, Resource } from 'aws-cdk-lib';
import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch';
import * as kms from 'aws-cdk-lib/aws-kms';
import { CfnSchedule } from 'aws-cdk-lib/aws-scheduler';
import { Construct } from 'constructs';
import { IGroup } from './group';
import { ScheduleTargetInput } from './input';
import { ScheduleExpression } from './schedule-expression';
import { IScheduleTarget } from './target';

Expand All @@ -15,10 +16,12 @@ export interface ISchedule extends IResource {
* The name of the schedule.
*/
readonly scheduleName: string;

/**
* The schedule group associated with this schedule.
*/
readonly group?: IGroup;

/**
* The arn of the schedule.
*/
Expand All @@ -30,6 +33,32 @@ export interface ISchedule extends IResource {
readonly key?: kms.IKey;
}

export interface ScheduleTargetProps {
/**
* The text, or well-formed JSON, passed to the target.
*
* If you are configuring a templated Lambda, AWS Step Functions, or Amazon EventBridge target,
* the input must be a well-formed JSON. For all other target types, a JSON is not required.
*
* @default - The target's input is used.
*/
readonly input?: ScheduleTargetInput;

/**
* The maximum amount of time, in seconds, to continue to make retry attempts.
*
* @default - The target's maximumEventAgeInSeconds is used.
*/
readonly maxEventAge?: Duration;

/**
* The maximum number of retry attempts to make before the request fails.
*
* @default - The target's maximumRetryAttempts is used.
*/
readonly retryAttempts?: number;
}

/**
* Construction properties for `Schedule`.
*/
Expand All @@ -45,6 +74,11 @@ export interface ScheduleProps {
*/
readonly target: IScheduleTarget;

/**
* Allows to override target properties when creating a new schedule.
*/
readonly targetOverrides?: ScheduleTargetProps;

/**
* The name of the schedule.
*
Expand Down Expand Up @@ -183,10 +217,12 @@ export class Schedule extends Resource implements ISchedule {
* The schedule group associated with this schedule.
*/
public readonly group?: IGroup;

/**
* The arn of the schedule.
*/
public readonly scheduleArn: string;

/**
* The name of the schedule.
*/
Expand All @@ -197,6 +233,11 @@ export class Schedule extends Resource implements ISchedule {
*/
readonly key?: kms.IKey;

/**
* A `RetryPolicy` object that includes information about the retry policy settings.
*/
private readonly retryPolicy?: CfnSchedule.RetryPolicyProperty;

constructor(scope: Construct, id: string, props: ScheduleProps) {
super(scope, id, {
physicalName: props.scheduleName,
Expand All @@ -211,6 +252,8 @@ export class Schedule extends Resource implements ISchedule {
this.key.grantEncryptDecrypt(targetConfig.role);
}

this.retryPolicy = targetConfig.retryPolicy;

const resource = new CfnSchedule(this, 'Resource', {
name: this.physicalName,
flexibleTimeWindow: { mode: 'OFF' },
Expand All @@ -222,9 +265,11 @@ export class Schedule extends Resource implements ISchedule {
target: {
arn: targetConfig.arn,
roleArn: targetConfig.role.roleArn,
input: targetConfig.input?.bind(this),
input: props.targetOverrides?.input ?
props.targetOverrides?.input?.bind(this) :
targetConfig.input?.bind(this),
deadLetterConfig: targetConfig.deadLetterConfig,
retryPolicy: targetConfig.retryPolicy,
retryPolicy: this.renderRetryPolicy(props.targetOverrides?.maxEventAge?.toSeconds(), props.targetOverrides?.retryAttempts),
ecsParameters: targetConfig.ecsParameters,
kinesisParameters: targetConfig.kinesisParameters,
eventBridgeParameters: targetConfig.eventBridgeParameters,
Expand All @@ -240,4 +285,25 @@ export class Schedule extends Resource implements ISchedule {
resourceName: `${this.group?.groupName ?? 'default'}/${this.physicalName}`,
});
}

private renderRetryPolicy(
maximumEventAgeInSeconds?: number,
maximumRetryAttempts?: number,
): CfnSchedule.RetryPolicyProperty | undefined {
const policy = {
...this.retryPolicy,
maximumEventAgeInSeconds: maximumEventAgeInSeconds ?? this.retryPolicy?.maximumEventAgeInSeconds,
maximumRetryAttempts: maximumRetryAttempts ?? this.retryPolicy?.maximumRetryAttempts,
};

if (policy.maximumEventAgeInSeconds && (policy.maximumEventAgeInSeconds < 60 || policy.maximumEventAgeInSeconds > 86400)) {
throw new Error(`maximumEventAgeInSeconds must be between 60 and 86400, got ${policy.maximumEventAgeInSeconds}`);
}
if (policy.maximumRetryAttempts && (policy.maximumRetryAttempts < 0 || policy.maximumRetryAttempts > 185)) {
throw new Error(`maximumRetryAttempts must be between 0 and 185, got ${policy.maximumRetryAttempts}`);
}

const isEmptyPolicy = Object.values(policy).every(value => value === undefined);
return !isEmptyPolicy ? policy : undefined;
}
}
20 changes: 20 additions & 0 deletions packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,24 @@ describe('schedule target input', () => {
},
});
});

test('can override target input', () => {
// WHEN
const input = ScheduleTargetInput.fromText('Original Input');
new Schedule(stack, 'TestSchedule', {
schedule: expr,
target: new SomeLambdaTarget(func, input),
targetOverrides: {
input: ScheduleTargetInput.fromText('Overridden Input'),
},
enabled: false,
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::Scheduler::Schedule', {
Target: {
Input: '"Overridden Input"',
},
});
});
});

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@
"Arn"
]
},
"Input": "\"Input Text\"",
"RetryPolicy": {
"MaximumEventAgeInSeconds": 180,
"MaximumRetryAttempts": 3
},
"RoleArn": {
"Fn::GetAtt": [
"Role1ABCC5F0",
Expand All @@ -139,6 +144,41 @@
"Arn"
]
},
"Input": "\"Input Text\"",
"RetryPolicy": {
"MaximumEventAgeInSeconds": 180,
"MaximumRetryAttempts": 3
},
"RoleArn": {
"Fn::GetAtt": [
"Role1ABCC5F0",
"Arn"
]
}
}
}
},
"TargetOverrideScheduleFF8CB184": {
"Type": "AWS::Scheduler::Schedule",
"Properties": {
"FlexibleTimeWindow": {
"Mode": "OFF"
},
"ScheduleExpression": "rate(12 hours)",
"ScheduleExpressionTimezone": "Etc/UTC",
"State": "ENABLED",
"Target": {
"Arn": {
"Fn::GetAtt": [
"Function76856677",
"Arn"
]
},
"Input": "\"Changed Text\"",
"RetryPolicy": {
"MaximumEventAgeInSeconds": 360,
"MaximumRetryAttempts": 5
},
"RoleArn": {
"Fn::GetAtt": [
"Role1ABCC5F0",
Expand Down Expand Up @@ -217,6 +257,11 @@
"Arn"
]
},
"Input": "\"Input Text\"",
"RetryPolicy": {
"MaximumEventAgeInSeconds": 180,
"MaximumRetryAttempts": 3
},
"RoleArn": {
"Fn::GetAtt": [
"Role1ABCC5F0",
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading