Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion localstack-core/localstack/services/events/rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,29 @@

TARGET_ID_REGEX = re.compile(r"^[\.\-_A-Za-z0-9]+$")
TARGET_ARN_REGEX = re.compile(r"arn:[\d\w:\-/]*")
RULE_SCHEDULE_CRON_REGEX = re.compile(r"^cron\(.*\)")
CRON_REGEX = ( # borrowed from https://regex101.com/r/I80Eu0/1
r"^(?:cron[(](?:(?:(?:[0-5]?[0-9])|[*])(?:(?:[-](?:(?:[0-5]?[0-9])|[*]))|(?:[/][0-9]+))?"
r"(?:[,](?:(?:[0-5]?[0-9])|[*])(?:(?:[-](?:(?:[0-5]?[0-9])|[*]))|(?:[/][0-9]+))?)*)[ ]+"
r"(?:(?:(?:[0-2]?[0-9])|[*])(?:(?:[-](?:(?:[0-2]?[0-9])|[*]))|(?:[/][0-9]+))?"
r"(?:[,](?:(?:[0-2]?[0-9])|[*])(?:(?:[-](?:(?:[0-2]?[0-9])|[*]))|(?:[/][0-9]+))?)*)[ ]+"
r"(?:(?:[?][ ]+(?:(?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])"
r"(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[/][0-9]+)?)|"
r"(?:[/][0-9]+))?(?:[,](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])"
r"(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[/][0-9]+)?)|"
r"(?:[/][0-9]+))?)*)[ ]+(?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))[#][0-5])|"
r"(?:(?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|[L*])(?:(?:[-](?:(?:(?:[1-7]|"
r"(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|[L*]))|(?:[/][0-9]+))?(?:[,](?:(?:(?:[1-7]|"
r"(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|[L*])(?:(?:[-](?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|"
r"[L*]))|(?:[/][0-9]+))?)*)))|(?:(?:(?:(?:(?:[1-3]?[0-9])W?)|LW|[L*])(?:(?:[-](?:(?:(?:[1-3]?[0-9])W?)|"
r"LW|[L*]))|(?:[/][0-9]+))?(?:[,](?:(?:(?:[1-3]?[0-9])W?)|LW|[L*])(?:(?:[-](?:(?:(?:[1-3]?[0-9])W?)|"
r"LW|[L*]))|(?:[/][0-9]+))?)*)[ ]+(?:(?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|"
r"[*])(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[/][0-9]+)?)|"
r"(?:[/][0-9]+))?(?:[,](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])"
r"(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[/][0-9]+)?)|"
r"(?:[/][0-9]+))?)*)[ ]+[?]))[ ]+(?:(?:(?:[12][0-9]{3})|[*])(?:(?:[-](?:(?:[12][0-9]{3})|[*]))|"
r"(?:[/][0-9]+))?(?:[,](?:(?:[12][0-9]{3})|[*])(?:(?:[-](?:(?:[12][0-9]{3})|[*]))|(?:[/][0-9]+))?)*)[)])$"
)
RULE_SCHEDULE_CRON_REGEX = re.compile(CRON_REGEX)
RULE_SCHEDULE_RATE_REGEX = re.compile(r"^rate\(\d*\s(minute|minutes|hour|hours|day|days)\)")


Expand Down
4 changes: 2 additions & 2 deletions localstack-core/localstack/services/events/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
ARCHIVE_NAME_ARN_PATTERN = re.compile(
rf"{ARN_PARTITION_REGEX}:events:[a-z0-9-]+:\d{{12}}:archive/(?P<name>.+)$"
)
CONNCTION_NAME_ARN_PATTERN = re.compile(
CONNECTION_NAME_ARN_PATTERN = re.compile(
rf"{ARN_PARTITION_REGEX}:events:[a-z0-9-]+:\d{{12}}:connection/(?P<name>[^/]+)/(?P<id>[^/]+)$"
)

Expand Down Expand Up @@ -98,7 +98,7 @@ def extract_event_bus_name(
def extract_connection_name(
connection_arn: ConnectionArn,
) -> ConnectionName:
match = CONNCTION_NAME_ARN_PATTERN.match(connection_arn)
match = CONNECTION_NAME_ARN_PATTERN.match(connection_arn)
if not match:
raise ValidationException(
f"Parameter {connection_arn} is not valid. Reason: Provided Arn is not in correct format."
Expand Down
24 changes: 24 additions & 0 deletions tests/aws/services/events/test_events_schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,13 @@ class TestScheduleCron:
@pytest.mark.parametrize(
"schedule_cron",
[
"cron(0 2 ? * SAT *)", # Run at 2:00 am every Saturday
"cron(0 12 * * ? *)", # Run at 12:00 pm every day
"cron(5,35 14 * * ? *)", # Run at 2:05 pm and 2:35 pm every day
"cron(15 10 ? * 6L 2002-2005)", # Run at 10:15 am on the last Friday of every month during the years 2002-2005
"cron(0 2 ? * SAT#3 *)", # Run at 2:00 am on the third Saturday of every month
"cron(* * ? * SAT#3 *)", # Run every minute on the third Saturday of every month
"cron(0/5 5 ? JAN 1-5 2022)", # RUN every 5 minutes on the first 5 days of January 2022
"cron(0 10 * * ? *)", # Run at 10:00 am every day
"cron(15 12 * * ? *)", # Run at 12:15 pm every day
"cron(0 18 ? * MON-FRI *)", # Run at 6:00 pm every Monday through Friday
Expand All @@ -302,6 +309,23 @@ def tests_put_rule_with_schedule_cron(
response = aws_client.events.list_rules(NamePrefix=rule_name)
snapshot.match("list-rules", response)

@markers.aws.validated
@pytest.mark.parametrize(
"schedule_cron",
[
"cron(7 20 * * NOT *)",
"cron(INVALID)",
"cron(0 dummy ? * MON-FRI *)",
"cron(71 8 1 * ? *)",
],
)
def tests_put_rule_with_invalid_schedule_cron(self, schedule_cron, events_put_rule, snapshot):
rule_name = f"rule-{short_uid()}"

with pytest.raises(ClientError) as e:
events_put_rule(Name=rule_name, ScheduleExpression=schedule_cron)
snapshot.match("invalid-put-rule", e.value.response)

@markers.aws.validated
@pytest.mark.skip("Flaky, target time can be 1min off message time")
def test_schedule_cron_target_sqs(
Expand Down
Loading
Loading