golang-http-template: Unable to use vendoring with golang-http

Why do you need this?

I need to be able to use vendoring with the golang-http template because it is how I get private Go code into my functions.

Expected Behaviour

This should work without error, when using a vendor folder and any other stack.yml flags or build-args required:

faas-cli build

Current Behaviour

Step 29/42 : RUN ${GO} build --ldflags "-s -w" -a -installsuffix cgo -o handler .
 ---> Running in b9e5d187d314
Setting vendor mode env variables
main.go:18:2: cannot find package "github.com/openfaas/templates-sdk/go-http" in any of:
        /usr/local/go/src/github.com/openfaas/templates-sdk/go-http (from $GOROOT)
        /go/src/github.com/openfaas/templates-sdk/go-http (from $GOPATH)
The command '/bin/sh -c ${GO} build --ldflags "-s -w" -a -installsuffix cgo -o handler .' returned a non-zero code: 1
[0] < Building go1 done in 5.91s.
[0] Worker done.

Total build time: 5.91s
Errors received during build:
- [go1] received non-zero exit code from build, error: The command '/bin/sh -c ${GO} build --ldflags "-s -w" -a -installsuffix cgo -o handler .' returned a non-zero code: 1

List All Possible Solutions and Workarounds

  1. Create a new separate template golang-http-vendoring that only works with vendoring
  2. Stop maintaining a golang-http template at all, migrate everyone to golang-middleware
  3. Use interfaces instead of structs, so that the main.go module doesn’t have to reference the SDK, which seems to be the source of the problem - full example, and it’s a breaking change: https://github.com/alexellis/golang-http-template
  4. Consider a new direction that looks like AWS Lambda’s Go handler - https://docs.aws.amazon.com/lambda/latest/dg/golang-handler.html https://docs.aws.amazon.com/lambda/latest/dg/golang-handler.html
  5. Use Go’s plugin capability, to build the function and entry point separately, and link them after the fact. Unfortunately this seems to require CGO and breaks portability - https://github.com/golang/go/issues/19569

For 4), the function’s handler owns the main() entrypoint instead of the hidden underlying code, and calls a utility method to set up the HTTP server etc.

package main

import (
        "fmt"
        "context"
        "github.com/openfaas/golang-http/entrypoint"
)

type MyEvent struct {
        Name string `json:"name"`
}

func HandleRequest(ctx context.Context, name MyEvent) (string, error) {
        return fmt.Sprintf("Hello %s!", name.Name ), nil
}

func main() {
        entrypoint.Start(HandleRequest)
}

For 3), the code would end up looking like this for a handler:

(this can be compared to the second code sample to see what’s different)

package function

import (
    "fmt"
    "net/http"

    gohttp "github.com/alexellis/templates-sdk/go-http"
    logrus "github.com/sirupsen/logrus"
)

// Handle a function invocation
func Handle(req gohttp.Request) (gohttp.Response, error) {
    var err error

    logrus.Info("Value of body: ", string(req.GetBody()))

    message := fmt.Sprintf("Body: %s", string(req.GetBody()))

    return &gohttp.FunctionResponse{
        Body:       []byte(message),
        StatusCode: http.StatusOK,
    }, err
}

What is your preferred solution?

I like 3) and to lessen its impact to existing users, we could consider promoting golang-http into the primary community templates repository and deprecating the one we use here.

Alternatively, we could call it "go-http1.18orgolang-http1.18as a new template, withgolang-http1.19` and so on coming after that.

Steps to Reproduce (for bugs)

faas-cli template store pull golang-http-template
faas-cli new --lang golang-http  --prefix alexellis2 withlogrus

Edit withlogrus/handler.go:

package function

import (
	"fmt"
	"io"
	"net/http"

	"github.com/sirupsen/logrus"
)

func Handle(w http.ResponseWriter, r *http.Request) {
	var input []byte

	if r.Body != nil {
		defer r.Body.Close()

		body, _ := io.ReadAll(r.Body)

		input = body
	}

	logrus.Debug(fmt.Sprintf("Handle; %q", input))

	w.WriteHeader(http.StatusOK)
	w.Write([]byte(fmt.Sprintf("Body: %q", string(input))))
}

Then edit withlogrus.yml, adding:

    build_args:
      GO111MODULE: off
      GOFLAGS: "-mod=vendor"

Then try a build:

cd golang-http-template

go get
go mod vendor

cd ..
faas-cli build -f withlogrus.yml

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 1
  • Comments: 16 (12 by maintainers)

Commits related to this issue

Most upvoted comments

@alexellis and @MrWormHole

I think after a lot of time thinking on this, that just reverting to the previous behavior before workspaces makes the most sense. I am not a purist on this, if a little bash makes the template work smoothly for 99% of use-cases, then let’s use some bash. Especially because the bash script is just calling some go mod edit commands vs directly editing the files, it is relatively stable.

Originally, I thought the workspaces would allow us to really remove the bash scripts, but they don’t seem to work as expected.

I have prepared a branch for re-adding the bash scripts and tested it against my golang template sandbox https://github.com/LucasRoesler/golang-http-template-examples it seems to behave well. But I could be missing an important test case, so please take a look and let me know.

Here is the branch I created https://github.com/openfaas/golang-http-template/compare/master...LucasRoesler:feat-revert-to-bash-scripts?expand=1

As an aside, but something I saw in the conversation above. In general, I would expect most projects to use only one or the other template, not a mix of both. Just for code consistency in the project to ease the developer experience, I would avoid mixing and matching templates within a project. Within an organization, sure use whichever fits best for your project, but stick with just one for a project.

It looks like we have three main options for going forward.

  1. Archive the golang-http template
  2. Retain workspaces, without bash scripts and use the interface change as I proposed, which will require all users to make some changes to continue using the template, along with fixes from us to documentation, blog posts, example repos and eBooks.
  3. Revert to how things were prior to the introduction of workspaces and do some additional checking

I’d be interested in speed differences between 2 and 3 for an uncached container build, or one with --no-cache passed in.

Alex

i will just make the changes whenever needed and roll the new ones out. Maintaining a classic may be a good idea but everyone has to change anyway since it’s a core go problem. With enough notice and planned upgrade it shouldn’t be an issue.