Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lambda Version fails to publish when CloudFormation changes but Lambda config doesn't #8251

Closed
brent-jackson opened this issue Sep 15, 2020 · 24 comments

Comments

@brent-jackson
Copy link
Contributor

brent-jackson commented Sep 15, 2020

Description

CloudFormation can be used to help define a Lambda's configuration. In cases where the CloudFormation changes in a way such that the generated Lambda config remains the same as a previously published Lambda, the sls deploy step fails due to CloudFormation being unable to create the Lambda::Version resource with the following error:

An error occurred: FooLambdaVersionOHsXVFbopmx6xLLc8pj3Dqm8a6wTnZSED4UB9TyYJQ - A version for this Lambda function exists ( 1 ). Modify the function to create a new version..

The AWS Lambda API PublishVersion docs state:

AWS Lambda doesn't publish a version if the function's configuration and code haven't changed since the last version.

However, since the serverless framework calculates the logical ID of the Lambda::Version resource based on the hashed handler code and lambda config (prior to CloudFormation intrinsic function resolution) [source], a change to the a Lambda's config will always generate a new Lambda::Version logical ID, even if the resolved CloudFormation generates an identical Lambda config.

Disabling provider.versionFunctions would likely work-around this issue, but is not something we want to do, as we want the benefits of being able to use Lambda traffic shifting between versions.

serverless.yml before change
service: foobar

provider:
  name: aws
  runtime: nodejs12.x

functions:
  foo:
    handler: handler.foo
    environment:
      BAR:
        bar
serverless.yml after change
service: foobar

provider:
  name: aws
  runtime: nodejs12.x

functions:
  foo:
    handler: handler.foo
    environment:
      BAR:
        Fn::Join:
          - ""
          - - b
            - a
            - r
sls deploy --stage brentj output
Serverless: Deprecation warning: Support for Node.js versions below v10 will be dropped with next major release. Please upgrade at https://nodejs.org/en/
            More Info: https://www.serverless.com/framework/docs/deprecations/#OUTDATED_NODEJS
Serverless: Deprecation warning: Local installation of Serverless detected. Starting with next major version, CLI will run it instead of globally installed version.
            Set "enableLocalInstallationFallback" to "true" to switch to new behavior now, set to "false" to keep current behavior and hide this message
            More Info: https://www.serverless.com/framework/docs/deprecations/#LOCAL_INSTALLATION_FALLBACK
Serverless: To ensure safe major version upgrades ensure "frameworkVersion" setting in service configuration (recommended setup: "frameworkVersion: ^1.83.0")

Serverless: Load command interactiveCli
Serverless: Load command config
Serverless: Load command config:credentials
Serverless: Load command create
Serverless: Load command install
Serverless: Load command package
Serverless: Load command deploy
Serverless: Load command deploy:function
Serverless: Load command deploy:list
Serverless: Load command deploy:list:functions
Serverless: Load command invoke
Serverless: Load command invoke:local
Serverless: Load command info
Serverless: Load command logs
Serverless: Load command metrics
Serverless: Load command print
Serverless: Load command remove
Serverless: Load command rollback
Serverless: Load command rollback:function
Serverless: Load command slstats
Serverless: Load command plugin
Serverless: Load command plugin
Serverless: Load command plugin:install
Serverless: Load command plugin
Serverless: Load command plugin:uninstall
Serverless: Load command plugin
Serverless: Load command plugin:list
Serverless: Load command plugin
Serverless: Load command plugin:search
Serverless: Load command config
Serverless: Load command config:credentials
Serverless: Load command rollback
Serverless: Load command rollback:function
Serverless: Load command upgrade
Serverless: Load command uninstall
Serverless: Load command login
Serverless: Load command logout
Serverless: Load command generate-event
Serverless: Load command test
Serverless: Load command dashboard
Serverless: Load command output
Serverless: Load command output:get
Serverless: Load command output:list
Serverless: Load command param
Serverless: Load command param:get
Serverless: Load command param:list
Serverless: Load command studio
Serverless: Load command dev
Serverless: Invoke deploy
Serverless: Invoke package
Serverless: Invoke aws:common:validate
Serverless: Invoke aws:common:cleanupTempDir
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Invoke aws:package:finalize
Serverless: Invoke aws:common:moveArtifactsToPackage
Serverless: Invoke aws:common:validate
Serverless: Invoke aws:deploy:deploy
Serverless: [AWS cloudformation 200 0.702s 0 retries] describeStacks({ StackName: 'foobar-brentj' })
Serverless: [AWS cloudformation 200 0.445s 0 retries] describeStackResource({ StackName: 'foobar-brentj',
  LogicalResourceId: 'ServerlessDeploymentBucket' })
Serverless: [AWS s3 200 0.632s 0 retries] listObjectsV2({ Bucket: 'foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r',
  Prefix: 'serverless/foobar/brentj' })
Serverless: [AWS s3 200 0.614s 0 retries] headObject({ Bucket: 'foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r',
  Key: 'serverless/foobar/brentj/1600182814624-2020-09-15T15:13:34.624Z/foobar.zip' })
Serverless: [AWS s3 200 0.734s 0 retries] headObject({ Bucket: 'foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r',
  Key: 'serverless/foobar/brentj/1600182814624-2020-09-15T15:13:34.624Z/compiled-cloudformation-template.json' })
Serverless: [AWS lambda 200 0.519s 0 retries] getFunction({ FunctionName: 'foobar-brentj-foo' })
Serverless: [AWS sts 200 0.602s 0 retries] getCallerIdentity({})
Serverless: Uploading CloudFormation file to S3...
Serverless: [AWS s3 200 0.568s 0 retries] putObject({ Body: <Buffer 7b 22 41 57 53 54 65 6d 70 6c 61 74 65 46 6f 72 6d 61 74 56 65 72 73 69 6f 6e 22 3a 22 32 30 31 30 2d 30 39 2d 30 39 22 2c 22 44 65 73 63 72 69 70 74 ... >,
  Bucket: 'foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r',
  Key: 'serverless/foobar/brentj/1600183707565-2020-09-15T15:28:27.565Z/compiled-cloudformation-template.json',
  ContentType: 'application/json',
  Metadata: { filesha256: 'zxkTIgwEdV8+BjV9sar7fnLKSitH2okMr1lk9znB35Y=' } })
Serverless: Uploading artifacts...
Serverless: Uploading service foobar.zip file to S3 (57.91 MB)...
Serverless: [AWS s3 200 0.717s 0 retries] createMultipartUpload({ Bucket: 'foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r',
  Key: 'serverless/foobar/brentj/1600183707565-2020-09-15T15:28:27.565Z/foobar.zip',
  ContentType: 'application/zip',
  Metadata: { filesha256: 'mR+VwYo7UvjTo1+blfiJSazF0YuDcj3iD0x98Nh+1zI=' } })
Serverless: [AWS s3 200 3.35s 0 retries] uploadPart({ Body: <Buffer 8e ab c6 71 dc a6 8d 62 38 68 70 07 d8 56 6f 61 21 a5 10 5c 96 8a 9c bb 42 7d 5e 88 d5 62 fa 92 4f bd fa f9 49 f5 d6 5a 1f a3 af 82 94 b5 5b 8a 17 a3 ... >,
  ContentLength: 5242880,
  PartNumber: 3,
  Bucket: 'foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r',
  Key: 'serverless/foobar/brentj/1600183707565-2020-09-15T15:28:27.565Z/foobar.zip',
  UploadId: 'do33JKqdGaCG3QPtflCy7IkTN2k1A.T5Q5fnGdyhHZSSrH_QIzaBhvJFwEgw.4oTLd4fPNTRyTk2yKxo675y62yWmC5K2G5ybYLz.WMGIcD3JjVRrlJUH04r1iFTtovU' })
Serverless: [AWS s3 200 7.476s 0 retries] uploadPart({ Body: <Buffer 50 4b 03 04 14 00 08 00 08 00 00 00 21 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 00 00 00 68 61 6e 64 6c 65 72 2e 6a 73 5d 4e 41 6a c3 40 0c bc ef 2b ... >,
  ContentLength: 5242880,
  PartNumber: 1,
  Bucket: 'foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r',
  Key: 'serverless/foobar/brentj/1600183707565-2020-09-15T15:28:27.565Z/foobar.zip',
  UploadId: 'do33JKqdGaCG3QPtflCy7IkTN2k1A.T5Q5fnGdyhHZSSrH_QIzaBhvJFwEgw.4oTLd4fPNTRyTk2yKxo675y62yWmC5K2G5ybYLz.WMGIcD3JjVRrlJUH04r1iFTtovU' })
Serverless: [AWS s3 200 9.041s 0 retries] uploadPart({ Body: <Buffer 9f 7b e2 f7 2f 49 7f 81 08 79 bf cc 67 d9 32 57 1a aa 73 31 7f 00 50 4b 07 08 de a8 1c c6 b9 00 00 00 ab 02 00 00 50 4b 03 04 14 00 08 00 08 00 00 00 ... >,
  ContentLength: 5242880,
  PartNumber: 2,
  Bucket: 'foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r',
  Key: 'serverless/foobar/brentj/1600183707565-2020-09-15T15:28:27.565Z/foobar.zip',
  UploadId: 'do33JKqdGaCG3QPtflCy7IkTN2k1A.T5Q5fnGdyhHZSSrH_QIzaBhvJFwEgw.4oTLd4fPNTRyTk2yKxo675y62yWmC5K2G5ybYLz.WMGIcD3JjVRrlJUH04r1iFTtovU' })
Serverless: [AWS s3 200 8.162s 0 retries] uploadPart({ Body: <Buffer 97 f0 d8 f9 53 6f 31 97 a4 7e d5 da 80 17 b1 18 0b f7 9c d5 2f f5 b1 48 ea c3 12 6d 33 96 b0 3b 68 93 52 88 fa af bf 4a d4 9b 1c f2 50 bd 92 44 c2 58 ... >,
  ContentLength: 5242880,
  PartNumber: 5,
  Bucket: 'foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r',
  Key: 'serverless/foobar/brentj/1600183707565-2020-09-15T15:28:27.565Z/foobar.zip',
  UploadId: 'do33JKqdGaCG3QPtflCy7IkTN2k1A.T5Q5fnGdyhHZSSrH_QIzaBhvJFwEgw.4oTLd4fPNTRyTk2yKxo675y62yWmC5K2G5ybYLz.WMGIcD3JjVRrlJUH04r1iFTtovU' })
Serverless: [AWS s3 200 9.816s 0 retries] uploadPart({ Body: <Buffer 21 08 b5 89 78 b9 df 95 9a 08 79 50 05 01 70 39 6a 8c 3e a0 86 d3 be 67 ef b5 46 be 7f 16 f5 05 50 4b 07 08 77 4a 0f 5c 89 00 00 00 b6 00 00 00 50 4b ... >,
  ContentLength: 5242880,
  PartNumber: 4,
  Bucket: 'foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r',
  Key: 'serverless/foobar/brentj/1600183707565-2020-09-15T15:28:27.565Z/foobar.zip',
  UploadId: 'do33JKqdGaCG3QPtflCy7IkTN2k1A.T5Q5fnGdyhHZSSrH_QIzaBhvJFwEgw.4oTLd4fPNTRyTk2yKxo675y62yWmC5K2G5ybYLz.WMGIcD3JjVRrlJUH04r1iFTtovU' })
Serverless: [AWS s3 200 3.694s 0 retries] uploadPart({ Body: <Buffer 39 78 9b 53 2e f6 d6 e6 3e 71 89 98 95 5b db b3 37 7c 73 d2 82 78 97 a3 42 ef 81 ba 97 0d 75 93 6b 4c 5f e2 3b 0e e1 6d ca 73 f7 d7 a1 bc 53 95 73 7e ... >,
  ContentLength: 5242880,
  PartNumber: 8,
  Bucket: 'foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r',
  Key: 'serverless/foobar/brentj/1600183707565-2020-09-15T15:28:27.565Z/foobar.zip',
  UploadId: 'do33JKqdGaCG3QPtflCy7IkTN2k1A.T5Q5fnGdyhHZSSrH_QIzaBhvJFwEgw.4oTLd4fPNTRyTk2yKxo675y62yWmC5K2G5ybYLz.WMGIcD3JjVRrlJUH04r1iFTtovU' })
Serverless: [AWS s3 200 10.046s 0 retries] uploadPart({ Body: <Buffer 7b e9 46 f6 bd 6e 1f 7e 7c fb b2 82 91 16 bf ac d8 c9 fe 0f 50 4b 07 08 7c 09 73 7d 66 03 00 00 a3 0a 00 00 50 4b 03 04 14 00 08 00 08 00 00 00 21 00 ... >,
  ContentLength: 5242880,
  PartNumber: 6,
  Bucket: 'foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r',
  Key: 'serverless/foobar/brentj/1600183707565-2020-09-15T15:28:27.565Z/foobar.zip',
  UploadId: 'do33JKqdGaCG3QPtflCy7IkTN2k1A.T5Q5fnGdyhHZSSrH_QIzaBhvJFwEgw.4oTLd4fPNTRyTk2yKxo675y62yWmC5K2G5ybYLz.WMGIcD3JjVRrlJUH04r1iFTtovU' })
Serverless: [AWS s3 200 8.944s 0 retries] uploadPart({ Body: <Buffer 00 00 50 4b 03 04 14 00 08 00 08 00 00 00 21 00 00 00 00 00 00 00 00 00 00 00 00 00 5e 00 00 00 6e 6f 64 65 5f 6d 6f 64 75 6c 65 73 2f 73 65 72 76 65 ... >,
  ContentLength: 5242880,
  PartNumber: 7,
  Bucket: 'foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r',
  Key: 'serverless/foobar/brentj/1600183707565-2020-09-15T15:28:27.565Z/foobar.zip',
  UploadId: 'do33JKqdGaCG3QPtflCy7IkTN2k1A.T5Q5fnGdyhHZSSrH_QIzaBhvJFwEgw.4oTLd4fPNTRyTk2yKxo675y62yWmC5K2G5ybYLz.WMGIcD3JjVRrlJUH04r1iFTtovU' })
Serverless: [AWS s3 200 8.365s 0 retries] uploadPart({ Body: <Buffer b2 67 92 54 8b 49 08 70 21 bf c6 40 d9 a1 30 56 5f f6 6b 02 83 21 90 20 41 33 3f c6 e6 4a 40 83 80 b6 d6 03 f2 3d 09 42 33 d7 1b 02 7a fb 07 f1 e5 ef ... >,
  ContentLength: 5242880,
  PartNumber: 9,
  Bucket: 'foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r',
  Key: 'serverless/foobar/brentj/1600183707565-2020-09-15T15:28:27.565Z/foobar.zip',
  UploadId: 'do33JKqdGaCG3QPtflCy7IkTN2k1A.T5Q5fnGdyhHZSSrH_QIzaBhvJFwEgw.4oTLd4fPNTRyTk2yKxo675y62yWmC5K2G5ybYLz.WMGIcD3JjVRrlJUH04r1iFTtovU' })
Serverless: [AWS s3 200 6.751s 0 retries] uploadPart({ Body: <Buffer 3c 1d e9 18 7a 0f f0 24 1e bd 28 74 54 45 8e dd a2 89 86 8d ef 7a f2 03 07 dd 98 1f 08 87 9d aa 68 11 a8 95 94 56 cf c4 5f 1f 9e 89 87 30 d2 3e 48 ad ... >,
  ContentLength: 5242880,
  PartNumber: 10,
  Bucket: 'foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r',
  Key: 'serverless/foobar/brentj/1600183707565-2020-09-15T15:28:27.565Z/foobar.zip',
  UploadId: 'do33JKqdGaCG3QPtflCy7IkTN2k1A.T5Q5fnGdyhHZSSrH_QIzaBhvJFwEgw.4oTLd4fPNTRyTk2yKxo675y62yWmC5K2G5ybYLz.WMGIcD3JjVRrlJUH04r1iFTtovU' })
Serverless: [AWS s3 200 5.367s 0 retries] uploadPart({ Body: <Buffer 74 65 2f 65 6e 63 6f 64 69 6e 67 73 2f 75 74 66 37 2e 6a 73 50 4b 01 02 2d 03 14 00 08 00 08 00 00 00 21 00 b8 08 80 6b 85 01 00 00 55 04 00 00 2b 00 ... >,
  ContentLength: 3047492,
  PartNumber: 12,
  Bucket: 'foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r',
  Key: 'serverless/foobar/brentj/1600183707565-2020-09-15T15:28:27.565Z/foobar.zip',
  UploadId: 'do33JKqdGaCG3QPtflCy7IkTN2k1A.T5Q5fnGdyhHZSSrH_QIzaBhvJFwEgw.4oTLd4fPNTRyTk2yKxo675y62yWmC5K2G5ybYLz.WMGIcD3JjVRrlJUH04r1iFTtovU' })
Serverless: [AWS s3 200 6.471s 0 retries] uploadPart({ Body: <Buffer 21 e8 cd 8b 2d 50 e4 71 0a 9a a0 db 20 67 5a 1a 59 4c 24 52 25 29 ef 6e 17 fb df 33 23 52 4f 2f 8a 6c 4f 3d d4 30 fc 20 e7 f1 cd 37 df 8c 9d f4 0e c1 ... >,
  ContentLength: 5242880,
  PartNumber: 11,
  Bucket: 'foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r',
  Key: 'serverless/foobar/brentj/1600183707565-2020-09-15T15:28:27.565Z/foobar.zip',
  UploadId: 'do33JKqdGaCG3QPtflCy7IkTN2k1A.T5Q5fnGdyhHZSSrH_QIzaBhvJFwEgw.4oTLd4fPNTRyTk2yKxo675y62yWmC5K2G5ybYLz.WMGIcD3JjVRrlJUH04r1iFTtovU' })
Serverless: [AWS s3 200 0.832s 0 retries] completeMultipartUpload({ MultipartUpload:
   { Parts:
      [ { ETag: '"5447ecc84c7f0a7c3d4d9bd0cde9fb58"', PartNumber: 1 },
        { ETag: '"d9254069c2d4cb891e17474b0178ad1c"', PartNumber: 2 },
        { ETag: '"09e318f0f2ad07bda0edbeb8cde5e50a"', PartNumber: 3 },
        { ETag: '"dfbfc9dd78764c1d58557c38c190f5ba"', PartNumber: 4 },
        { ETag: '"1f8d5d7ac659f0d62de332ece6ac3db5"', PartNumber: 5 },
        { ETag: '"87d1e942f41510c1c9944109d62024ec"', PartNumber: 6 },
        { ETag: '"705dff8ec168773532ab139aee9ad21d"', PartNumber: 7 },
        { ETag: '"a2b9a0467846b907ec544468a4831b29"', PartNumber: 8 },
        { ETag: '"2ce65f11746f55110414e335c9d0cd45"', PartNumber: 9 },
        { ETag: '"eec1b7007a181a0baf668bc189bbe11d"', PartNumber: 10 },
        { ETag: '"b11d1189512806b427f508a16b4ecb9f"', PartNumber: 11 },
        { ETag: '"832e402bb2f1efc03a4965ad7486f478"', PartNumber: 12 },
        [length]: 12 ] },
  Bucket: 'foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r',
  Key: 'serverless/foobar/brentj/1600183707565-2020-09-15T15:28:27.565Z/foobar.zip',
  UploadId: 'do33JKqdGaCG3QPtflCy7IkTN2k1A.T5Q5fnGdyhHZSSrH_QIzaBhvJFwEgw.4oTLd4fPNTRyTk2yKxo675y62yWmC5K2G5ybYLz.WMGIcD3JjVRrlJUH04r1iFTtovU' })
Serverless: Validating template...
Serverless: [AWS cloudformation 200 0.544s 0 retries] validateTemplate({ TemplateURL: 'https://s3.amazonaws.com/foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r/serverless/foobar/brentj/1600183707565-2020-09-15T15:28:27.565Z/compiled-cloudformation-template.json' })
Serverless: Updating Stack...
Serverless: [AWS cloudformation 200 0.742s 0 retries] updateStack({ StackName: 'foobar-brentj',
  Capabilities: [ 'CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM', [length]: 2 ],
  Parameters: [ [length]: 0 ],
  TemplateURL: 'https://s3.amazonaws.com/foobar-brentj-serverlessdeploymentbucket-igd5hsz1ej9r/serverless/foobar/brentj/1600183707565-2020-09-15T15:28:27.565Z/compiled-cloudformation-template.json',
  Tags: [ { Key: 'STAGE', Value: 'brentj' }, [length]: 1 ] })
Serverless: Checking Stack update progress...
Serverless: [AWS cloudformation 200 0.576s 0 retries] describeStackEvents({ StackName: 'arn:aws:cloudformation:us-east-1:[REDACTED]:stack/foobar-brentj/11ca43c0-f764-11ea-9214-1201663355d3' })
.Serverless: [AWS cloudformation 200 0.519s 0 retries] describeStackEvents({ StackName: 'arn:aws:cloudformation:us-east-1:[REDACTED]:stack/foobar-brentj/11ca43c0-f764-11ea-9214-1201663355d3' })
..Serverless: [AWS cloudformation 200 0.754s 0 retries] describeStackEvents({ StackName: 'arn:aws:cloudformation:us-east-1:[REDACTED]:stack/foobar-brentj/11ca43c0-f764-11ea-9214-1201663355d3' })
...
Serverless: Operation failed!
Serverless: View the full error output: [REDACTED]

  Serverless Error ---------------------------------------

  ServerlessError: An error occurred: FooLambdaVersionOHsXVFbopmx6xLLc8pj3Dqm8a6wTnZSED4UB9TyYJQ - A version for this Lambda function exists ( 1 ). Modify the function to create a new version..
      at provider.request.then (C:\Users\brentj\AppData\Roaming\npm\node_modules\serverless\lib\plugins\aws\lib\monitorStack.js:94:23)
      at runCallback (timers.js:810:20)
      at tryOnImmediate (timers.js:768:5)
      at processImmediate [as _immediateCallback] (timers.js:745:5)
  From previous event:
      at wait.then (C:\Users\brentj\AppData\Roaming\npm\node_modules\serverless\lib\plugins\aws\lib\monitorStack.js:27:12)
      at <anonymous>

  Get Support --------------------------------------------
     Docs:          docs.serverless.com
     Bugs:          github.com/serverless/serverless/issues
     Issues:        forum.serverless.com

  Your Environment Information ---------------------------
     Operating System:          win32
     Node Version:              8.15.0
     Framework Version:         1.83.0
     Plugin Version:            3.8.4
     SDK Version:               2.3.2
     Components Version:        2.34.9

Installed version

Framework Core: 1.83.0
Plugin: 3.8.4
SDK: 2.3.2
Components: 2.34.9

Proposed Solution

In some ideal world, the serverless framework is able detect that the raw CloudFormation will resolve to the same Lambda config, and prevent creating an entirely new Lambda::Version resource. But since the serverless framework uses CloudFormation as a deployment mechanism, this is impossible.

Instead, the framework needs a way to ensure that the generated Lambda config changes if the hash of the config it sees has changed. My proposed solution is to append an environment variable onto each Lambda::Function resource with a value of the hashed Lambda config (as calculated here) (or just the Lambda::Function logical ID). This will ensure that anytime the hash changed, the generated Lambda config has to have changed, meaning the deployment of the Lambda::Version should succeed.

I hastily threw together an plugin locally to verify that this would work, and it seems to. But I felt this was more of a bug than a feature, so it is better to be fixed in the framework itself.

Related Issues

These issues also discuss the lambda versioning logic, but are not duplicates:

I believe this issue may be describing a different side effect of the same behaviour/bug

@thewizarodofoz
Copy link
Contributor

I believe this was fixed by #8212

@medikoo
Copy link
Contributor

medikoo commented Sep 16, 2020

@brent-jackson great thanks for report. It's an interesting problem. As @thewizarodofoz pointed we're solving it in #8212 for few obvious cases, but indeed, where we deal with values that are resolved on AWS side during deployment we have limited means to properly detect weather real change happened or not.

My proposed solution is to append an environment variable onto each Lambda::Function resource with a value of the hashed Lambda config (as calculated here)

This looks as interesting idea. I take this could only by solved by changing the result zip file. We could either add some .serverless-hash file to the bundle (although that's a bit noisy) or modify a modification date of one of the files (here question is how to deterministically translate hash to specific modification date, I guess it could be prone to errors).

Other issue, is that for some deployments users prepare zip artifacts on their own, and in that case I think we can't apply any of the above.

We could also try to resolve those env vars prior hash calculation (we already resolve them for local invocation), but that's limited, as we resolve just some CF functions, and it's not possible to resolve every CF function before deployment.

@glb
Copy link
Contributor

glb commented Sep 16, 2020

@medikoo the proposal that @brent-jackson has to inject the versionDigest into the function environment is sufficient to ensure that Lambda creates a new version; it's not necessary to modify the function code / artifact. It seems like a safe workaround to me.

@brent-jackson
Copy link
Contributor Author

brent-jackson commented Sep 16, 2020

I believe this was fixed by #8212

The solution implemented in #8212 won't resolve this issue. Same error message, different underlying reason.

I take this could only by solved by changing the result zip file.

I don't think we are limited to changing the zip. The AWS docs state that Lambda "doesn't publish a version if the function's configuration and code haven't changed since the last version." So we should be able to change either of them. Looking at the Lambda API docs for UpdateFunctionConfiguration and UpdateFunctionCode should give us an idea of which fields can be changed to allow a new Lambda::Version to be successfully published. The tricky part is that we are blind to the logic CloudFormation uses to convert a Lambda::Function resource into Lambda UpdateFunctionCode and UpdateFunctionConfiguration API calls.

I feel that the most benign change available on the Lambda::Function resource is to add an environment variable along the lines of SLS_LAMBDA_VERSION_CONFIG_HASH with a value of the versionDigest as @glb pointed out. The env var name will have very very low (but non-zero 😞) chance of colliding a user's application environment variables.

Your idea of including a .serverless-hash is elegant, but as you said, has issues when users are bundling their own zip files. I don't really like the idea of trying to convert a hash into a modification date. Resolving the CloudFormation env vars prior to hash calculation also feels like it could be flaky.

@medikoo
Copy link
Contributor

medikoo commented Sep 18, 2020

I feel that the most benign change available on the Lambda::Function resource is to add an environment variable along the lines of SLS_LAMBDA_VERSION_CONFIG_HASH with a value of the versionDigest as @glb pointed out.

@brent-jackson that sounds great to me. Indeed that's probably cleanest and most reliable solution. PR that adds that is welcome!

@Enase
Copy link
Contributor

Enase commented Sep 25, 2020

have the same issue after update to 2.3.0

@medikoo
Copy link
Contributor

medikoo commented Sep 25, 2020

@Enase that issue was not yet solved. We welcome a PR that addresses that!

brent-jackson added a commit to brent-jackson/serverless that referenced this issue Sep 30, 2020
In some instances the version hash may change due to a change involving
CloudFormation instrisic functions, but upon resolving the functions, the
resulting lambda configuration is identical to what is currently
deployed. This causes the Lambda::Version to fail to publish since AWS
sees the lambdas as identical.

This commit embeds an environment variable onto each lambda function with
the value of the version config hash, ensuring that anytime the config
hash changes, the resulting lambda configuration will be changed,
allowing a Lambda::Version to be published.

Resolves issue serverless#8251
@medikoo
Copy link
Contributor

medikoo commented Oct 2, 2020

Unfortunately we needed to revert #8310, as unconditionally adding an env var will break lambdas attached to CloudFront distributions (note: we remove env vars for lambda explicitly configured with cloudFront event, but still it's not uncommon to attach lambdas to CloudFront externally).

Do we have any better ideas on how to address that? Ideally if it's done via Lambda configuration, as patching lambda code is not a solution when we're relying on prebuilt artifacts

@medikoo medikoo reopened this Oct 2, 2020
@Bersaelor
Copy link

Bersaelor commented Oct 12, 2020

Just dropping a note that I never had this problem before, but since updating serverless to 2.6.0 I see it with every attempt of sls deploy no matter how much I change my lambda functions.
(In my case the serverless.yml has many lambda functions, and the error always mentions a different function than the one I had edited).

@Enase
Copy link
Contributor

Enase commented Oct 12, 2020

@Bersaelor The same for me and the latest version without the issue is:

sls -v 
Framework Core: 2.2.0
Plugin: 4.0.4
SDK: 2.3.2
Components: 3.1.5

@Bersaelor
Copy link

@Enase thank you for dropping that info, after going back to 2.2.0 too (npm install -g serverless@2.2.0), I was able to deploy my stack again.

@medikoo
Copy link
Contributor

medikoo commented Oct 13, 2020

@Bersaelor @Enase can you open other bug report? As what you reports looks as regression introduced with #8066, while this issue describes different problem (happening already before 2.2.0)

@Bersaelor
Copy link

I tried documenting it in #8392 but it seems I can only consistently reproduce it in out production platform which isn't great for experimenting with it.

@Enase
Copy link
Contributor

Enase commented Oct 13, 2020

@medikoo That's definitely regression, but it's hard to find the reason in production environment. Hope I will have a time to test and share some details.

@medikoo
Copy link
Contributor

medikoo commented Oct 13, 2020

@Enase @Bersaelor thanks for details. Yes I can confirm it's a regression, please see my comment: #8392 (comment)

Ideally would be to find a proper solution for this issue, this will also make regression gone

@coyoteecd
Copy link
Contributor

We see this issue as well after upgrading from 1.x to 2.6/2.7

@medikoo
Copy link
Contributor

medikoo commented Oct 27, 2020

@coyoteecd check #8392 (comment)

@coyoteecd
Copy link
Contributor

@medikoo I already read the related tickets; I don't like the workaround of modifying all the lambdas. When it becomes annoying I'll probably try disabling versions instead, or rolling back to 2.2.0. But I'd rather see this fixed.

I saw this error several times, including when deploying locally to a dev stage. What I noticed till now is that when it fails, it fails repeatedly. That means once it happens, I could put additional logging to see what's going wrong. Can I help with debugging, in which case some pointers where to look would be much appreciated?

@medikoo
Copy link
Contributor

medikoo commented Oct 28, 2020

@coyoteecd if you feel that you're dealing with other problems than described here and #8392, then ideally if you open another issue with good explanation and minimal test case that will allow us to introduce the problem.

In other case, ideally let's agree on a proper solution here and deliver PR so matter is finally addressed.

@coyoteecd
Copy link
Contributor

No, I think I just misunderstood that comment. This other comment just clarified it. Somehow I thought that I had to modify the lambdas on every deploy, when in fact it seems that once I successfully deploy all functions with serverless 2.8, the error would not reappear.

@coyoteecd
Copy link
Contributor

@medikoo I got this error again, and this time it's not related to upgrades from 1.x to 2.x. Luckily it happened together with a very limited changeset, describing this in case it helps addressing the problem:

  • deployed new stack with serverless v2.13.0. Some of the functions in the stack have environment properties which reference another stack's output via Fn::ImportValue
  myfunction:
    environment:
      dbClusterArn:
        Fn::ImportValue: DbArn-${self:provider.stage}
  • due to some migration requirements, changed the Fn::ImportValue to a different export key that contained the same exported value:
  myfunction:
    environment:
      dbClusterArn:
        Fn::ImportValue: DbArnOld-${self:provider.stage}
  • redeployed: at this point I get the same "A version for this Lambda function exists ( 1 ). Modify the function to create a new version.." error.

Reading through the initial description of the issue, I believe the cause is in this case Serverless Framework generates a new function version because it sees the ImportValue key has changed. However the resolved value did not change, therefore deployment fails because the actual configuration is the same.

Would it be possible to resolve these imports at deployment time, to make the resolved values part of the hash calculations?

Meanwhile, my workaround in this case was to add a "dummy" env variable to each offending function, as a way to force a configuration (somewhat similar to the dummy code change above, but in serverless.yml)

@medikoo
Copy link
Contributor

medikoo commented Feb 1, 2021

@coyoteecd great thanks for feedback.

I think in this specific case, best solution is to either (1) rely on Serverless Framework variables (check: https://www.serverless.com/framework/docs/providers/aws/guide/variables/#reference-cloudformation-outputs) - they are guaranteed to be resolved prior deployment, (2) apply some ineffective update (e.g. whitespace change) to lambda code, it'll allow you to deploy such configuration without issues)

Problem with attempting to resolve CF functions before deploying, is that for some cases it's not possible to resolve them before deployment (e.g. they may refer to meta of resources to be deployed with the stack, not yet existing) and there's no ideal documentation on AWS side how to resolve them. Introducing an attempt for resolving them on our side will definitely introduce a significant maintenance baggage, and extra pool of bugs to address.

Technically our approach is, that we treat CF intrinsic functions as something AWS-specific, not really meant to be resolved on Framework side. We support them only in values intended to be put it as-is into result CF template. There are some exception where we attempt to resolve it (as e.g. local invocation) but support for it is only partial, and only for second-case scenarios (where first case is to pass them as-is to CF template).

@medikoo
Copy link
Contributor

medikoo commented Jun 7, 2021

We're leaning into direction where ideally CF intrinsic functions are not used in the config, and it's only Framework variables through which we access data.

So just replacing CF intrinsic functions with variables will solve this issue, and for a meantime no better solution seems available

@medikoo
Copy link
Contributor

medikoo commented Dec 3, 2021

Closing, as this issue was triggered by a change with which lambda hashing algorithm slightly changed, and that created scenarios where in some cases hash was changed for unchanged function configuration.

We didn't revert then, but promised to ourselves to not repeat the same mistake again, and another (hopefully final) change of version hashing algorithm was introduced behind the flag.

If you're still affected by it, you can easily upgrade with the small helper we've created. See: https://www.serverless.com/framework/docs/guides/upgrading-v3#lambda-hashing-algorithm

@medikoo medikoo closed this as completed Dec 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants