aws-amplify / amplify-cli

The AWS Amplify CLI is a toolchain for simplifying serverless web and mobile development.
Apache License 2.0
2.82k stars 820 forks source link

Auto-update Lambda runtimes for analytics category with CLI upgrade #13788

Open sushpatg opened 5 months ago

sushpatg commented 5 months ago

Is this feature request related to a new or existing Amplify category?

function, analytics

Is this related to another service?

No response

Describe the feature you'd like to request

To Auto-update Lambda runtimes for analytics, function category with CLI upgrade

Describe the solution you'd like

Currently the categories where CloudFormation is generated during build time like auth, auto generated Lambda functions runtime is being auto updated. For example with 12.2.0, the runtimes are updated to nodejs18

However for categories like analytics, Cloudformation template is not being auto-updated.

Describe alternatives you've considered

As CloudFormation is not part of build time, like functions category updating the runtime amplify/backend/analytics/name/analytics-name-cloudformation-template.json . However the auto-generated code is in aws-sdk v2. [+] https://docs.amplify.aws/gen1/javascript/build-a-backend/functions/configure-options/

Additional context

No response

Is this something that you'd be interested in working on?

Would this feature include a breaking change?

josefaidt commented 5 months ago

Hey @sushpatg :wave: thanks for raising this! Is this a custom function for your Analytics resource? The CLI will update the runtime after stepping through amplify update function and choosing the affected function

ykethan commented 5 months ago

Hey @sushpatg, you can update the Analytics CFN template for the function runtime then run a push. it should update the runtime.

Observed this on a quick test using the following steps

  1. run amplify init with amplify version at 11.1.0
  2. run amplify add analytics -> amplify push
  3. open analytics CloudFormation template in the build folder verify node 16 as version for function
  4. amplify upgrade
  5. open analytics CloudFormation template change node version to 18
  6. run amplify status observe update status on analytics
  7. amplify push -> then verify the function on AWS console if the runtime was updated.
sushpatg commented 5 months ago

Hey @sushpatg, you can update the Analytics CFN template for the function runtime then run a push. it should update the runtime.

Observed this on a quick test using the following steps

1. run `amplify init` with amplify version at 11.1.0

2. run `amplify add analytics` -> `amplify push`

3. open analytics CloudFormation template in the build folder verify node 16 as version for function

4. `amplify upgrade`

5. open analytics CloudFormation template change node version to 18

6. run `amplify status` observe update status on analytics

7. `amplify push` ->  then verify the function on AWS console if the runtime was updated.

Thank you @ykethan for the reply

Not only the runtime, but "PinpointFunction" Lambda function code also need to be modified from sdk v2 to v3. Hence raising this feature request to auto-update both runtime and code when CLI is upgraded

ykethan commented 5 months ago

Hey @sushpatg, did notice this as well. Marking as bug due to lack of an auto update functionality.

But as we have seen previously, we can edit the CloudFormation template to update both the runtime and the inline code.

"PinpointFunction": {
      "Type": "AWS::Lambda::Function",
      "Condition": "ShouldCreatePinpointApp",
      "Properties": {
        "Code": {
          "ZipFile": {
            "Fn::Join": [
              "\n",
              [
                "const response = require('cfn-response');",
                "const { CloudFormationClient, DescribeStacksCommand } = require('@aws-sdk/client-cloudformation');",
                "const { PinpointClient, CreateAppCommand, DeleteAppCommand } = require('@aws-sdk/client-pinpoint');",
                "exports.handler = function(event, context) {",
                "    // Don't return promise, response.send() marks context as done internally",
                "    const ignoredPromise = handleEvent(event, context)",
                "};",
                "async function handleEvent(event, context) {",
                "    if (event.RequestType === 'Delete') {",
                "        try {",
                "            const stackID = event.StackId;",
                "            const cloudFormationClient = new CloudFormationClient({region: event.ResourceProperties.region});",
                "            const describeStacksOutput = await cloudFormationClient.send(new DescribeStacksCommand({StackName: stackID}));",
                "            let appId;",
                "            if (describeStacksOutput.Stacks && describeStacksOutput.Stacks.length > 0) {",
                "                const {Outputs} = describeStacksOutput.Stacks[0];",
                "                const appIdOutput = Outputs.find((output) => {",
                "                    return output.OutputKey === 'Id'",
                "                });",
                "                appId = appIdOutput ? appIdOutput.OutputValue : undefined;",
                "            }",
                "            if (appId) {",
                "                const pinpointClient = new PinpointClient({region: event.ResourceProperties.pingPointRegion});",
                "                const params = {",
                "                    ApplicationId: appId,",
                "                };",
                "                await pinpointClient.send(new DeleteAppCommand(params));",
                "            }",
                "            response.send(event, context, response.SUCCESS, {'message': `Successfully deleted pinpoint project`});",
                "        } catch (e) {",
                "            if (e.name !== 'NotFoundException') {",
                "                response.send(event, context, response.FAILED, {'message': `Failed to deleted Pinpoint project`, 'exception': e});",
                "            } else {",
                "                response.send(event, context, response.SUCCESS, {'message': `Successfully deleted pinpoint project`});",
                "            }",
                "        }",
                "    } else if (event.RequestType === 'Create') {",
                "        try {",
                "            const appName = event.ResourceProperties.appName;",
                "            const params = {",
                "                CreateApplicationRequest: {",
                "                    Name: appName",
                "                }",
                "            };",
                "            const pinpoint = new PinpointClient({region: event.ResourceProperties.pingPointRegion});",
                "            const res = await pinpoint.send(new CreateAppCommand(params));",
                "            response.send(event, context, response.SUCCESS, res.ApplicationResponse);",
                "        } catch (err) {",
                "            console.log(err.stack);",
                "            response.send(event, context, response.FAILED, {Error: err});",
                "        }",
                "    } else {",
                "        response.send(event, context, response.SUCCESS);",
                "    }",
                "};"
              ]
            ]
          }
        },
        "Handler": "index.handler",
        "Runtime": "nodejs18.x",
        "Timeout": 300,
        "Role": {
          "Fn::GetAtt": [
            "LambdaExecutionRole",
            "Arn"
          ]
        }
      }
    },