uPong26
uPong26

Reputation: 45

Dockerfile : no such file or directory

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

Answers (2)

GoForth
GoForth

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

jmaitrehenry
jmaitrehenry

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

Related Questions