aws-sam-cli
aws-sam-cli copied to clipboard
Function with 'Fn::Transform' as first events causes 'sam local start-api' to crash
Description:
If a template contains a Function, of which the top-most event is a Fn::Transform entry. Then sam local start-api fails to start with something like:
...
File "/usr/local/Cellar/aws-sam-cli/0.21.0/libexec/lib/python3.7/site-packages/samcli/commands/local/lib/sam_api_provider.py", line 191, in extract_routes_from_events
for _, event in serverless_function_events.items():
AttributeError: 'str' object has no attribute 'items'
This template does not cause any issues with other commands, including sam local invoke, sam package and sam deploy.
Steps to reproduce the issue:
- Given the template:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
Func1:
Type: AWS::Serverless::Function
Properties:
CodeUri: "./"
Handler: "main"
Runtime: "go1.x"
Events:
Api1:
Type: Api
Properties:
Path: "/func1"
Method: "GET"
Func2:
Type: AWS::Serverless::Function
Properties:
CodeUri: "./"
Handler: "main"
Runtime: "go1.x"
Events:
Fn::Transform:
Name: AWS::Include
Parameters:
Location: "events.yaml"
- Run
sam local start-api
Observed result:
sam local start-api fails to start:
Traceback (most recent call last):
File "/usr/local/bin/sam", line 11, in <module>
load_entry_point('aws-sam-cli==0.21.0', 'console_scripts', 'sam')()
File "/usr/local/Cellar/aws-sam-cli/0.21.0/libexec/lib/python3.7/site-packages/click/core.py", line 764, in __call__
return self.main(*args, **kwargs)
File "/usr/local/Cellar/aws-sam-cli/0.21.0/libexec/lib/python3.7/site-packages/click/core.py", line 717, in main
rv = self.invoke(ctx)
File "/usr/local/Cellar/aws-sam-cli/0.21.0/libexec/lib/python3.7/site-packages/click/core.py", line 1137, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/usr/local/Cellar/aws-sam-cli/0.21.0/libexec/lib/python3.7/site-packages/click/core.py", line 1137, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/usr/local/Cellar/aws-sam-cli/0.21.0/libexec/lib/python3.7/site-packages/click/core.py", line 956, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/usr/local/Cellar/aws-sam-cli/0.21.0/libexec/lib/python3.7/site-packages/click/core.py", line 555, in invoke
return callback(*args, **kwargs)
File "/usr/local/Cellar/aws-sam-cli/0.21.0/libexec/lib/python3.7/site-packages/click/decorators.py", line 64, in new_func
return ctx.invoke(f, obj, *args, **kwargs)
File "/usr/local/Cellar/aws-sam-cli/0.21.0/libexec/lib/python3.7/site-packages/click/core.py", line 555, in invoke
return callback(*args, **kwargs)
File "/usr/local/Cellar/aws-sam-cli/0.21.0/libexec/lib/python3.7/site-packages/samcli/lib/telemetry/metrics.py", line 94, in wrapped
raise exception # pylint: disable=raising-bad-type
File "/usr/local/Cellar/aws-sam-cli/0.21.0/libexec/lib/python3.7/site-packages/samcli/lib/telemetry/metrics.py", line 65, in wrapped
return_value = func(*args, **kwargs)
File "/usr/local/Cellar/aws-sam-cli/0.21.0/libexec/lib/python3.7/site-packages/samcli/commands/local/start_api/cli.py", line 60, in cli
parameter_overrides) # pragma: no cover
File "/usr/local/Cellar/aws-sam-cli/0.21.0/libexec/lib/python3.7/site-packages/samcli/commands/local/start_api/cli.py", line 95, in do_cli
static_dir=static_dir)
File "/usr/local/Cellar/aws-sam-cli/0.21.0/libexec/lib/python3.7/site-packages/samcli/commands/local/lib/local_api_service.py", line 43, in __init__
cwd=self.cwd)
File "/usr/local/Cellar/aws-sam-cli/0.21.0/libexec/lib/python3.7/site-packages/samcli/commands/local/lib/api_provider.py", line 42, in __init__
self.api = self._extract_api(self.resources)
File "/usr/local/Cellar/aws-sam-cli/0.21.0/libexec/lib/python3.7/site-packages/samcli/commands/local/lib/api_provider.py", line 71, in _extract_api
provider.extract_resources(resources, collector, cwd=self.cwd)
File "/usr/local/Cellar/aws-sam-cli/0.21.0/libexec/lib/python3.7/site-packages/samcli/commands/local/lib/sam_api_provider.py", line 52, in extract_resources
self._extract_routes_from_function(logical_id, resource, collector)
File "/usr/local/Cellar/aws-sam-cli/0.21.0/libexec/lib/python3.7/site-packages/samcli/commands/local/lib/sam_api_provider.py", line 172, in _extract_routes_from_function
self.extract_routes_from_events(logical_id, serverless_function_events, collector)
File "/usr/local/Cellar/aws-sam-cli/0.21.0/libexec/lib/python3.7/site-packages/samcli/commands/local/lib/sam_api_provider.py", line 191, in extract_routes_from_events
for _, event in serverless_function_events.items():
AttributeError: 'str' object has no attribute 'items'
Expected result:
sam local start-api should process the Fn::Transform entry and run properly.
Or (also acceptable for my use case) sam local start-api should ignore the Fn::Transform entry completely and not crash.
Workarounds:
If you add any other event above the Fn::Transform entry, sam local start-api will work.
But the included events (events.yaml in this case) do not get picked up by sam local start-api.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
Func1:
Type: AWS::Serverless::Function
Properties:
CodeUri: "./"
Handler: "main"
Runtime: "go1.x"
Events:
Api1:
Type: Api
Properties:
Path: "/func1"
Method: "GET"
Func2:
Type: AWS::Serverless::Function
Properties:
CodeUri: "./"
Handler: "main"
Runtime: "go1.x"
Events:
Schedule2:
Type: Schedule
Properties:
Schedule: "rate(5 minutes)"
Fn::Transform:
Name: AWS::Include
Parameters:
Location: "events.yaml"
Versions:
SAM CLI, version 0.21.0
aws-cli/1.16.210 Python/3.7.4 Darwin/18.7.0 botocore/1.12.200
Thanks for the detailed report! While I understand the bug on the crash, can you explain your usecase here on trying to use a macro in the events section to supply a custom events file? Feels like i'm missing something.
I have a function which is triggered by several schedules, each schedule has its own slightly different JSON input. This repetitive list made my main template file feel very crowded. Additionally, I expect this file to be edited more often than the rest of the template. These schedules are more like configurations, while the rest of the template is mostly static infrastructure. So I opted to move this configuration stuff into a separate file and include it in the main template.
I'm having a similar issue with transform used to process a swagger template for an Api Gateway declaration. This works fine in v0.19.0 but breaks after. Little bit of debugging - it seems the parsing of the swagger file doesn't happen, and it tries to get the resources out of a dict, but it's a string not a dict so I get a similar error as OP: AttributeError: 'str' object has no attribute 'get'. I can provide more details if needed, but it looks very similar and is also related to a change in the transform.
Edit: For anyone else having this issue that wants to revert and you're using home-brew try this:
$ brew uninstall was-sam-cli
$ brew install https://raw.githubusercontent.com/aws/homebrew-tap/2160475c2a6d86cb9d5ceea25d4ad791ee908908/Formula/aws-sam-cli.rb
I am also having the same issue as @sgates with AWS::Serverless::Api.
I tried to debug and I found that the error is somewhere in the intrinsic_property_resolver.py.
This is the Cloudformation part:
Api:
Type: AWS::Serverless::Api
Properties:
StageName: !Ref Variable
Cors: "'*'"
DefinitionBody:
Fn::Transform:
Name: AWS::Include
Parameters:
Location: !Ref Swagger
Then i am getting the sanitized version that gives:
'DefinitionBody': 'Swagger'
'StageName': 'Variable'
I found that the change happens here: https://github.com/awslabs/aws-sam-cli/blob/704c1ea94a72d7db9438a61d64da64156d4cbe53/samcli/lib/intrinsic_resolver/intrinsic_property_resolver.py#L217
Below is the output from before and after the sanitation.
original: 'StageName' -> '{'Ref': 'Variable'}'
sanitized: 'StageName' -> 'Variable'
original: 'DefinitionBody' -> 'OrderedDict([('Fn::Transform', OrderedDict([('Name', 'AWS::Include'), ('Parameters', OrderedDict([('Location', {'Ref': 'Swagger'})]))]))])'
sanitized: 'DefinitionBody' -> 'Swagger'
sanitized: 'Properties' -> '{'MethodSettings': [{'LoggingLevel': 'INFO', 'MetricsEnabled': True, 'DataTraceEnabled': True, 'ResourcePath': '/*', 'HttpMethod': '*'}], 'StageName': 'Variable', 'Cors': "'*'", 'DefinitionBody': 'Swagger'}'
SAM version: 0.22.0 Python: 3.7.4
Hope that helps and I will try to continue debugging when I will have some time in case it's not solved.
@joskark Yeah, exactly that. My swagger file lives on an S3 bucket that is constructed at build time, and all the variables are sanitized as just their name not their value. Nice sleuthing!
@jfuss does the fix apply to reading file from s3 bucket?
I have a Fn:Transform which looks like this:
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
DefinitionBody:
Fn::Transform:
Name: AWS::Include
Parameters:
Location: !Sub "s3://my-bucket-name/oas.yaml"
And on sam-build, I receive the following:
Unable to resolve property DefinitionBody: OrderedDict([('Fn::Transform', OrderedDict([('Name', 'AWS::Include'), ('Parameters', OrderedDict([('Location', {'Fn::Sub': 's3://my-bucket-name/oas.yaml'})]))]))]). Leaving as is.
After it, sam local start-api fails with the following:
Error: Template does not have any APIs connected to Lambda functions
sam --version returns SAM CLI, version 0.41.0.
Even if it does not work with S3 path, how to set a path to a file?
AFAIU from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/create-reusable-transform-function-snippets-and-add-to-your-template-with-aws-include-transform.html, Fn::Transform → Location can only be an Amazon S3 URI.
I am facing same issue as @dmitry-weirdo .
Following is my template.yaml file:
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Timeout: 300
# AutoPublishAlias: live
Api:
OpenApiVersion: 3.0.3
Cors:
AllowMethods: "'*'"
AllowHeaders: "'*'"
AllowOrigin: "'*'"
Parameters:
Environment:
Type: String
Description: Environment parameter is used to tag associated resource in relevant environment
Default: Development
AllowedValues:
- Development
- Production
Mappings:
'Fn::Transform':
Name: AWS::Include
Parameters:
Location: ./mappings.yaml
Resources:
CustomerFunnelFunction:
Type: AWS::Serverless::Function
Properties:
Description: Lead Contact and stripe customer management function
FunctionName: customer-funnel-manager
Role: arn:aws:iam::270685132072:role/samlocalRole
CodeUri: ./customer-funnel-management
Handler: app.lambda_handler
Runtime: python3.7
MemorySize: 256
Layers:
- Ref: LayerDependencies
Environment:
Variables:
ENVIRONMENT: !FindInMap [MAPPINGPARAMETERS, !Ref Environment, 'AbbrLowerCase']
HUBSPOT_API_KEY_SSM: !FindInMap [MAPPINGPARAMETERS, !Ref Environment, 'HUBSPOT_API_KEY']
STRIPE_API_KEY_SSM: !FindInMap [MAPPINGPARAMETERS, !Ref Environment, 'STRIPE_API_KEY']
Events:
CreateContact:
Type: Api
Properties:
Path: /plans/customers
Method: POST
RestApiId:
Ref: CatapultServerlessAPI
LayerDependencies:
Type: 'AWS::Serverless::LayerVersion'
Properties:
LayerName: python-nodejs
ContentUri: ./layers/dependencies/
CompatibleRuntimes:
- python3.7
- nodejs12.x
RetentionPolicy: Delete
CatapultServerlessAPI:
Type: AWS::Serverless::Api
Properties:
StageName: !FindInMap [MAPPINGPARAMETERS, !Ref Environment, 'AbbrLowerCase']
EndpointConfiguration: REGIONAL
OpenApiVersion: 3.0.3
DefinitionBody:
'Fn::Transform':
Name: 'AWS::Include'
Parameters:
Location: catapult-openapi.yaml
Outputs:
ApiUrl:
Description: "API url"
Value: !Join ['/', [!Sub 'https://${CatapultServerlessAPI}.execute-api.${AWS::Region}.amazonaws.com', !FindInMap [MAPPINGPARAMETERS, !Ref Environment, 'AbbrLowerCase']]]
Following is my mappings.yaml file:
MAPPINGPARAMETERS:
Development:
AbbrLowerCase: dev
AbbrUpperCase: DEV
STRIPE_API_KEY: ********
HUBSPOT_API_KEY: *********
Staging:
AbbrLowerCase: stg
AbbrUpperCase: STG
Production:
AbbrLowerCase: prod
AbbrUpperCase: PROD
STRIPE_API_KEY: sk_******
HUBSPOT_API_KEY: *********
FBClient:
DEV:
SSMParamNameFBClientId: FB_CLIENT_ID_DEV
SSMParamNameFBClientSecret: FB_CLIENT_SECRET_DEV
GoogleClient:
DEV:
SSMParamNameGoogleClientId: GOOGLE_CLIENT_ID_DEV
SSMParamNameGoogleSecretId: GOOGLE_CLIENT_SECRET_DEV
My Open-API File name resides on the same location as the template.yaml. I have also Used 'Fn::Transform' and AWS::Include for mapping my API Keys and env vars to be manipulated from mappings.yaml file. The result is same for both the mappings and swagger openapi transformations. As a result, not only I am unable to load the POST payload data without being able to ingrate my lambdas with through the swagger; my API Keys are does not load from mappings.yaml too because of the same reason.
For open-api.yaml, sam build fails with following error
2020-12-21 23:54:33,093 | Unable to resolve property DefinitionBody: OrderedDict([('Fn::Transform', OrderedDict([('Name', 'AWS::Include'), ('Parameters', OrderedDict([('LocationUri', './catapult-openapi.yaml')]))]))]). Leaving as is.
And since the build is failing, the sam deploy also fails with the same error. The Error looks like following:
2020-12-21 23:30:59,533 | Unable to resolve property DefinitionBody: OrderedDict([('Fn::Transform', OrderedDict([('Name', 'AWS::Include'), ('Parameters', OrderedDict([('Location', 's3://samlocal-bucket/ba3e3a93ec0ea56ed76b177e3cfa198d')]))]))]). Leaving as is.
For mappings.yaml
2020-12-21 23:30:59,517 | Collected default values for parameters: {'Environment': 'Development'}
2020-12-21 23:30:59,533 | Unable to resolve property ENVIRONMENT: OrderedDict([('Fn::FindInMap', ['MAPPINGPARAMETERS', OrderedDict([('Ref', 'Environment')]), 'AbbrLowerCase'])]). Leaving as is.
2020-12-21 23:30:59,533 | Unable to resolve property HUBSPOT_API_KEY_SSM: OrderedDict([('Fn::FindInMap', ['MAPPINGPARAMETERS', OrderedDict([('Ref', 'Environment')]), 'HUBSPOT_API_KEY'])]). Leaving as is.
2020-12-21 23:30:59,533 | Unable to resolve property STRIPE_API_KEY_SSM: OrderedDict([('Fn::FindInMap', ['MAPPINGPARAMETERS', OrderedDict([('Ref', 'Environment')]), 'STRIPE_API_KEY'])]). Leaving as is.
2020-12-21 23:30:59,533 | Unable to resolve property StageName: OrderedDict([('Fn::FindInMap', ['MAPPINGPARAMETERS', OrderedDict([('Ref', 'Environment')]), 'AbbrLowerCase'])]). Leaving as is.
The Troubleshoots I have already performed:
- Changing the Location from local relative directory to the s3 bucket after running the command
aws s3 cp catapult-openapi.yaml s3://<my_bucket_name>successfully 1.a.Did sam package and saw the output.yaml and then pasted that sam bucket destination to the template.yaml and tried to build/ deploy again with fail. - When I copy the mappings.yaml contents into the template.yaml, at least the API Keys are loading, but again, that is not a good practice at all.
- Backdated my aws-sam-cli to 0.9 version and then realised it does not support
OpenApiVersion: 3.0.3 - Changed the Location with
./and without the./as a prefix to the relative location with template.yaml
`mj@DESKTOP~
$ sam --version
SAM CLI, version 1.15.0
mj@DESKTOP~
$ aws --version
aws-cli/2.1.13 Python/3.7.9 Windows/10 exe/AMD64 prompt/off
`
@jfuss does the fix apply to reading file from s3 bucket?
I have a
Fn:Transformwhich looks like this:MyApi: Type: AWS::Serverless::Api Properties: StageName: Prod DefinitionBody: Fn::Transform: Name: AWS::Include Parameters: Location: !Sub "s3://my-bucket-name/oas.yaml"And on
sam-build, I receive the following:Unable to resolve property DefinitionBody: OrderedDict([('Fn::Transform', OrderedDict([('Name', 'AWS::Include'), ('Parameters', OrderedDict([('Location', {'Fn::Sub': 's3://my-bucket-name/oas.yaml'})]))]))]). Leaving as is.After it,
sam local start-apifails with the following:Error: Template does not have any APIs connected to Lambda functions
sam --versionreturnsSAM CLI, version 0.41.0.Even if it does not work with S3 path, how to set a path to a file? AFAIU from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/create-reusable-transform-function-snippets-and-add-to-your-template-with-aws-include-transform.html,
Fn::Transform → Locationcan only be an Amazon S3 URI.
@dmitry-weirdo ,
I see you are the only one whose build also reported the same issue and the deploy failed as the open-api Denitionbody is not being able to load using the 'AWS::Transform' and 'AWS::Include'. I am stuck with building my application for the same reason. It will be a great help if you would please share any work around how you were able to get through this.
'Fn::Transform':
Name: 'AWS::Include'
As a result I cannot bind my form data to the jsonescaped body payload using x-amazon-api-gateway-integration type "aws_proxy" which I am configuring in side my openapi3.0 definition. Therefore I cannot integrate my API with the Front End UI form parameters. My detailed issue can be found above.
Any updates on this one?