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

Golang handler confusion #1358

Open
and3rson opened this issue Mar 28, 2022 · 5 comments
Open

Golang handler confusion #1358

and3rson opened this issue Mar 28, 2022 · 5 comments

Comments

@and3rson
Copy link

and3rson commented Mar 28, 2022

There doesn't seem to be a way to make the project runnable in both AWS and offline.

Attempt 1 (from aws-go)

So I have this config (from aws-go template):

functions:
  hello:
    handler: bin/hello
    events:
      - httpApi:
          path: /hello
          method: get
  world:
    handler: bin/world
    events:
      - httpApi:
          path: /world
          method: get

This works on AWS and with --useDocker, but fails locally with an error:

$ curl 127.0.0.1:5555/hello
{"errorMessage":"ENOENT: no such file or directory, open '/home/anderson/src/serverlessapp.go'","errorType":"Error","stackTrace":[]}

Please note the path above: it's pointing to an unexisting serverlessapp.go file (which in fact is a folder of my project, serverlessapp)

Attempt 2 (from PR #1320)

I've checked #1320 and that PR contains a different config, so I've rewritten my config by changing handler string:

functions:
  hello:
    handler: hello/main.go  # <-------
    events:
      - httpApi:
          path: /hello
          method: get
  world:
    handler: world/main.go  # <-------
    events:
      - httpApi:
          path: /world
          method: get

Now, local execution succeeds:

$ curl 127.0.0.1:5555/hello
{"message":"Go Serverless v1.0! Your function executed successfully!"}

However, now the code does not work when deployed to AWS:

$ curl https://XXXXXXXXXX.execute-api.us-east-1.amazonaws.com/hello
{"message":"Internal Server Error"}
$ serverless logs --function hello
Running "serverless" from node_modules
START
fork/exec /var/task/hello/main.go: no such file or directory: PathError
null
END Duration: 2.96 ms (init: 19.76 ms) Memory Used: 19 MB
START
fork/exec /var/task/hello/main.go: no such file or directory: PathError
null
END Duration: 2.62 ms (init: 19.52 ms) Memory Used: 19 MB

Question: What is the correct way to configure my project? I cannot seem to find a way to run a project with the same config both locally and on AWS. Config 1 works on AWS only, config 2 works locally only...

Thanks in advance!

@NSerbin
Copy link

NSerbin commented May 23, 2022

I have the same problem... if we don't use serverless offline --useDocker the offline plugin try to build the folder project as project.go

@TheTeaCat
Copy link
Contributor

TheTeaCat commented May 31, 2022

My current workaround for this is to have a serverless.yml for local development, then create a serverless.prod.yml.

I can't seem to find any docs on how to do substitutions nicely in these yaml files, or maybe I'm just new to all this, but I extrapolated from the documentation provided about how to create multiple files for the functions list here:

# serverless.yml
---
functions:
  - ${file(./foo-functions.yml)}
  - ${file(./bar-functions.yml)}

And then applied the syntax used in this GitHub issue comment to create my serverless.yml:

service: ${file(serverless.prod.yml):service}
frameworkVersion: ${file(serverless.prod.yml):frameworkVersion}
provider: ${file(serverless.prod.yml):provider}

plugins:
  - serverless-offline

functions:
  - ${file(functions/serverless.yml)}

I've then got my serverless.prod.yml pointing to a different yaml file for my functions:

service: my-service

frameworkVersion: '3'

provider:
  name: aws
  runtime: go1.x
  stage: dd-dev
  region: eu-west-2

functions:
  - ${file(functions/serverless.prod.yml)}

I can then do the following command for dev:

sls offline

And for prod I run my makefiles and then do:

sls deploy -c serverless.prod.yml

@dnalborczyk
Copy link
Collaborator

if anyone could look into this and create a PR that would be appreciated. I'm guessing the (initial) implementation was done by (a) contributor(s).

@TheTeaCat
Copy link
Contributor

TheTeaCat commented Jul 23, 2022

@dnalborczyk The root of the issue here seems to be that the serverless framework expects binaries for its handlers when you're using Go, but there's no way to express how those binaries should be created in the serverless.yml.

The standard way this is dealt with is by providing a makefile that needs to be run before sls deploy. You can see makefiles are provided in all the serverless examples for Go, and here's a blog post on the serverless website for Go support where a makefile is used before running sls deploy: https://www.serverless.com/blog/framework-example-golang-lambda-support/

These makefiles usually output the binaries to a bin directory to which the handler option in the serverless.yml points, however the current golang runner in the serverless-offline plugin expects the handler option to point to a single Go source file (.go) that can be copied to a tmp directory and then built with go build.

I think the correct approach would be to change the golang runner to use binary handlers instead, and expect people to run their makefiles before doing sls offline. The drawback of this is that we'd lose hot reloads - developers would have to run their makefiles manually to update the binaries after every code change. 😔

In order to keep the hot reloads, we could add a "build step"(?) option to the serverless-offline plugin which developers could point to their makefiles. The go runner could then execute the makefile on every invocation, and then execute the binary handlers. This would mean the binary is executed always up to date with the latest code changes.

@lucasoares
Copy link

@dnalborczyk The root of the issue here seems to be that the serverless framework expects binaries for its handlers when you're using Go, but there's no way to express how those binaries should be created in the serverless.yml.

The standard way this is dealt with is by providing a makefile that needs to be run before sls deploy. You can see makefiles are provided in all the serverless examples for Go, and here's a blog post on the serverless website for Go support where a makefile is used before running sls deploy: https://www.serverless.com/blog/framework-example-golang-lambda-support/

These makefiles usually output the binaries to a bin directory to which the handler option in the serverless.yml points, however the current golang runner in the serverless-offline plugin expects the handler option to point to a single Go source file (.go) that can be copied to a tmp directory and then built with go build.

I think the correct approach would be to change the golang runner to use binary handlers instead, and expect people to run their makefiles before doing sls offline. The drawback of this is that we'd lose hot reloads - developers would have to run their makefiles manually to update the binaries after every code change. 😔

In order to keep the hot reloads, we could add a "build step"(?) option to the serverless-offline plugin which developers could point to their makefiles. The go runner could then execute the makefile on every invocation, and then execute the binary handlers. This would mean the binary is executed always up to date with the latest code changes.

Hot reloads are ok to lose, but what about debugging? :(

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

5 participants