| 1 | import type { Input } from "../../Input.ts"; |
| 2 | import * as Scheduler from "../Scheduler/index.ts"; |
| 3 | import type { Cluster } from "./Cluster.ts"; |
| 4 | |
| 5 | export interface ScheduleProps { |
| 6 | /** |
| 7 | * ECS cluster that the scheduled task should run on. |
| 8 | */ |
| 9 | cluster: Cluster; |
| 10 | |
| 11 | /** |
| 12 | * ECS task definition and roles to invoke on each schedule tick. |
| 13 | * |
| 14 | * This is typically an `AWS.ECS.Task` resource (whose attributes are |
| 15 | * `Output`s — the fields are `Input`-typed because `every` is a helper |
| 16 | * function, not resource Props). |
| 17 | */ |
| 18 | task: { |
| 19 | /** |
| 20 | * Task definition ARN to run. |
| 21 | */ |
| 22 | taskDefinitionArn: Input<string>; |
| 23 | /** |
| 24 | * Task role ARN passed through EventBridge `iam:PassRole`. |
| 25 | */ |
| 26 | taskRoleArn: Input<string>; |
| 27 | /** |
| 28 | * Execution role ARN passed through EventBridge `iam:PassRole`. |
| 29 | */ |
| 30 | executionRoleArn: Input<string>; |
| 31 | }; |
| 32 | |
| 33 | /** |
| 34 | * Subnets used for the Fargate task network configuration. |
| 35 | */ |
| 36 | subnets: Input<string[]>; |
| 37 | |
| 38 | /** |
| 39 | * Security groups attached to the scheduled task ENIs. |
| 40 | */ |
| 41 | securityGroups?: Input<string[]>; |
| 42 | |
| 43 | /** |
| 44 | * Whether the scheduled task should receive a public IP. |
| 45 | * @default false |
| 46 | */ |
| 47 | assignPublicIp?: boolean; |
| 48 | |
| 49 | /** |
| 50 | * Number of task copies to start on each invocation. |
| 51 | * @default 1 |
| 52 | */ |
| 53 | taskCount?: number; |
| 54 | |
| 55 | /** |
| 56 | * Static JSON payload forwarded to the EventBridge ECS target. |
| 57 | */ |
| 58 | input?: string; |
| 59 | } |
| 60 | |
| 61 | const toScheduleExpression = (value: string) => |
| 62 | value.startsWith("rate(") || value.startsWith("cron(") |
| 63 | ? value |
| 64 | : `rate(${value})`; |
| 65 | |
| 66 | /** |
| 67 | * Creates a scheduled EventBridge rule that runs an ECS Fargate task. |
| 68 | * |
| 69 | * `every` is the high-level ECS scheduling helper for phase 1. It provisions |
| 70 | * the EventBridge rule plus the invoke role required to call `ecs:RunTask` and |
| 71 | * `iam:PassRole` for the target task's execution roles. |
| 72 | * |
| 73 | * Plain English durations like `"1 hour"` are normalized to `rate(...)` |
| 74 | * expressions automatically. Full `rate(...)` and `cron(...)` expressions are |
| 75 | * also accepted as-is. |
| 76 | * **Example:** Run a task every hour |
| 77 | * ```typescript |
| 78 | * yield* AWS.ECS.every("HourlyJob", "1 hour", { |
| 79 | * cluster, |
| 80 | * task: jobTask, |
| 81 | * subnets: [privateSubnet1.subnetId, privateSubnet2.subnetId], |
| 82 | * securityGroups: [jobSecurityGroup.groupId], |
| 83 | * }); |
| 84 | * ``` |
| 85 | * |
| 86 | * **Example:** Use an explicit cron expression |
| 87 | * ```typescript |
| 88 | * yield* AWS.ECS.every("NightlyJob", "cron(0 3 * * ? *)", { |
| 89 | * cluster, |
| 90 | * task: nightlyTask, |
| 91 | * subnets: [privateSubnet1.subnetId, privateSubnet2.subnetId], |
| 92 | * securityGroups: [jobSecurityGroup.groupId], |
| 93 | * }); |
| 94 | * ``` |
| 95 | * |
| 96 | * **Example:** Run multiple copies with static input |
| 97 | * ```typescript |
| 98 | * yield* AWS.ECS.every("BatchJob", "30 minutes", { |
| 99 | * cluster, |
| 100 | * task: batchTask, |
| 101 | * subnets: [privateSubnet1.subnetId, privateSubnet2.subnetId], |
| 102 | * securityGroups: [jobSecurityGroup.groupId], |
| 103 | * taskCount: 3, |
| 104 | * input: JSON.stringify({ source: "scheduler" }), |
| 105 | * }); |
| 106 | * ``` |
| 107 | * |
| 108 | * @binding |
| 109 | */ |
| 110 | export const every = (id: string, schedule: string, props: ScheduleProps) => |
| 111 | Scheduler.every(toScheduleExpression(schedule)).named(id).toEcsTask({ |
| 112 | cluster: props.cluster, |
| 113 | task: props.task, |
| 114 | subnets: props.subnets, |
| 115 | securityGroups: props.securityGroups, |
| 116 | assignPublicIp: props.assignPublicIp, |
| 117 | taskCount: props.taskCount, |
| 118 | input: props.input, |
| 119 | }); |