aws / serverless-application-model

The AWS Serverless Application Model (AWS SAM) transform is a AWS CloudFormation macro that transforms SAM templates into CloudFormation templates.
https://aws.amazon.com/serverless/sam
Apache License 2.0
9.33k stars 2.38k forks source link

Allow Globals to be ignored by Functions and not applied. #1874

Closed brysontyrrell closed 11 months ago

brysontyrrell commented 3 years ago

Describe your idea/feature/enhancement

I'm experimenting with using the PackageType: Image option for Functions and came across this error when I attempted to deploy my test app:

Error: Failed to create changeset for the stack: *****, ex: Waiter ChangeSetCreateComplete failed: Waiter encountered a terminal failure state Status: FAILED. Reason: Transform AWS::Serverless-2016-10-31 failed with: Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [*****] is invalid. Runtime, Handler, Layers cannot be present when PackageType is of type `Image`

Example template:

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Globals:

  Function:
    Runtime: python3.8
    Handler: index.lambda_handler
    MemorySize: 128

Resources:

  ZipFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ./src/zip

  ImageFunction:
    Type: AWS::Serverless::Function
    Properties:
      PackageType: Image
    Metadata:
      Dockerfile: Dockerfile
      DockerContext: ./src/image

If I have a template of Zip and Image type Functions I can no longer use Globals because this error is thrown. I will have to define these properties on each Zip type individually.

Proposal

Either an addition to Globals or AWS::Serverless::Function that prevents globals from being applied to the resource(s).

Additional Details

SAM CLI, version 1.13.2

brysontyrrell commented 3 years ago

Another thought on the proposal: is there a way to make it so function resources will ignore attributes in Globals that don't apply to their PackageType?

jfuss commented 3 years ago

@brysontyrrell Globals is to define the same property across multiple Resources. All resources do not need or cannot have the same properties then you cannot use Globals. You will need to move the conflicting properties into the Resources.

I am not sure if this will work but values defined in the Resource will override the ones defined in the Globals section (at least for strings they will). So you might be able to set them to AWS::NoValue. I am not sure if Cloudformation will remove these properties before it gets sent to SAM, but might be worth a try if you really want to keep what you are doing.

Doing conditional things based upon properties can get very confusing for customers and be something very difficult to manage. Globals then become this conditionally applied block to sets of Resources and changes what Globals is.

I can understand the frustration but this is acting as designed.

si3mshady commented 3 years ago

SAM sucks

sapessi commented 3 years ago

SAM sucks

We'd love to try and change your mind. What can we change/fix to make SAM work for your use-case?

BlissfulDarkness commented 3 years ago

It feels like the core issue is actually how Globals is specified and applied to resources. Specifically, it's not possible to 'subtype' deep enough. Conceptually, the better solution is something like one of these for Globals:

Globals:
  Function:
    ZipPackageType:
      Runtime:
      Layers:
    ImagePackageType:
      ImageUri:

Globals:
  Function:
    Runtime:
    Layers:
  ImageFunction:
    ImageUri:

This is complicated mostly that there is no concept of Subtype with the Resources, but I think this is actually what the best solution would look like, especially the 2nd form. I think the SAM transform could correctly translate this SAM specific resource type (ImageFunction) into a valid CloudFormation resource, while also applying the 'Function' globals to standard CloudFormation Function resources at the same time.

MarinaRappoport commented 2 years ago

I faced the same issue. it would be great to have a possibility to ignore/except some Functions from using globals

damogallagher commented 2 years ago

Just faced this issue - it would be great to have a solution where the 1 template file could hold normal functions and the package type of image

barkermn01 commented 2 years ago

No matter which way you look at this the globals is set up daft, here, either there should be a way to say override globals at a function, or globals as a whole need a better system off perhaps having support for baseSettings: BaseSettingsName

So Currently i use

Globals:
  Function:
    Timeout: 5
    MemorySize: 128
    Runtime: nodejs16.x
    Architectures:
      - "x86_64"
    Layers:
      - "{layer_arn}"
    Handler: index.handler
    VpcConfig:
      SecurityGroupIds: !Ref DefaultSecurityGroupParameter
      SubnetIds: !Ref DefaultSubnetsParameter

However, I now need to add an Image type function because it's been built by an AWS Partner, so the image package type complains because the globals are there (Layers, handler, and PackageType), so to fix this I then have to replicate all those lines for every function, (worst-case scenario) adding 11 lines times the number of functions using Zip package type in my case that is 44 functions, so an extra 484 lines.

Why not change this so that Globals can be there, or support for custom base settings, E.G

FunctionBaseSettings:
  SettingsTypeZipBaseSettings:
    Timeout: 5
    MemorySize: 128
    Runtime: nodejs16.x
    Architectures:
      - "x86_64"
    Layers:
      - "{layer_arn}"
    Handler: index.handler
    VpcConfig:
      SecurityGroupIds: !Ref DefaultSecurityGroupParameter
      SubnetIds: !Ref DefaultSubnetsParameter

then functions use them something like

GETrequestsMonitoredVisitor:
    Type: AWS::Serverless::Function
    BaseSettings: SettingsTypeZipBaseSettings
    Properties:
      Handler: {handler_name}
      CodeUri: {code_path}/
      Events:
        EndpointGetMonitor:
          Type: Api
          Properties:
            Path: {api_gateway_path}
            Method: {api_gaterway_method}
            RestApiId: !Ref API
    Metadata:
      DockerTag: nodejs16.x-v1
      DockerContext: ./{code_path}/

This would allow the developer to create multiple base settings as needed and select the correct one for each function.

I'm using {} to denote what should be present where I have removed specific to our codebase stuff,

EnkhAmar commented 1 year ago

Hi there,

I understand that you're experiencing an issue with AWS SAM where you would like to be able to ignore Globals for specific functions. From my understanding, Globals are currently applied to all functions defined in the AWS SAM template, which may not always be desired.

I think this is a valid use case and it would be great to have an option to selectively ignore Globals for certain functions. This could potentially be implemented by adding a new property to the function definition, such as "IgnoreGlobals" or something similar.

I'm not sure if there are any technical challenges that would make this difficult to implement, but I'm curious to hear the thoughts of the AWS SAM team on this.

Thanks for bringing this up and please let me know if there's anything else I can do to help.

justinmclark commented 1 year ago

For what it's worth, I sort of got around this as long as Layers is not one of the Globals.

E.g., this stack deploys just fine:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Parameters:
  UrlParserImage:
    Type: String
    Description: The image to use for the url parser function

Globals:
  Function:
    Timeout: 30
    Runtime: python3.9
    Handler: index.handler

Resources:
  UrlParser:
    Type: AWS::Serverless::Function
    Properties:
      ImageUri: !Ref UrlParserImage

  TestImageFunction:
    Type: AWS::Serverless::Function
    Properties:
      PackageType: Image
      Runtime: null
      Handler: null
      ImageConfig:
        Command: ["test.lambda_handler"]
    Metadata:
      Dockerfile: Dockerfile
      DockerContext: ./test
      DockerTag: v1

However, I have a real production stack with Layers as one of the globals and I'm unable to deploy that stack.

GavinZZ commented 11 months ago

Hi after team discussion, we will support introducing a new resource attribute called IgnoreGlobals.

IgnoreGlobals will accept a string input value of "*" or a list of string input values.

  1. When IgnoreGlobals: "*" is used as resource attribute of a specific resource, it means to ignore all Globals for this specific resource
  2. When a list of valid string input is used, it will ignore the matching properties in Globals.

Consider the example where I have a mix of PackageType: Image function and a PackageType: Zip function.

Globals:
  Function:
    Runtime: python3.8
    Handler: index.lambda_handler
    MemorySize: 128

Resources:
  ZipFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: s3://bucket/key

  ImageFunction:
    Type: AWS::Serverless::Function
    IgnoreGlobals: '*'
    Properties:
      PackageType: Image
      ImageUri: s3://bucket/key
    Metadata:
      Dockerfile: Dockerfile
      DockerContext: ./src/image

  ImageFunction2:
    Type: AWS::Serverless::Function
    IgnoreGlobals:
    - Runtime
    - Handler
    Properties:
      PackageType: Image
      ImageUri: s3://bucket/key
    Metadata:
      Dockerfile: Dockerfile
      DockerContext: ./src/image

Note that this feature is already merged, see https://github.com/aws/serverless-application-model/pull/3386. It will take about 2~3 weeks to gradually roll out to all regions.

GavinZZ commented 11 months ago

Marking this issue as completed.