Skip to content

Commit

Permalink
Include valid line numbers for SAM transformed resources (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kevin Formsma committed Oct 14, 2021
1 parent c066771 commit 672eb90
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 17 deletions.
39 changes: 22 additions & 17 deletions lib/cfn-model/transforms/serverless.rb
Expand Up @@ -3,7 +3,7 @@
class CfnModel
class Transforms
# Handle transformation of model elements performed by the
# Serverless trasnform, see
# Serverless transform, see
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/transform-aws-serverless.html
class Serverless
def perform_transform(cfn_hash)
Expand Down Expand Up @@ -108,6 +108,8 @@ def serverless_function_properties(cfn_hash, serverless_function, fn_name, with_
def replace_serverless_function(cfn_hash, resource_name, with_line_numbers)
serverless_function = cfn_hash['Resources'][resource_name]

original_line_number = serverless_function['Type']['line']

lambda_fn_params = serverless_function_properties(cfn_hash,
serverless_function,
resource_name,
Expand All @@ -120,15 +122,17 @@ def replace_serverless_function(cfn_hash, resource_name, with_line_numbers)
role: lambda_fn_params[:role],
runtime: lambda_fn_params[:runtime],
reserved_concurrent_executions: lambda_fn_params[:reserved_concurrent_executions],
line_number: original_line_number,
with_line_numbers: lambda_fn_params[:with_line_numbers]
)
unless serverless_function['Properties']['Role']
cfn_hash['Resources'][resource_name + 'Role'] = function_role(serverless_function,
resource_name,
original_line_number,
with_line_numbers)
end

transform_function_events(cfn_hash, serverless_function, resource_name, with_line_numbers) if \
transform_function_events(cfn_hash, serverless_function, resource_name, original_line_number, with_line_numbers) if \
serverless_function['Properties']['Events']

# Handle passing along cfn-nag specific metadata. SAM itself does not support metadata during transformation.
Expand Down Expand Up @@ -156,9 +160,9 @@ def lambda_service_can_assume_role

# Return the hash structure of the '<function_name>Role'
# AWS::IAM::Role resource as created by Serverless transform
def function_role(serverless_function, function_name, with_line_numbers)
def function_role(serverless_function, function_name, line_number, with_line_numbers)
fn_role = {
'Type' => format_resource_type('AWS::IAM::Role', -1, with_line_numbers),
'Type' => format_resource_type('AWS::IAM::Role', line_number, with_line_numbers),
'Properties' => {
'ManagedPolicyArns' => function_role_managed_policies(serverless_function['Properties']),
'AssumeRolePolicyDocument' => lambda_service_can_assume_role
Expand Down Expand Up @@ -226,9 +230,10 @@ def lambda_function(handler:,
role:,
runtime:,
reserved_concurrent_executions:,
line_number:,
with_line_numbers: false)
fn_resource = {
'Type' => format_resource_type('AWS::Lambda::Function', -1, with_line_numbers),
'Type' => format_resource_type('AWS::Lambda::Function', line_number, with_line_numbers),
'Properties' => {
'Handler' => handler,
'Role' => role,
Expand All @@ -242,32 +247,32 @@ def lambda_function(handler:,

# Return the Event structure of a AWS::Lambda::Function as created
# by Serverless transform
def transform_function_events(cfn_hash, serverless_function, function_name, with_line_numbers)
def transform_function_events(cfn_hash, serverless_function, function_name, line_number, with_line_numbers)
serverless_function['Properties']['Events'].each do |_, event|
serverlessrestapi_resources(cfn_hash, event, function_name, with_line_numbers) if \
serverlessrestapi_resources(cfn_hash, event, function_name, line_number, with_line_numbers) if \
matching_resource_type?(event['Type'], 'Api')
end
end

def serverlessrestapi_resources(cfn_hash, event, func_name, with_line_numbers)
def serverlessrestapi_resources(cfn_hash, event, func_name, line_number, with_line_numbers)
# ServerlessRestApi
cfn_hash['Resources']['ServerlessRestApi'] ||= serverlessrestapi_base with_line_numbers
cfn_hash['Resources']['ServerlessRestApi'] ||= serverlessrestapi_base line_number, with_line_numbers
add_serverlessrestapi_event(
cfn_hash['Resources']['ServerlessRestApi']['Properties']['Body']['paths'],
event,
func_name
)

# ServerlessRestApiDeployment
cfn_hash['Resources']['ServerlessRestApiDeployment'] = serverlessrestapi_deployment with_line_numbers
cfn_hash['Resources']['ServerlessRestApiDeployment'] ||= serverlessrestapi_deployment line_number, with_line_numbers

# ServerlessRestApiProdStage
cfn_hash['Resources']['ServerlessRestApiProdStage'] = serverlessrestapi_stage with_line_numbers
cfn_hash['Resources']['ServerlessRestApiProdStage'] ||= serverlessrestapi_stage line_number, with_line_numbers
end

def serverlessrestapi_base(with_line_nos)
def serverlessrestapi_base(line_number, with_line_numbers)
{
'Type' => format_resource_type('AWS::ApiGateway::RestApi', -1, with_line_nos),
'Type' => format_resource_type('AWS::ApiGateway::RestApi', line_number, with_line_numbers),
'Properties' => {
'Body' => {
'info' => {
Expand All @@ -294,9 +299,9 @@ def add_serverlessrestapi_event(paths_hash, event, function_name)
}
end

def serverlessrestapi_deployment(with_line_nos)
def serverlessrestapi_deployment(line_number, with_line_numbers)
{
'Type' => format_resource_type('AWS::ApiGateway::Deployment', -1, with_line_nos),
'Type' => format_resource_type('AWS::ApiGateway::Deployment', line_number, with_line_numbers),
'Properties' => {
'Description' => 'Generated by cfn-model',
'RestApiId' => { 'Ref' => 'ServerlessRestApi' },
Expand All @@ -311,9 +316,9 @@ def serverlessrestapi_deployment(with_line_nos)
}
end

def serverlessrestapi_stage(with_line_nos)
def serverlessrestapi_stage(line_number, with_line_numbers)
{
'Type' => format_resource_type('AWS::ApiGateway::Stage', -1, with_line_nos),
'Type' => format_resource_type('AWS::ApiGateway::Stage', line_number, with_line_numbers),
'Properties' => {
'DeploymentId' => { 'Ref' => 'ServerlessRestApiDeployment' },
'RestApiId' => { 'Ref' => 'ServerlessRestApi' },
Expand Down
36 changes: 36 additions & 0 deletions spec/transforms/serverless_spec.rb
Expand Up @@ -224,4 +224,40 @@
expect(serverlessrestapi_stage).to be_nil
end
end

context 'Templates with line numbers enabled' do
it 'assigns line numbers to function resource' do
cloudformation_template_yml = yaml_test_template('sam/valid_simple_lambda_fn')
actual_cfn_model = @cfn_parser.parse cloudformation_template_yml, nil, true

lambda_function = actual_cfn_model.resources_by_type('AWS::Lambda::Function').first
expect(actual_cfn_model.line_numbers[lambda_function.logical_resource_id]).to eq(7)
end

it 'assigns line numbers to role resource' do
cloudformation_template_yml = yaml_test_template('sam/valid_simple_lambda_fn')
actual_cfn_model = @cfn_parser.parse cloudformation_template_yml, nil, true

iam_role = actual_cfn_model.resources_by_type('AWS::IAM::Role').first
expect(actual_cfn_model.line_numbers[iam_role.logical_resource_id]).to eq(7)
end

it 'assigns line numbers to serverless event resources' do
cloudformation_template_yml = yaml_test_template('sam/serverlessrestapi_as_ref')
actual_cfn_model = @cfn_parser.parse cloudformation_template_yml, nil, true

expect(actual_cfn_model.line_numbers['ServerlessRestApi']).to eq(15)
expect(actual_cfn_model.line_numbers['ServerlessRestApiDeployment']).to eq(15)
expect(actual_cfn_model.line_numbers['ServerlessRestApiProdStage']).to eq(15)
end
end

context 'Templates with line numbers disabled' do
it 'does not assign line numbers to function resource' do
cloudformation_template_yml = yaml_test_template('sam/valid_simple_lambda_fn')
actual_cfn_model = @cfn_parser.parse cloudformation_template_yml, nil, false

expect(actual_cfn_model.line_numbers).to be_empty
end
end
end

0 comments on commit 672eb90

Please sign in to comment.