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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
42 changes: 35 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ The two templates are equivalent with `golang-http` using a structured request/r
You can manage dependencies in one of the following ways:

* To use Go modules without vendoring, add `--build-arg GO111MODULE=on` to `faas-cli up`, you can also use `--build-arg GOPROXY=https://` if you want to use your own mirror for the modules
* You can also Go modules with vendoring, run `go mod vendor` in your function folder and add `--build-arg GO111MODULE=on` to `faas-cli up`
* For traditional vendoring with `dep` give no argument, or add `--build-arg GO111MODULE=off` to `faas-cli up`

## 1.0 golang-http
Expand Down Expand Up @@ -361,7 +362,37 @@ func Handle(w http.ResponseWriter, r *http.Request) {
}
```

#### Advanced usage - Go sub-modules via `GO_REPLACE.txt`
#### Advanced usage

##### Sub-packages

It is often natural to organize your code into sub-packages, for example you may have a function with the following structure

```
./echo
├── go.mod
├── go.sum
├── handler.go
└── pkg
└── version
└── version.go
```

First update your go.mod file to replace `handler/function` with your local folder

```go
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"


```go
import "handler/function/pkg/version"
```

This replacement is handled gracefully by the template at build time and your local development environment will now recognize the sub-package.

##### Go sub-modules

For this example you will need to be using Go 1.13 or newer and Go modules, enable this via `faas-cli build --build-arg GO111MODULE=on`.

Expand All @@ -384,16 +415,13 @@ func Echo(w http.ResponseWriter, r *http.Request) {
}
```

To include a relative module such as this new `handlers` package, you should create a `GO_REPLACE.txt` file as follows:
To include a relative module such as this new `handlers` package, you should update your `go.mod` file as follows:

```
replace github.com/alexellis/vault/purchase/handlers => ./function/handlers
go mod edit -replace=github.com/alexellis/vault/purchase/handlers=./handlers
```

How did we get to that? Let's say your GOPATH for your GitHub repo is: `github.com/alexellis/vault/` and your OpenFaaS function is `purchase` generated by `faas-cli new purchase --lang golang-middleware`.

Your relative GOPATH is: `github.com/alexellis/vault/purchase`, so add a redirect as per below to redirect the "handlers" package to where it exists in the build container.

At build time, this relative path will be handled correctly inside the template.

Now if you want to reference the handlers package from within your `handler.go` write the following:

Expand Down
30 changes: 19 additions & 11 deletions template/golang-http/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,49 @@ ARG TARGETARCH

RUN apk --no-cache add git

ENV CGO_ENABLED=0

COPY --from=watchdog /fwatchdog /usr/bin/fwatchdog
RUN chmod +x /usr/bin/fwatchdog

ENV CGO_ENABLED=0

RUN mkdir -p /go/src/handler
WORKDIR /go/src/handler
COPY . .

ARG GO111MODULE="off"
ARG GOPROXY=""
ARG GOFLAGS=""
ARG DEBUG=0

# Lift the vendor and go.mod to the main package, cleanup any relative references
RUN sh modules-cleanup.sh

# Run a gofmt and exclude all vendored code.
RUN test -z "$(gofmt -l $(find . -type f -name '*.go' -not -path "./vendor/*" -not -path "./function/vendor/*"))" || { echo "Run \"gofmt -s -w\" on your Golang code"; exit 1; }

ARG GO111MODULE="off"
ARG GOPROXY=""

WORKDIR /go/src/handler/function

RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go test ./... -cover

WORKDIR /go/src/handler
RUN CGO_ENABLED=${CGO_ENABLED} GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
go build --ldflags "-s -w" -a -installsuffix cgo -o handler .
RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go test handler/function/... -cover

FROM --platform=${TARGETPLATFORM:-linux/amd64} alpine:3.13
# Add non root user and certs
RUN apk --no-cache add ca-certificates \
&& addgroup -S app && adduser -S -g app app
# Split instructions so that buildkit can run & cache
# Split instructions so that buildkit can run & cache
# the previous command ahead of time.
RUN mkdir -p /home/app \
&& chown app /home/app

WORKDIR /home/app

COPY --from=build /go/src/handler/handler .
COPY --from=build /usr/bin/fwatchdog .
COPY --from=build /go/src/handler/function/ .

RUN chown -R app /home/app
COPY --from=build --chown=app /go/src/handler/handler .
COPY --from=build --chown=app /usr/bin/fwatchdog .
COPY --from=build --chown=app /go/src/handler/function/ .

USER app

Expand Down
149 changes: 149 additions & 0 deletions template/golang-http/modules-cleanup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#!/usr/bin/env sh

set -e

GO111MODULE=$(go env GO111MODULE)

# move_vendor will copy the function's vendor folder,
# if it exists.
move_vendor() {
if [ ! -d ./function/vendor ]; then
echo "vendor not found"
return
fi

echo "moving function vendor"
mv -f ./function/vendor .
}


# cleanup_gomod will move the function's go module
cleanup_gomod() {

# Nothing to do when modules is explicitly off
# the z prefix protects against any SH wonkiness
# see https://stackoverflow.com/a/18264223
if [ "z$GO111MODULE" = "zoff" ]; then
echo "modules disabled, skipping go.mod cleanup"
return;
fi

if [ ! -f ./function/go.mod ]; then
echo "module not initialized, skipping go.mod cleanup"
return;
fi

echo "cleaning up go.mod"

# 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!


# Clean up the go.mod

# Cleanup any sub-module replacements.
# This requires modifying any replace that points to "./*",
# the user has will use this to reference sub-modules instead
# of sub-packages, which we cleanup below.
echo "cleanup local replace statements"
# 1. Replace references to the local folder with `./function`
sed -i 's/=> \.\//=> \.\/function\//' go.mod


# Remove any references to the handler/function module.
# It is ok to just remove it because we will replace it later.
#
# Note that these references may or may not exist. We expect the
# go.mod to have a replace statement _if_ developer has subpackages
# in their handler. In this case they will need a this replace statement
#
# replace handler/function => ./
#
# `go mod` will then add a line that looks like
#
# handler/function v0.0.0-00010101000000-000000000000
#
# both of these lines need to be replaced, this grep selects everything
# _except_ those offending lines.
grep -v "\shandler/function" go.mod > gomod2; mv gomod2 go.mod

# Now update the go.mod
#
# 1. use replace so that imports of handler/function use the local code
# 2. we need to rename the module to handler because our main.go assumes
# this is the package name
go mod edit \
-replace=handler/function=./function \
-module handler



if [ "$DEBUG" -eq 1 ]; then
cat go.mod
echo ""
fi
}


# cleanup_vendor_modulestxt will cleanup the modules.txt file in the vendor folder
# this file is needed when modules are enabled and it must be in sync with the
# go.mod. To function correctly we need to modify the references to handler/function,
# if they exist.
cleanup_vendor_modulestxt() {
if [ ! -d ./vendor ]; then
echo "no vendor found, skipping modules.txt cleanup"
return
fi

# Nothing to do when modules is explicitly off
# the z prefix protects against any SH wonkiness
# see https://stackoverflow.com/a/18264223
if [ "z$GO111MODULE" = "zoff" ]; then
echo "modules disabled, skipping modules.txt cleanup"
return;
fi

echo "cleanup vendor/modules.txt"

# just in case
touch "./vendor/modules.txt"

# when vendored, we need to do similar edits to the vendor/modules.txt
# as we did to the go.mod

# 1. we need to replace any possible copy of the handler code
rm -rf vendor/handler && \

# 2. in modules.txt, we remove existing references to the handler/function
# we reconstruct these in the last step
grep -v "\shandler/function" ./vendor/modules.txt> modulestext; mv modulestext ./vendor/modules.txt

# 3. Handle any other local replacements.
# any replace that points to `./**` needs to be udpat echo "cleanup local replace statements"
sed -i 's/=> \.\//=> \.\/function\//' ./vendor/modules.txt

# 4. To make the modules.txt consistent with the new go.mod,
# we add the mising replace to the vendor/modules.txt
echo "## explicit" >> ./vendor/modules.txt
echo "# handler/function => ./function" >> ./vendor/modules.txt

if [ "$DEBUG" -eq 1 ]; then
cat ./vendor/modules.txt;
echo ""
fi
}

# has_local_replacement checks if the file contains local go module replacement
has_local_replacement() {
return "$(grep -E -c '=> \./\S+' "$1")"
}


################
# main
################
move_vendor

cleanup_gomod

cleanup_vendor_modulestxt

This file was deleted.

This file was deleted.

This file was deleted.