aws-sam-cli icon indicating copy to clipboard operation
aws-sam-cli copied to clipboard

Function with 'Fn::Transform' as first events causes 'sam local start-api' to crash

Open yvrhdn opened this issue 6 years ago • 9 comments

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:

  1. 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"
  1. 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

yvrhdn avatar Sep 21 '19 11:09 yvrhdn

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.

sriram-mv avatar Sep 24 '19 22:09 sriram-mv

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.

yvrhdn avatar Sep 25 '19 07:09 yvrhdn

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

sgates avatar Sep 25 '19 12:09 sgates

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 avatar Sep 25 '19 13:09 joskark

@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!

sgates avatar Sep 25 '19 13:09 sgates

@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.

dmitry-weirdo avatar Jun 09 '20 11:06 dmitry-weirdo

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:

  1. 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.
  2. 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.
  3. Backdated my aws-sam-cli to 0.9 version and then realised it does not support OpenApiVersion: 3.0.3
  4. 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

`

mj-roychowdhury avatar Dec 21 '20 18:12 mj-roychowdhury

@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.

@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.

mj-roychowdhury avatar Dec 23 '20 06:12 mj-roychowdhury

Any updates on this one?

driverpt avatar May 10 '22 13:05 driverpt