Skip to content

Commit

Permalink
feat(scheduler-alpha): target properties override (#27603)
Browse files Browse the repository at this point in the history
Allows to override a `Schedule` target's properties.
Supported properties: `input`, `maxEventAge`, and `retryAttempts`.

Example:
```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,
    },
});
```

Closes #27545.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
lpizzinidev authored Oct 26, 2023
1 parent 7e426c8 commit 1433ff2
Show file tree
Hide file tree
Showing 9 changed files with 372 additions and 8 deletions.
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

0 comments on commit 1433ff2

Please sign in to comment.