Skip to content

Commit 974861a

Browse files
LucasRoesleralexellis
authored andcommitted
Extra modules cleanup into a script and document it
**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 <[email protected]>
1 parent 067382d commit 974861a

File tree

5 files changed

+329
-57
lines changed

5 files changed

+329
-57
lines changed

README.md

+35-7
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ The two templates are equivalent with `golang-http` using a structured request/r
2828
You can manage dependencies in one of the following ways:
2929

3030
* 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
31+
* 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`
3132
* For traditional vendoring with `dep` give no argument, or add `--build-arg GO111MODULE=off` to `faas-cli up`
3233

3334
## 1.0 golang-http
@@ -361,7 +362,37 @@ func Handle(w http.ResponseWriter, r *http.Request) {
361362
}
362363
```
363364

364-
#### Advanced usage - Go sub-modules via `GO_REPLACE.txt`
365+
#### Advanced usage
366+
367+
##### Sub-packages
368+
369+
It is often natural to organize your code into sub-packages, for example you may have a function with the following structure
370+
371+
```
372+
./echo
373+
├── go.mod
374+
├── go.sum
375+
├── handler.go
376+
└── pkg
377+
└── version
378+
└── version.go
379+
```
380+
381+
First update your go.mod file to replace `handler/function` with your local folder
382+
383+
```go
384+
go mod edit -replace=handler/function=./
385+
```
386+
387+
Now if you want to reference the`version` sub-package, import it as
388+
389+
```go
390+
import "handler/function/pkg/version"
391+
```
392+
393+
This replacement is handled gracefully by the template at build time and your local development environment will now recognize the sub-package.
394+
395+
##### Go sub-modules
365396

366397
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`.
367398

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

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

389420
```
390-
replace github.com/alexellis/vault/purchase/handlers => ./function/handlers
421+
go mod edit -replace=github.com/alexellis/vault/purchase/handlers=./handlers
391422
```
392423

393-
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`.
394-
395-
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.
396-
424+
At build time, this relative path will be handled correctly inside the template.
397425

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

template/golang-http/Dockerfile

+3-42
Original file line numberDiff line numberDiff line change
@@ -20,49 +20,10 @@ COPY . .
2020
ARG GO111MODULE="off"
2121
ARG GOPROXY=""
2222
ARG GOFLAGS=""
23+
ARG DEBUG=0
2324

24-
# Add user overrides to the root go.mod, which is the only place "replace" can be used
25-
RUN cat function/GO_REPLACE.txt >> ./go.mod || exit 0
26-
RUN if [ -d ./function/vendor ]; then \
27-
echo "moving handler vendor" && \
28-
mv -f ./function/vendor .;\
29-
else \
30-
echo "vendor not found "; \
31-
fi
32-
33-
RUN if [ "${GO111MODULE}" == 'on' ]; then \
34-
# copy the user's go.mod
35-
mv -f ./function/go.mod . && \
36-
mv -f ./function/go.sum . && \
37-
# clean up the go.mod,
38-
# remove any local require for the handler/function, this can exist _if_ the
39-
# developer has subpackages in their handler. It is ok to just remove it
40-
# because we will replace it later
41-
grep -v "handler/function" go.mod > gomod2; mv gomod2 go.mod && \
42-
# now update the go.mod
43-
# first, replace handler/function to point at the local code
44-
# second, we need to rename the module to handler to match our main
45-
go mod edit \
46-
-replace=handler/function=./function \
47-
-module handler && \
48-
cat go.mod && \
49-
if [ -d ./vendor ]; then \
50-
# when vendored, we need to do similar edits to the vendor/modules.txt
51-
# first we need to replace any possible copy of the handler code
52-
rm -rf vendor/handler && \
53-
# in modules.txt, we remove existing references to the handler/function
54-
# these are replaced later with the new structure
55-
grep -v "handler/function" ./vendor/modules.txt> modulestext; mv modulestext ./vendor/modules.txt && \
56-
# add the mising replace to the vendor/modules.txt
57-
echo "## explicit" >> ./vendor/modules.txt && \
58-
echo "# handler/function => ./function" >> ./vendor/modules.txt && \
59-
cat ./vendor/modules.txt; \
60-
else \
61-
echo "skip adding replace to ' ./vendor/modules.txt'"; \
62-
fi \
63-
else \
64-
echo "skip go.mod handling"; \
65-
fi
25+
# Lift the vendor and go.mod to the main package, cleanup any relative references
26+
RUN sh modules-cleanup.sh
6627

6728
# Run a gofmt and exclude all vendored code.
6829
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; }
+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#!/usr/bin/env sh
2+
3+
set -e
4+
5+
GO111MODULE=$(go env GO111MODULE)
6+
7+
# move_vendor will copy the function's vendor folder,
8+
# if it exists.
9+
move_vendor() {
10+
if [ ! -d ./function/vendor ]; then
11+
echo "vendor not found"
12+
return
13+
fi
14+
15+
echo "moving function vendor"
16+
mv -f ./function/vendor .
17+
}
18+
19+
20+
# cleanup_gomod will move the function's go module
21+
cleanup_gomod() {
22+
23+
# Nothing to do when modules is explicitly off
24+
# the z prefix protects against any SH wonkiness
25+
# see https://stackoverflow.com/a/18264223
26+
if [ "z$GO111MODULE" = "zoff" ]; then
27+
echo "modules disabled, skipping go.mod cleanup"
28+
return;
29+
fi
30+
31+
echo "cleaning up go.mod"
32+
33+
# Copy the user's go.mod
34+
mv -f ./function/go.mod .
35+
mv -f ./function/go.sum .
36+
37+
# Clean up the go.mod
38+
39+
# Cleanup any sub-module replacements.
40+
# This requires modifying any replace that points to "./*",
41+
# the user has will use this to reference sub-modules instead
42+
# of sub-packages, which we cleanup below.
43+
echo "cleanup local replace statements"
44+
# 1. Replace references to the local folder with `./function`
45+
sed -i 's/=> \.\//=> \.\/function\//' go.mod
46+
47+
48+
# Remove any references to the handler/function module.
49+
# It is ok to just remove it because we will replace it later.
50+
#
51+
# Note that these references may or may not exist. We expect the
52+
# go.mod to have a replace statement _if_ developer has subpackages
53+
# in their handler. In this case they will need a this replace statement
54+
#
55+
# replace handler/function => ./
56+
#
57+
# `go mod` will then add a line that looks like
58+
#
59+
# handler/function v0.0.0-00010101000000-000000000000
60+
#
61+
# both of these lines need to be replaced, this grep selects everything
62+
# _except_ those offending lines.
63+
grep -v "\shandler/function" go.mod > gomod2; mv gomod2 go.mod
64+
65+
# Now update the go.mod
66+
#
67+
# 1. use replace so that imports of handler/function use the local code
68+
# 2. we need to rename the module to handler because our main.go assumes
69+
# this is the package name
70+
go mod edit \
71+
-replace=handler/function=./function \
72+
-module handler
73+
74+
75+
76+
if [ "$DEBUG" -eq 1 ]; then
77+
cat go.mod
78+
echo ""
79+
fi
80+
}
81+
82+
83+
# cleanup_vendor_modulestxt will cleanup the modules.txt file in the vendor folder
84+
# this file is needed when modules are enabled and it must be in sync with the
85+
# go.mod. To function correctly we need to modify the references to handler/function,
86+
# if they exist.
87+
cleanup_vendor_modulestxt() {
88+
if [ ! -d ./vendor ]; then
89+
echo "no vendor found, skipping modules.txt cleanup"
90+
return
91+
fi
92+
93+
# Nothing to do when modules is explicitly off
94+
# the z prefix protects against any SH wonkiness
95+
# see https://stackoverflow.com/a/18264223
96+
if [ "z$GO111MODULE" = "zoff" ]; then
97+
echo "modules disabled, skipping modules.txt cleanup"
98+
return;
99+
fi
100+
101+
echo "cleanup vendor/modules.txt"
102+
103+
# just in case
104+
touch "./vendor/modules.txt"
105+
106+
# when vendored, we need to do similar edits to the vendor/modules.txt
107+
# as we did to the go.mod
108+
109+
# 1. we need to replace any possible copy of the handler code
110+
rm -rf vendor/handler && \
111+
112+
# 2. in modules.txt, we remove existing references to the handler/function
113+
# we reconstruct these in the last step
114+
grep -v "\shandler/function" ./vendor/modules.txt> modulestext; mv modulestext ./vendor/modules.txt
115+
116+
# 3. Handle any other local replacements.
117+
# any replace that points to `./**` needs to be udpat echo "cleanup local replace statements"
118+
sed -i 's/=> \.\//=> \.\/function\//' ./vendor/modules.txt
119+
120+
# 4. To make the modules.txt consistent with the new go.mod,
121+
# we add the mising replace to the vendor/modules.txt
122+
echo "## explicit" >> ./vendor/modules.txt
123+
echo "# handler/function => ./function" >> ./vendor/modules.txt
124+
125+
if [ "$DEBUG" -eq 1 ]; then
126+
cat ./vendor/modules.txt;
127+
echo ""
128+
fi
129+
}
130+
131+
# has_local_replacement checks if the file contains local go module replacement
132+
has_local_replacement() {
133+
return "$(grep -E -c '=> \./\S+' "$1")"
134+
}
135+
136+
137+
################
138+
# main
139+
################
140+
move_vendor
141+
142+
cleanup_gomod
143+
144+
cleanup_vendor_modulestxt

template/golang-middleware/Dockerfile

+3-8
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,10 @@ COPY . .
2020
ARG GO111MODULE="off"
2121
ARG GOPROXY=""
2222
ARG GOFLAGS=""
23+
ARG DEBUG=0
2324

24-
# Add user overrides to the root go.mod, which is the only place "replace" can be used
25-
RUN cat function/GO_REPLACE.txt >> ./go.mod || exit 0
26-
RUN if [ -d ./function/vendor ]; then \
27-
echo "moving handler vendor" && \
28-
mv -f ./function/vendor .; \
29-
else \
30-
echo "using modules or vendor not found "; \
31-
fi
25+
# Lift the vendor and go.mod to the main package, cleanup any relative references
26+
RUN sh modules-cleanup.sh
3227

3328
# Run a gofmt and exclude all vendored code.
3429
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; }

0 commit comments

Comments
 (0)