Skip to content

Commit 3e6febb

Browse files
authored
Fix lambda-manager subscriptions when LogGroupClass is omitted (#193)
* Fix lambda-manager when LogGroupClass is omitted * Avoid PyYAML in lambda-manager template test
1 parent bb65ec5 commit 3e6febb

5 files changed

Lines changed: 75 additions & 2 deletions

File tree

src/lambda-manager/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
All notable changes to this project will be documented in this file.
44
This format is based on Keep a Changelog.
55

6+
## [2.0.12] - 2026-06-15
7+
### Fixed
8+
- Match CloudFormation `CreateLogGroup` events when `LogGroupClass` is omitted and the log group still defaults to `STANDARD`.
9+
- Continue skipping non-standard `INFREQUENT_ACCESS` and `DELIVERY` log groups during create-event processing.
10+
611
## [2.0.11] - 2026-06-13
712
### Fixed
813
- Ignore failed CloudTrail `CreateLogGroup` events so they do not trigger duplicate subscription work.

src/lambda-manager/lambda_function.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
cloudwatch_logs = boto3.client('logs', config=config)
2727
lambda_client = boto3.client('lambda', config=config)
2828

29+
SUPPORTED_LOG_GROUP_CLASS = "STANDARD"
30+
2931
def lambda_handler(event: Dict[str, Any], context) -> None:
3032
"""
3133
Main Lambda function handler that manages CloudWatch log group subscriptions.
@@ -94,6 +96,9 @@ def lambda_handler(event: Dict[str, Any], context) -> None:
9496
return
9597

9698
log_group_to_subscribe = event_detail['requestParameters']['logGroupName']
99+
100+
if not should_process_log_group_class(event_detail):
101+
return
97102
found_log_group_in_regex_pattern = False
98103

99104
for regex_pattern in regex_pattern_list:
@@ -250,6 +255,21 @@ def should_create_subscription(cloudwatch_logs, log_group_name: str, destination
250255

251256
return True
252257

258+
def should_process_log_group_class(event_detail: Dict[str, Any]) -> bool:
259+
"""
260+
Allow standard log groups, including events where CloudTrail omits logGroupClass.
261+
262+
CloudFormation-created log groups default to STANDARD even when the property is
263+
not present in requestParameters. Explicit non-standard classes should still be
264+
ignored to preserve the existing behavior.
265+
"""
266+
log_group_class = event_detail.get('requestParameters', {}).get('logGroupClass', SUPPORTED_LOG_GROUP_CLASS)
267+
if log_group_class == SUPPORTED_LOG_GROUP_CLASS:
268+
return True
269+
270+
logger.info(f"Skipping log group because logGroupClass {log_group_class} is not supported")
271+
return False
272+
253273
def add_subscription(
254274
filter_name: str,
255275
logs_filter: str,

src/lambda-manager/template.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Metadata:
1616
- cloudwatch
1717
- lambda
1818
HomePageUrl: https://coralogix.com
19-
SemanticVersion: 2.0.11
19+
SemanticVersion: 2.0.12
2020
SourceCodeUrl: https://github.com/coralogix/coralogix-aws-serverless
2121
AWS::CloudFormation::Interface:
2222
ParameterGroups:
@@ -215,6 +215,7 @@ Resources:
215215
requestParameters:
216216
logGroupClass:
217217
- "STANDARD"
218+
- exists: false
218219
Target:
219220
Id: cx-loggroup-target
220221

src/lambda-manager/tests/test_lambda_function.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def test_existing_destination_subscription_is_not_duplicated(self):
107107
add_permission.assert_not_called()
108108
add_subscription.assert_not_called()
109109

110-
def test_new_destination_subscription_still_gets_created(self):
110+
def test_event_without_log_group_class_still_gets_subscribed(self):
111111
event = {"detail": {"requestParameters": {"logGroupName": "/aws/lambda/example"}}}
112112
self.module.cloudwatch_logs.describe_subscription_filters.return_value = {"subscriptionFilters": []}
113113

@@ -124,6 +124,25 @@ def test_new_destination_subscription_still_gets_created(self):
124124
)
125125
add_subscription.assert_called_once()
126126

127+
def test_non_standard_log_group_class_is_ignored(self):
128+
for log_group_class in ("INFREQUENT_ACCESS", "DELIVERY"):
129+
event = {
130+
"detail": {
131+
"requestParameters": {
132+
"logGroupName": "/aws/lambda/example",
133+
"logGroupClass": log_group_class,
134+
}
135+
}
136+
}
137+
138+
with self.subTest(log_group_class=log_group_class):
139+
with patch.object(self.module, "add_permission_to_lambda") as add_permission, patch.object(
140+
self.module, "add_subscription"
141+
) as add_subscription:
142+
self.invoke_handler(event)
143+
144+
add_permission.assert_not_called()
145+
add_subscription.assert_not_called()
127146

128147
if __name__ == "__main__":
129148
unittest.main()
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import re
2+
import unittest
3+
from pathlib import Path
4+
5+
6+
TEMPLATE_PATH = Path(__file__).resolve().parents[1] / "template.yaml"
7+
8+
9+
class LambdaManagerTemplateTests(unittest.TestCase):
10+
def test_eventbridge_pattern_matches_standard_or_missing_log_group_class(self):
11+
template = TEMPLATE_PATH.read_text()
12+
match = re.search(r"logGroupClass:\n((?:\s+-.*\n)+)", template)
13+
14+
self.assertIsNotNone(match)
15+
16+
log_group_class_pattern = [line.strip() for line in match.group(1).splitlines()]
17+
18+
self.assertEqual(
19+
[
20+
'- "STANDARD"',
21+
"- exists: false",
22+
],
23+
log_group_class_pattern,
24+
)
25+
26+
27+
if __name__ == "__main__":
28+
unittest.main()

0 commit comments

Comments
 (0)