Hey I am seeing this error in cloudwatch when I de...
# help
n
Hey I am seeing this error in cloudwatch when I deploy my API and lambda:
[ERROR] Runtime.ImportModuleError: Unable to import module 'lambda_function': No module named 'src' Traceback (most recent call last):
but when I run in debug mode I am able to hit the lambda
d
can you share the Stack code?
n
Copy code
import * as sst from "@serverless-stack/resources";
import { TableFieldType } from "@serverless-stack/resources";
import { LayerVersion } from "@aws-cdk/aws-lambda";
import * as ssm from "@aws-cdk/aws-ssm";

export default class IntegrationSalesforceProvivienda extends sst.Stack {
  constructor(scope: <http://sst.App|sst.App>, id: string, props?: sst.StackProps) {
    super(scope, id, props);

    const integrationName = `${this.stage}-integrations-salesforce-provivienda-sst`;

    new ssm.StringParameter(
      this,
      "integrations-salesforce-provivienda-accesstoken-parameter",
      {
        allowedPattern: ".*",
        description:
          "Access token for production environment to access SF REST API",
        parameterName: `provivienda_salesforce_access_token_${this.stage}`,
        stringValue: " ",
      }
    );

    new ssm.StringParameter(
      this,
      "integrations-salesforce-provivienda-consumerkey-parameter",
      {
        allowedPattern: ".*",
        description: "Consumer public key to identify external application",
        parameterName: `provivienda_salesforce_consumer_key_${this.stage}`,
        stringValue: " ",
      }
    );

    new ssm.StringParameter(
      this,
      "integrations-salesforce-provivienda-instanceurl-parameter",
      {
        allowedPattern: ".*",
        description: "Instance url of provivienda salesforce organization",
        parameterName: `provivienda_salesforce_instance_url_${this.stage}`,
        stringValue: " ",
      }
    );

    new ssm.StringParameter(
      this,
      "integrations-salesforce-provivienda-password-parameter",
      {
        allowedPattern: ".*",
        description: "Resource owner password",
        parameterName: `provivienda_salesforce_password_${this.stage}`,
        stringValue: " ",
      }
    );

    new ssm.StringParameter(
      this,
      "integrations-salesforce-provivienda-username-parameter",
      {
        allowedPattern: ".*",
        description: "Resource owner username",
        parameterName: `provivienda_salesforce_username_${this.stage}`,
        stringValue: " ",
      }
    );

    new ssm.StringParameter(
      this,
      "integrations-salesforce-provivienda-securitytoken-parameter",
      {
        allowedPattern: ".*",
        description: "Resource owner security token",
        parameterName: `provivienda_salesforce_security_token_${this.stage}`,
        stringValue: " ",
      }
    );

    const table = new sst.Table(this, `Table`, {
      fields: {
        eomu: TableFieldType.STRING,
        activity_code: TableFieldType.STRING,
        retry_count: TableFieldType.NUMBER,
      },
      primaryIndex: { partitionKey: "eomu", sortKey: "activity_code" },
    });

    const layerArn = "arn:aws:lambda:eu-west-1:836484966858:layer:requests:4";
    const layer = LayerVersion.fromLayerVersionArn(
      this,
      `${integrationName}-Layer`,
      layerArn
    );
    // Create a HTTP API
    const api = new sst.Api(this, `Api`, {
      defaultFunctionProps: {
        srcPath: "src",
        logRetention: 14,
        environment: {
          SERVICE_NAME: `${this.stage}-integrations-salesforce-provivienda-sst`,
          DYNAMODB_TABLE_NAME: table.dynamodbTable.tableName,
          ACCESS_TOKEN_URL:
            this.stage === "prod"
              ? "<https://login.salesforce.com/services/oauth2/token>"
              : "<https://test.salesforce.com/services/oauth2/token>",
          QUERY_SERVICE: "/services/data/v51.0/query/Product2/",
          UPDATE_SERVICE:
            "/services/data/v51.0/sobjects/Product2/vuu_codigo_unico__c/",
          STAGE: `${scope.stageName}`,
        },
      },
      routes: {
        "GET /test": {
          function: {
            handler: "lambda_function.lambda_handler",
            timeout: 29,
            layers: [layer],
          },
        },
        "POST /activity": {
          function: {
            handler: "lambda_function.lambda_handler",
            timeout: 29,
            layers: [layer],
          },
        },
      },
      customDomain: {
        hostedZone: "<http://api.integrations.letsbuild.com|api.integrations.letsbuild.com>",
        domainName: `${this.stage}-<http://proviviendasf.api.integrations.letsbuild.com|proviviendasf.api.integrations.letsbuild.com>`,
      },
    });

    api.attachPermissions([table, "ssm"]);

    // Show the endpoint in the output
    this.addOutputs({
      ApiEndpoint: api.url,
      CustomDomain: api.customDomainUrl || "No custom domain is created",
    });
  }
}
I am specifically trying to hit the /test endpoint
d
is there a reason you are importing the same function twice and calling it /test and /activity?
n
no, I guess I just wanted to map 2 different routes
d
Unable to import module 'lambda_function'
sounds to me like the function itself is trying to load the module called ``lambda_function`` . Are you using node? Can you share the imports at the top of
lambda_function.lambda_handler
and what your function is called in there
n
It is python
d
Not as familiar with python errors. Does this look like a typical python module / lib loading error. ?
n
No I don't think so since it works "locally" with the debug environment
d
hmmmm ya nothing odd I can see
so its only breaking with you run
sst deploy
?
n
yeah
d
any idea what
No module named 'src'
is referring to? I assume you have all your function code sitting in directory
/src
?
hmmm… actually
n
well I have tried it with
npx sst start
and there it works but when I try it with
npx sst deploy --stage dev
I get
Internal server error
with the logs as I described
d
I have mine in /src and all my handlers are like this…
handler: 'src/lambda.webhook',
n
yeah I tried to add the src so it looks like this:
d
oh you have
srcPath: "src",
so that should be fine?
you can’t do both though
n
yeah it won't work like that, then I get
d
you may want to try without the
srcPath: "src",
but adding it locally as you were
n
okay, give me a sec
d
I’m just troubleshooting though… maybe one of the core team might have seen this kinda thing before. @thdxr?
n
I think I read that I needed the srcPath props for python
yeah, it is not working
t
Need to take a look when I'm back at my computer
n
Error: Cannot set the "srcPath" to the project root for the "Lambda_GET_/test" function.
d
could be…. I’m not familiar with SST and python
that
No module named 'src'
is the part that seems odd to me
n
Copy code
For Python functions, srcPath is required. This is the directory where the requirements.txt, Pipfile, or poetry.lock is expected.
the weird part for me is that it works "locally"
I thought that maybe the layer would be the reason, but I have not worked with layers that much
d
Ya, sorry…. hope the core team can help… perhaps also you could strip the stack down to nothing BUT a single function and slowly introduce resources until you get the issue again
Ya, I’ve also not worked much with layers yet
n
sadly I have a deadline tomorrow, and it is just me that want to move everything away from serverless to sst
but to be honest I don't have any good excuse other then consistency 😂
and my personal preference for SST 😄
d
ouch. Ya that can sting. We’re making the same move to SST from SLS.
We’ll it was SLS -> CDK and SST is a great wrapper.
n
yeah it feels like SLS is moving in a direction we are not aligned with
d
they are moving to serverless components pretty heavily. I’m not a fan of managing the resource creation code/infra. It’s why I love CFN.
t
ok so this is a common issue with python
When running locally it can find all your dependent code on your filesystem. But when bundling for prod, it zips up everything in the
srcPath
And the srcPath cannot be project root
n
ah and because I am using boto3 I need to ensure that is bundled too?
so my folder structure is: root | ---> src/... | ---> stacks/ | ---> test/ etc
t
boto3 should be automatically available
Yeah so it sounds like you should set srcPath: "src"
n
when I define each of the functions in the routes, should I set the srcPath there or in the defaultFunctionProps?
also I actually only have 1 function, but I want to map 2 routes to it
t
You should set as much as possible in defaultFunctionProps to avoid dupes
I think in your example you can set everything there and it would simplify your route to
"GET /": "handler"
n
so like this:
Copy code
const api = new sst.Api(this, `Api`, {
      defaultFunctionProps: {
        srcPath: "src",
        logRetention: 14,
        timeout: 29,
        layers: [layer],
        environment: {
          SERVICE_NAME: `${this.stage}-integrations-salesforce-provivienda-sst`,
          DYNAMODB_TABLE_NAME: table.dynamodbTable.tableName,
          ACCESS_TOKEN_URL:
            this.stage === "prod"
              ? "<https://login.salesforce.com/services/oauth2/token>"
              : "<https://test.salesforce.com/services/oauth2/token>",
          QUERY_SERVICE: "/services/data/v51.0/query/Product2/",
          UPDATE_SERVICE:
            "/services/data/v51.0/sobjects/Product2/vuu_codigo_unico__c/",
          STAGE: `${scope.stageName}`,
        },
      },
      routes: {
        "GET /test": "lambda_function.lambda_handler",
        "POST /activity": "lambda_function.lambda_handler",
      },
      customDomain: {
        hostedZone: "<http://api.integrations.letsbuild.com|api.integrations.letsbuild.com>",
        domainName: `${this.stage}-<http://proviviendasf.api.integrations.letsbuild.com|proviviendasf.api.integrations.letsbuild.com>`,
      },
    });
t
That's right, I also think your import statements in python shouldn't include
src
I have basically 0 experience with python so a bit blind here
n
yeah sadly that is not my code and I am on the same lvl as you with Python 😂
I still get the error with above
should I add a
/
to the end of srcPath?
I removed the src from the code, but I still get the error
[ERROR] Runtime.ImportModuleError: Unable to import module 'lambda_function': No module named 'src' Traceback (most recent call last):
t
The
/
doesn't matter. Ok I think I need @Frank to chime in here, I can't remember the right way to do python imports
I would guess it has to do with the requester lib, that is included as a layer
@Frank Would you have any idea why this is failing on deployment?
f
Hey @Nicklas Nyegaard sorry for jumping in late. Can you go into the AWS Lambda console and find this function. In the code tab, do you see the
lambda_function.py
file at the root? And if you scroll down a bit on the page, can you check what the handler for this Lambda function is set to?
n
Hey @Frank sorry for the late reply, I had some vacation days. We just stuck with serverless for now for this project since it works and we are live in production now. In general we don't use Python, but C#/.NET and TypeScript/NodeJS so it is probably not something we will hit