Reputation: 45
I do a Dockerfile to put my development in production. However when I run my docker I have this following error :
http: panic serving xxx.xxx.xxx.xxx:xxxxx: open views/public/index.html: no such file or directory
My mistake is in my Dockerfile but I don't see where..
My Dockerfile :
FROM golang:alpine as builder
RUN apk update && apk add --no-cache git ca-certificates gcc g++ make && update-ca-certificates
RUN adduser -D -g '' appuser
WORKDIR /usr/src/app
COPY . .
RUN go mod download
RUN go mod verify
WORKDIR /usr/src/app/cmd/web
RUN make docker
FROM scratch
WORKDIR /
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /usr/src/app/cmd/web/web /web
USER appuser
ENTRYPOINT ["./web"]
CMD [":9000"]
Example for routes "/" in my program :
func (s *server) handleIndex() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
auth, _ := strconv.ParseBool(s.GetSessionValue(w, r, "authenticated"))
files := []string{
"views/public/index.html",
"views/public/nav.html",
}
templates := template.Must(template.ParseFiles(files...))
if err := templates.ExecuteTemplate(w, "index", authData{Authenticated: auth, NotAuthenticated: !auth}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
}
File Tree :
ants-map/
┣ auth/
┣ cmd/
┃ ┗ web/
┃ ┣ static/
┃ ┣ views/ .html file here
┃ ┣ Makefile
┃ ┣ main.go
┃ ┣ routes.go
┃ ┣ routes_public.go
┃ ┗ server.go
┣ database/
┣ .gitignore
┣ Dockerfile
┣ README.md
┣ go.mod
┗ go.sum
Upvotes: 2
Views: 2657
Reputation: 656
As others have pointed out, the HTML files are not being copied over to the final image. You could just add this copy command and be done but then you are still managing the executable and potentially lots of other files or even several directories of several files! There is a better way.
When building a Go executable only the actual Go code itself and its dependencies are included. Other static files such as HTML are not included in a Go binary. However, as of Go 1.16, there is now a solution for this, which is Go Embed.
The modern way to handle static files is to embed them into your Go program. For example, if you have a Go file in the same directory as another directory of HTML files, then you could embed the whole HTML directory like this:
//go:embed html/*
var htmlFS embed.FS
Then, when you actually need to use a file you can look it up by name:
htmlBytes, err := htmlFS.ReadFile("html/index.html")
This will give you the contents of that file as bytes. Then, you can just send this file as bytes in your response.
Embedding not only gives you that experience of a true one-file program but it is also more performant since the file's contents are in memory versus having to read the file into memory each time you want to send it.
UPDATE: In the OP's example above, instead of template.ParseFiles
, with a file system you can use template.ParseFS and pass the embed.FS
object in there as the extra first argument. Please don't read all your template files directly from the OS on every API call.
Upvotes: 1
Reputation: 2400
You need to add the static and views directory to your final image, for that add the following 2 line before USER appuser
:
COPY --from=builder /usr/src/app/cmd/web/views /web/views
COPY --from-builder /usr/src/app/cmd/web/static /web/static
Upvotes: 1