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

Add short script to move vendor folder around #56

Merged

Conversation

LucasRoesler
Copy link
Member

@LucasRoesler LucasRoesler commented Mar 10, 2021

What

  • Extract the vendor and modules cleanup into a separate shell script.
    This provides more space for documenting each step cleanly.
  • This scripting also removes the need for the GO_REPLACE.txt hack,
    it will automatically modify any local replacements in the original
    go.mod file, so that they work as expected.
  • Update the README to describe how dependencies work in the three
    supported modes: modules with vendoring, modules without vendoring,
    and traditional vendoring via dep.
  • This new flow allows dynamic downloading of Go modules or disabling
    Go modules and only using vendor. You can not use vendor and modules
    at the same time due to how Go will validate that the vendor folder
    and the go.mod are insync

Description

How Has This Been Tested?

You can test this branch of the template using

$ faas-cli template pull https://github.com/LucasRoesler/golang-http-template#impr
oved-module-and-vendor-support
$ faas-cli new --lang golang-http

I manually tested with this handler implementation

#  publisher/handler.go
package function

import (
	"bytes"
	"fmt"
	"log"
	"net/http"
	"os"

	nats "github.com/nats-io/nats.go"
	handler "github.com/openfaas/templates-sdk/go-http"
)

var (
	subject        = "nats-test"
	defaultMessage = "Hello World"
)

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

	msg := defaultMessage
	if r.Body != nil {
		msg = string(bytes.TrimSpace(r.Body))
	}

	natsURL := nats.DefaultURL
	val, ok := os.LookupEnv("nats_url")
	if ok {
		natsURL = val
	}

	nc, err := nats.Connect(natsURL)
	if err != nil {
		errMsg := fmt.Sprintf("can not connect to nats: %s", err)
		log.Print(errMsg)

		return handler.Response{
			Body:       []byte(errMsg),
			StatusCode: http.StatusInternalServerError,
		}, err
	}
	defer nc.Close()

	s := r.Header.Get("nats-subject")
	if s != "" {
		subject = s
	}

	log.Printf("Publishing %d bytes to: %q\n", len(msg), subject)

	err = nc.Publish(subject, []byte(msg))
	if err != nil {
		log.Println(err)

		return handler.Response{
			Body:       []byte(fmt.Sprintf("can not publish to nats: %s", err)),
			StatusCode: http.StatusInternalServerError,
		}, err

	}
	return handler.Response{
		Body:       []byte(fmt.Sprintf("Published %d bytes to: %q", len(msg), subject)),
		StatusCode: http.StatusOK,
	}, err
}

How are existing users impacted? What migration steps/scripts do we need?

Users should not notice any change.

Checklist:

I have:

  • updated the documentation and/or roadmap (if required)
  • read the CONTRIBUTION guide
  • signed-off my commits with git commit -s
  • added unit tests

**What**
- Add Dockerfile testp to mov ethe vendor folder up from the function
  volder to function root. This allows the user to control the vendor
  folder and avoid collisions/validation errors from Go in modules mode
- This new flow allows dynamic downloading of Go modules _or_ disabling
  Go modules and only using vendor. You can not use vendor _and_ modules
  at the same time due to how Go will validate that the vendor folder
  and the go.mod are insync

Signed-off-by: Lucas Roesler <roesler.lucas@gmail.com>
# Add user overrides to the root go.mod, which is the only place "replace" can be used
RUN cat function/GO_REPLACE.txt >> ./go.mod || exit 0
RUN if [ -d ./function/vendor ] && [ "${GO111MODULE}" == "off" ]; then \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a note, I don't think we'll need the GO111MODULE check in 1.17, when it can't be turned off.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, i removed it in the http template, they aren't in perfect sync yet

@alexellis
Copy link
Member

This looks good. Do you have any concerns or areas where I can help with testing / validation?

@LucasRoesler
Copy link
Member Author

This looks good. Do you have any concerns or areas where I can help with testing / validation?

This change is not 100% backwards compatible. For example, there is no support for vendor and modules at the same time. When modules is enabled, it will always attempt to download the modules. Which could be an issue for things like private modules. These users must use vendoring with go mod vendor but then disable modules with the build arg.

I don't know about any other configurations, e.g. existing functions using vendoring and dep, we should test that. I am not sure about any other configurations we want to support

**What**
- Remove the tidy step because it is not needed and does not work as
  expected

Signed-off-by: Lucas Roesler <roesler.lucas@gmail.com>
@LucasRoesler LucasRoesler marked this pull request as ready for review March 20, 2021 09:41
**What**
- Remove check for modules = off from the middleware template. This
  isn't needed

Signed-off-by: Lucas Roesler <roesler.lucas@gmail.com>
@alexellis
Copy link
Member

golang-middleware template:

  • Go modules on, public modules, no vendor folder, gets downloaded every time
  • Go modules off, with a vendor folder (using GOPATH)
  • Go modules on, with a vendor folder

In Go 1.17, this will fail, because go modules are always forced on, and you cannot build from GOPATH

  • Go modules off, with a vendor folder

golang-http template:

  • Go modules on, public modules, no vendor folder, gets downloaded every time
  • Go modules off, with a vendor folder (using GOPATH)
  • Go modules on, with a vendor folder

@alexellis
Copy link
Member

Couple of suggestions for things to try:

1 - lift the handler/function code up one level and overwrite the go.mod/sum of the main entrypoint
2 - Copy the files directly into the main entrypoint and rename that to "function" so that there's no "import" needed.

**What**
- Add scripting that handles copying the function's go.mod to the
  function root. This requires several mutations to handle renaming the
  module and dealig with local replacements of the function
- Add scriptin to handle the vendor/modules.txt. It also handles
  mutations to deal with the local replacement of the function code

Signed-off-by: Lucas Roesler <roesler.lucas@gmail.com>
@LucasRoesler
Copy link
Member Author

@alexellis i have pushed a change that lifts the go.mod/go.sum to the main entrypoint, but only for the http template. With this, i have had successful builds with

  1. modules ON, no vendor folder
  2. modules ON, with vendor folder
  3. modules OFF, with vendor folder

This also includes have sub-packages in my handler code, here is the structure of my test function with the vendor folder

├── publisher
│   ├── go.mod
│   ├── go.sum
│   ├── handler.go
│   ├── pkg
│   │   └── version
│   │       └── version.go
│   └── vendor
│       ├── github.com
│       │   ├── nats-io
│       │   │   ├── nats.go
│       │   │   │   ├── CODE-OF-CONDUCT.md
│       │   │   │   ├── context.go
│       │   │   │   ├── dependencies.md
│       │   │   │   ├── enc.go
│       │   │   │   ├── encoders
│       │   │   │   │   └── builtin
│       │   │   │   │       ├── default_enc.go
│       │   │   │   │       ├── gob_enc.go
│       │   │   │   │       └── json_enc.go
│       │   │   │   ├── go.mod
│       │   │   │   ├── go.sum
│       │   │   │   ├── GOVERNANCE.md
│       │   │   │   ├── js.go
│       │   │   │   ├── jsm.go
│       │   │   │   ├── LICENSE
│       │   │   │   ├── MAINTAINERS.md
│       │   │   │   ├── nats.go
│       │   │   │   ├── netchan.go
│       │   │   │   ├── parser.go
│       │   │   │   ├── README.md
│       │   │   │   ├── timer.go
│       │   │   │   ├── TODO.md
│       │   │   │   └── util
│       │   │   │       ├── tls.go
│       │   │   │       └── tls_go17.go
│       │   │   ├── nkeys
│       │   │   │   ├── crc16.go
│       │   │   │   ├── creds_utils.go
│       │   │   │   ├── go.mod
│       │   │   │   ├── go.sum
│       │   │   │   ├── GOVERNANCE.md
│       │   │   │   ├── keypair.go
│       │   │   │   ├── LICENSE
│       │   │   │   ├── main.go
│       │   │   │   ├── MAINTAINERS.md
│       │   │   │   ├── public.go
│       │   │   │   ├── README.md
│       │   │   │   ├── strkey.go
│       │   │   │   └── TODO.md
│       │   │   └── nuid
│       │   │       ├── GOVERNANCE.md
│       │   │       ├── LICENSE
│       │   │       ├── MAINTAINERS.md
│       │   │       ├── nuid.go
│       │   │       └── README.md
│       │   └── openfaas
│       │       └── templates-sdk
│       │           ├── go-http
│       │           │   ├── handler.go
│       │           │   └── README.md
│       │           └── LICENSE
│       ├── golang.org
│       │   └── x
│       │       └── crypto
│       │           ├── AUTHORS
│       │           ├── CONTRIBUTORS
│       │           ├── ed25519
│       │           │   ├── ed25519.go
│       │           │   ├── ed25519_go113.go
│       │           │   └── internal
│       │           │       └── edwards25519
│       │           │           ├── const.go
│       │           │           └── edwards25519.go
│       │           ├── LICENSE
│       │           └── PATENTS
│       ├── handler
│       │   └── function
│       │       └── pkg
│       │           └── version
│       │               └── version.go
│       └── modules.txt

It is important to note that this requires a small bit of schenanigens for local development, specifically, if you have sub-packages, then you need to import them relative to handler/function/...., not the "actual" function/... and then you need to add a replace of handler/function => ./

This is what the function/go.mod looks like so that local development works as expected

module function

go 1.16

require (
	github.com/nats-io/nats.go v1.10.1-0.20210228004050-ed743748acac
	github.com/openfaas/templates-sdk v0.0.0-20200723110415-a699ec277c12
	handler/function v0.0.0-00010101000000-000000000000
)

replace handler/function => ./

A side of effect of this approach is that the GO_REPLACE.txt is not needed anymore because we are just copying the go.mod to the project root, it will contain any of the original replace statements anyway.

ARG GOFLAGS=""

# Add user overrides to the root go.mod, which is the only place "replace" can be used
RUN cat function/GO_REPLACE.txt >> ./go.mod || exit 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lucas: is this line needed still?

# remove any local require for the handler/function, this can exist _if_ the
# developer has subpackages in their handler. It is ok to just remove it
# because we will replace it later
grep -v "handler/function" go.mod > gomod2; mv gomod2 go.mod && \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an issue with piping this instead? Would ; work as &&?

@alexellis
Copy link
Member

Question for you - could this be a bash script that's copied in from the base template?

@alexellis
Copy link
Member

From our call - what does a sub-package look like now with these changes?

**What**
- Extract the vendor and modules cleanup into a separate shell script.
  This provides more space for documenting each step cleanly.
- This scripting also removes the need for the `GO_REPLACE.txt` hack,
  it will automatically modify any local replacements in the original
  go.mod file, so that they work as expected.
- Update the README to describe how dependencies work in the three
  supported modes: modules with vendoring, modules without vendoring,
  and traditional vendoring via dep.

Signed-off-by: Lucas Roesler <roesler.lucas@gmail.com>
@LucasRoesler
Copy link
Member Author

@alexellis i have moved the logic to a shell script now. Note, from the commit message, this will automatically handle the GO_REPLACE so that we don't need the extra file anymore:

  • Extract the vendor and modules cleanup into a separate shell script.
    This provides more space for documenting each step cleanly.
  • This scripting also removes the need for the GO_REPLACE.txt hack,
    it will automatically modify any local replacements in the original
    go.mod file, so that they work as expected.
  • Update the README to describe how dependencies work in the three
    supported modes: modules with vendoring, modules without vendoring,
    and traditional vendoring via dep.

I would like to also commit a sample function to the repo, what do you think? It would be really helpful to have a test/sample function that we can all reference as a baseline verification that the template compiles, at least for the two module use cases: with and without vendoring.

**What**
- Add check and skip when the funciton folder is not a module
Signed-off-by: Lucas Roesler <roesler.lucas@gmail.com>
@LucasRoesler LucasRoesler force-pushed the improved-module-and-vendor-support branch from 129d3e5 to 28697c3 Compare April 5, 2021 09:29
@LucasRoesler
Copy link
Member Author

ping-ity ping ping

@LucasRoesler
Copy link
Member Author

@alexellis can you re-review this and let me know what needs to be done for it to be merged? I feel like it is ready to be merged

go mod edit -replace=handler/function=./
```

Now if you want to reference the`version` sub-package, import it as
Copy link
Member

@alexellis alexellis May 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: needs a space after "the"

Copy link
Member

@alexellis alexellis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. Let's see how we get on with this.

Will the script work on Alpine Linux's shell (ash) and on Ubuntu's bash shell?

@alexellis alexellis merged commit 5d6376e into openfaas:master May 5, 2021

# Copy the user's go.mod
mv -f ./function/go.mod .
mv -f ./function/go.sum .

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like this fails in case the file is not present, making the docker image construction fail

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes it currently assumes that you have a go.sum if you have a go.mod. Per the recommendations from the Go team, the file should exist and should be committed with your project https://github.com/golang/go/wiki/Modules#should-i-commit-my-gosum-file-as-well-as-my-gomod-file

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok, that I did not know. Thank you very much, I have modified my functions!

@LucasRoesler
Copy link
Member Author

Approved. Let's see how we get on with this.

Will the script work on Alpine Linux's shell (ash) and on Ubuntu's bash shell?

@alexellis the script is written and run using sh which i believe is a subset of both ash and bash, so it should be fine

@LucasRoesler LucasRoesler deleted the improved-module-and-vendor-support branch May 6, 2021 06:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants