Skip to content

Commit 51bb003

Browse files
committed
Add support for packaging app with log retention config
Also added a warning in the docs about using this config value when generating cloudformation templates.
1 parent 41278a0 commit 51bb003

File tree

8 files changed

+90
-10
lines changed

8 files changed

+90
-10
lines changed

chalice/deploy/appgraph.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,12 @@ def build(self, config, stage_name):
5050

5151
def _create_log_group(self,
5252
config, # type: Config
53+
resource_name, # type: str
5354
log_group_name # type: str
5455
):
5556
# type: (...) -> models.LogGroup
56-
return models.LogGroup(resource_name=log_group_name,
57+
return models.LogGroup(resource_name=resource_name,
58+
log_group_name=log_group_name,
5759
retention_in_days=config.log_retention_in_days)
5860

5961
def _create_custom_domain_name(
@@ -376,9 +378,11 @@ def _create_lambda_model(self,
376378
deployment, role
377379
)
378380
if config.log_retention_in_days:
381+
log_resource_name = '%s-log-group' % name
382+
log_group_name = '/aws/lambda/%s-%s-%s' % (
383+
config.app_name, stage_name, name)
379384
resource.log_group = self._create_log_group(
380-
config,
381-
'/aws/lambda/%s-%s-%s' % (config.app_name, stage_name, name))
385+
config, log_resource_name, log_group_name)
382386
return resource
383387

384388
def _get_managed_lambda_layer(self, config):

chalice/deploy/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ class ScheduledEvent(CloudWatchEventBase):
251251
@attrs
252252
class LogGroup(ManagedModel):
253253
resource_type = 'log_group'
254+
log_group_name = attrib() # type: str
254255
retention_in_days = attrib() # type: int
255256

256257

chalice/deploy/planner.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -831,18 +831,18 @@ def _plan_loggroup(self, resource):
831831
return instructions + [
832832
models.APICall(
833833
method_name='put_retention_policy',
834-
params={'name': resource.resource_name,
834+
params={'name': resource.log_group_name,
835835
'retention_in_days': resource.retention_in_days}
836836
)
837837
]
838838
return instructions + [
839839
models.APICall(
840840
method_name='create_log_group',
841-
params={'name': resource.resource_name}
841+
params={'name': resource.log_group_name}
842842
),
843843
models.APICall(
844844
method_name='put_retention_policy',
845-
params={'name': resource.resource_name,
845+
params={'name': resource.log_group_name,
846846
'retention_in_days': resource.retention_in_days}
847847
)
848848
]

chalice/package.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,21 @@ def _generate_lambdafunction(self, resource, template):
281281
} # type: Dict[str, Any]
282282
lambdafunction_definition['Properties'].update(layers_config)
283283

284+
if resource.log_group is not None:
285+
num_days = resource.log_group.retention_in_days
286+
log_name = self._register_cfn_resource_name(
287+
resource.log_group.resource_name)
288+
log_def = {
289+
'Type': 'AWS::Logs::LogGroup',
290+
'Properties': {
291+
'LogGroupName': {
292+
'Fn::Sub': '/aws/lambda/${%s}' % cfn_name
293+
},
294+
'RetentionInDays': num_days
295+
}
296+
}
297+
resources[log_name] = log_def
298+
284299
resources[cfn_name] = lambdafunction_definition
285300
self._add_iam_role(resource, resources[cfn_name])
286301

@@ -299,6 +314,11 @@ def _add_iam_role(self, resource, cfn_resource):
299314
role = cast(models.PreCreatedIAMRole, role)
300315
cfn_resource['Properties']['Role'] = role.role_arn
301316

317+
def _generate_loggroup(self, resource, template):
318+
# type: (models.LogGroup, Dict[str, Any]) -> None
319+
# Handled in LambdaFunction generation
320+
pass
321+
302322
def _generate_restapi(self, resource, template):
303323
# type: (models.RestAPI, Dict[str, Any]) -> None
304324
resources = template['Resources']
@@ -1263,9 +1283,22 @@ def _generate_lambdafunction(self, resource, template):
12631283
role = cast(models.PreCreatedIAMRole, resource.role)
12641284
func_definition['role'] = role.role_arn
12651285

1286+
if resource.log_group is not None:
1287+
log_group = resource.log_group
1288+
num_days = log_group.retention_in_days
1289+
template['resource'].setdefault('aws_cloudwatch_log_group', {})[
1290+
log_group.resource_name] = {
1291+
'name': log_group.resource_name,
1292+
'retention_in_days': num_days,
1293+
}
12661294
template['resource'].setdefault('aws_lambda_function', {})[
12671295
resource.resource_name] = func_definition
12681296

1297+
def _generate_log_group(self, resource, remplate):
1298+
# type: (models.LogGroup, Dict[str, Any]) -> None
1299+
# Handled in LambdaFunction generation
1300+
pass
1301+
12691302
def _generate_restapi(self, resource, template):
12701303
# type: (models.RestAPI, Dict[str, Any]) -> None
12711304

docs/source/topics/configfile.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,15 @@ function log groups. Only certain values are valid, see the `AWS CloudWatch
323323
Logs docs
324324
<https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutRetentionPolicy.html#API_PutRetentionPolicy_RequestParameters>`__
325325

326+
.. warning::
327+
If you using the `chalice package` command to generate a CloudFormation template,
328+
a Log Group resource will be added to your template with the configured
329+
``log_retention_in_days``. This will cause your deployment to fail
330+
if this Log Group resource already exists (i.e. if the associated
331+
Lambda function has previously been invoked which results in Lambda
332+
automatically created a Log Group for the function). In order to use
333+
this configuration option, it should be part of the initial deployment
334+
of the Lambda function.
326335

327336
.. _lambda-config:
328337

tests/unit/deploy/test_appgraph.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,8 @@ def test_can_build_single_lambda_function_app_with_log_retention(
169169
managed_layer=None,
170170
xray=None,
171171
log_group=models.LogGroup(
172-
resource_name='/aws/lambda/%s-%s-%s' %
172+
resource_name='myfunction-log-group',
173+
log_group_name='/aws/lambda/%s-%s-%s' %
173174
(config.app_name, 'dev', 'myfunction'),
174175
retention_in_days=14)
175176
)

tests/unit/deploy/test_planner.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3187,32 +3187,34 @@ def test_can_create_log_group(self):
31873187
self.remote_state.declare_no_resources_exists()
31883188
resource = models.LogGroup(
31893189
resource_name='default-log-group',
3190+
log_group_name='/aws/lambda/func-name',
31903191
retention_in_days=14,
31913192
)
31923193
plan = self.determine_plan(resource)
31933194
assert plan == [
31943195
models.APICall(
31953196
method_name='create_log_group',
3196-
params={'name': 'default-log-group'}
3197+
params={'name': '/aws/lambda/func-name'}
31973198
),
31983199
models.APICall(
31993200
method_name='put_retention_policy',
3200-
params={'name': 'default-log-group',
3201+
params={'name': '/aws/lambda/func-name',
32013202
'retention_in_days': 14},
32023203
),
32033204
]
32043205

32053206
def test_can_update_log_group(self):
32063207
resource = models.LogGroup(
32073208
resource_name='default-log-group',
3209+
log_group_name='/aws/lambda/func-name',
32083210
retention_in_days=14,
32093211
)
32103212
self.remote_state.declare_resource_exists(resource)
32113213
plan = self.determine_plan(resource)
32123214
assert plan == [
32133215
models.APICall(
32143216
method_name='put_retention_policy',
3215-
params={'name': 'default-log-group',
3217+
params={'name': '/aws/lambda/func-name',
32163218
'retention_in_days': 14},
32173219
),
32183220
]

tests/unit/test_package.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,20 @@ def test_adds_reserved_concurrency_when_provided(self, sample_app):
449449
tf_resource = self.get_function(template)
450450
assert tf_resource['reserved_concurrent_executions'] == 5
451451

452+
def test_adds_log_group_resource_when_configured(self, sample_app):
453+
function = self.lambda_function()
454+
name = function.resource_name + '-log-group'
455+
function.log_group = models.LogGroup(
456+
resource_name=name,
457+
log_group_name='/aws/lambda/%s' % function.function_name,
458+
retention_in_days=7)
459+
template = self.template_gen.generate([function])
460+
log_resource = template['resource']['aws_cloudwatch_log_group'][name]
461+
assert log_resource == {
462+
'name': name,
463+
'retention_in_days': 7,
464+
}
465+
452466
def test_can_add_tracing_config(self, sample_app):
453467
function = self.lambda_function()
454468
function.xray = True
@@ -1202,6 +1216,22 @@ def test_adds_reserved_concurrency_when_provided(self, sample_app):
12021216
cfn_resource = list(template['Resources'].values())[0]
12031217
assert cfn_resource['Properties']['ReservedConcurrentExecutions'] == 5
12041218

1219+
def test_adds_log_group_resource_when_configured(self, sample_app):
1220+
function = self.lambda_function()
1221+
function.log_group = models.LogGroup(
1222+
resource_name=function.resource_name + '-log-group',
1223+
log_group_name='/aws/lambda/%s' % function.function_name,
1224+
retention_in_days=7)
1225+
template = self.template_gen.generate([function])
1226+
log_resource = template['Resources']['FooLogGroup']
1227+
assert log_resource == {
1228+
'Type': 'AWS::Logs::LogGroup',
1229+
'Properties': {
1230+
'LogGroupName': {'Fn::Sub': '/aws/lambda/${Foo}'},
1231+
'RetentionInDays': 7
1232+
}
1233+
}
1234+
12051235
def test_adds_layers_when_provided(self, sample_app):
12061236
function = self.lambda_function()
12071237
function.layers = ['arn:aws:layer1', 'arn:aws:layer2']

0 commit comments

Comments
 (0)