Go app (in docker container) not reflecting changes on page? - go

I'm new to Go, but having an annoying issue where changes in the code are not reflected on the page, unless I do another --build when I bring up the container. Is this normal? I'm running`Windows 10, Go 1.19, AMD, Docker Desktop/Compose.
If I change "Hello, World!" to some other string, CTRL+C the running app, and then run docker-compose up, the changes are NOT reflected on the page, even after clearing browser cache and using an incognito window. HOWEVER, if I run docker-compose up --build, the changes WILL be reflected.
Reminder I'm new to Go, but is this normal behaviour? Do I have to re-build the project in docker-compose each time to see the changes? Or do you see anything "off" in my code? I'm following a few year old Udemy course, so of course every step there's a new "thing" to troubleshoot as it doesn't work as shown eye roll
They suggest using Air for hot-reloading, which I'm also having an issue with as IT'S not working either, however I've opened a GitHub issue for that.
Here is the code from the various files:
main.go
package main
import (
"ambassador/src/database"
"github.com/gofiber/fiber/v2"
)
func main() {
// Connect to the database
database.Connect()
// Migrate tables in the database
database.AutoMigrate()
// Create a new fiber app, which is based on Express.js
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
app.Listen(":3000")
}
Dockerfile
FROM golang:1.19
WORKDIR /app
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . .
# Use air for live go hot-reloading
# This one doesn't work, use go install instead
# RUN curl -sSfL https://raw.githubusercontent.com/cosmtrek/air/master/install.sh | sh -s -- -b $(go env GOPATH)/bin
# Air does not work for me. Opening github issue. Skip for now
# RUN go install github.com/cosmtrek/air#latest
# CMD ["air"]
CMD ["go", "run", "main.go"]
docker-compose.yaml
version: '3.9'
services:
backend:
build: .
ports:
- 8000:3000
# volumes:
# - .:/app
depends_on:
- db
db:
image: mysql:5.7.22
restart: always
environment:
MYSQL_DATABASE: ambassador
MYSQL_USER: root
MYSQL_PASSWORD: root
MYSQL_ROOT_PASSWORD: root
volumes:
- .dbdata:/var/lib/mysql
ports:
- 33066:3306
src > database > db.go
package database
import (
"ambassador/src/models"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var DB *gorm.DB
func Connect() {
var err error
DB, err = gorm.Open(mysql.Open("root:root#tcp(db:3306)/ambassador"), &gorm.Config{})
if err != nil {
panic("Could not connect with the database!")
}
}
func AutoMigrate() {
DB.AutoMigrate(models.User{})
}
src > models > user.go
package models
type User struct {
Id uint
FirstName string
LastName string
Email string
Password string
IsAmbassador bool
}
go.mod
module ambassador
go 1.19
require github.com/gofiber/fiber/v2 v2.36.0
require (
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/klauspost/compress v1.15.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.38.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
gorm.io/driver/mysql v1.3.5 // indirect
gorm.io/gorm v1.23.8 // indirect
)
The same code is included in this screenshot of my IDE.

Go isn’t a script language and needs in rebuild and restart application to apply changes
You can use Golang fresh for rebuild and restart your app
https://github.com/gravityblast/fresh
Add this to your Dockerfile
RUN go install github.com/pilu/fresh#latest
...
CMD [ "fresh" ]

You are not mounting any files into your container, only copying them once on image build. This is why you are not seeing anychanges unless you build, or copy new files into the container.
You've already commented out a volume from your docker-compose.yaml, but if you uncomment those lines you should see that changes are reflected without rebuilding.

To answer your original question — NO, you DO NOT need to add the --build tag when running up your docker-compose file. It doesn't relate to Go, it relates only to docker containers working logic.
If we come to the live-reload problem then the problem is with the technology docker uses for file sharing between a host system and containers.
I have had the same issues on mac when tried to use not the Docker Desktop but an alternative like Rancher which even uses docker CLI via moby. When I switched back to the original Docker Desktop which uses gRPC FUSE, osxfs, and VirtioFS — with all of them worked like a charm.
I don't know how this feature is implemented on windows, but I'm sure you could dig into this direction.

Related

Issue serving Swagger file in a containerised go app

I'm currently writing an app in go. I've never done this in go before so please be patient. I'm trying to happily serve my swagger doc via docker.
I'm using redoc/redocly but when I containerise my app, I can no longer access the endpoint I've configured to serve the swagger doc.
Here is the code regarding swagger I have within my app:
sm := mux.NewRouter()
opts := middleware.RedocOpts{SpecURL: "/swagger.yaml"}
sh := middleware.Redoc(opts, nil)
sm.Handle("/docs", sh)
sm.Handle("/swagger.yaml", http.FileServer(http.Dir("./")))
s := &http.Server{
Addr: ":8080", // configure the bind address
Handler: sm, // set the default handler
ReadTimeout: 1 * time.Second, // max read time out for client
WriteTimeout: 1 * time.Second, // max time to write response to client
IdleTimeout: 120 * time.Second, // max time for connections using TCP keep-alive
}
go func() {
err := s.ListenAndServe()
if err != nil {
os.Exit(1)
}
}()
Apparently I can serve static files using in-built fileserver in go-lang that I've tried to do.
The above works as intended when I run the app from within VS Code.
However when I containerize the code and run the docker container, I don't see the swagger doc served in the nice UI when I navigate to http://localhost:8080/docs.
When I check network tools in my web browser e.g Chrome it shows the following error:
Error: Failed to load http://localhost:8080/swagger.yaml: 404 Not Found
Can someone please tell me what I'm doing wrong?
Do I need to do make modifications to the code or the Dockerfile so I can get redocly to serve the file.
UPDATE
I'm sharing my Dockerfile
FROM golang:1.16-alpine AS builder
# Set necessary environmet variables needed for our image
ENV GO111MODULE=on \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64
# Move to working directory /build
WORKDIR /build
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . .
# Build the application
RUN go build -o main .
WORKDIR /dist
# Copy binary from build to main folder
RUN cp /build/main .
# Build a small image
FROM scratch
COPY --from=builder /dist/main /
EXPOSE 8080
# Command to run when starting the container
ENTRYPOINT ["/main"]

pubsub.NewClient is stuck in development machine as well as in docker

I understand, a similar issue has been asked already here
However suggested solution doesn't work for me.
Below is my code snippet
ctx := context.Background()
client, err := pubsub.NewClient(ctx, projectID)
if err != nil {
return fmt.Errorf("pubsub.NewClient: %v", err)
}
fmt.Printf("pubsub newclient called")
defer client.Close()
However it get stuck in NewClient call and doesn't even return any error also.
Setting GRPC_GO_LOG_SEVERITY_LEVEL to info shows below information.
INFO: 2019/12/07 07:46:14 parsed scheme: ""
INFO: 2019/12/07 07:46:14 scheme "" not registered, fallback to default scheme
INFO: 2019/12/07 07:46:14 ccResolverWrapper: sending update to cc: {[{pubsub.googleapis.com:443 0 <nil>}] }
INFO: 2019/12/07 07:46:14 balancerWrapper: got update addr from Notify: [{pubsub.googleapis.com:443 0} {pubsub.googleapis.com:443 1} {pubsub.googleapis.com:443 2} {pubsub.googleapis.com:443 3}]
I am running this program in ubuntu 16.04 and it contains proper ssl certs.
This same code doesn't run in docker too.
Below is my docker file.
FROM golang:1.12 as builder
RUN mkdir /app
ADD main.go /app/
COPY go.mod go.sum /app/
WORKDIR /app
RUN go mod download
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/main .
RUN ls -l
CMD ["./main"]
Can somebody suggest how to debug this issue?
Do you have any proxy settings or something that could prevent to do the call to google? Might have something to do with this https://github.com/googleapis/google-cloud-go/issues/1202

How can I deploy beego app to Heroku

I'm creating a web app in beego that I need to have running in Heroku.
I can get it running normally if I just specify the binary for the app in the Procfile. But I want to have swagger available in the app, so I need to use bee to start the app like:
bee run -downdoc=true -gendoc=true
so that it automatically creates and downloads all the swagger related icons, html, etc.
But, if I specify that as the command in Procfile (even after adding bee to vendor so that it is available), it fails because the app doesn't have the go command available in runtime. The exact error is this:
0001 There was an error running 'go version' command: exec: "go": executable file not found in $PATH
How can I bypass this without adding the whole swagger specification to heroku (and github, since it is a repository)?
You can not run bee command on heroku, because it is a executable program.
But you can run beego app on heroku with adding project dependencies. In order to that you should use tools like https://github.com/kardianos/govendor.
1. After install govendor try following steps in your project folder;
$ govendor init
This command will create ./vendor/vendor.json file in current directory.
{
"comment": "https://github.com/ismailakbudak/beego-api-example",
"heroku": {
"install" : [ "./..." ],
"goVersion": "go1.11"
},
"ignore": "test",
"package": [],
"rootPath": "reporting-api"
}
Add heroku tag like above example. There is a information about this configuration on heroku docs in here https://devcenter.heroku.com/articles/go-dependencies-via-govendor#build-configuration
2. After this add beego package dependency with this command
$ govendor fetch github.com/astaxie/beego
It will download the beego packages to ./vendor directory
3. Lastly you should configure listen ports and beego runmode as prod for heroku in main.go file
To deploy your app without problem, default config must be runmode = prod in conf/app.conf. I tried to set it default as dev and change it from heroku config vars as below, but it compiles packages before the set runmode and gives exception with this message panic: you are running in dev mode.
func main() {
log.Println("Env $PORT :", os.Getenv("PORT"))
if os.Getenv("PORT") != "" {
port, err := strconv.Atoi(os.Getenv("PORT"))
if err != nil {
log.Fatal(err)
log.Fatal("$PORT must be set")
}
log.Println("port : ", port)
beego.BConfig.Listen.HTTPPort = port
beego.BConfig.Listen.HTTPSPort = port
}
if os.Getenv("BEEGO_ENV") != "" {
log.Println("Env $BEEGO_ENV :", os.Getenv("BEEGO_ENV"))
beego.BConfig.RunMode = os.Getenv("BEEGO_ENV")
}
beego.BConfig.WebConfig.DirectoryIndex = true
beego.BConfig.WebConfig.StaticDir["/"] = "swagger"
beego.Run()
}
You can use BEEGO_ENV=dev bee run to continue develop your app without change it again

Docker-Compose: Composing with Dockerfiles that need relative imports

To begin, this is my project hierarchy:
myproj/
- commons1/
- com1_file1.go
- ...
- commons2/
- com2_file1.go
- ...
- module1/
- mod1_file1.go
- Dockerfile
- ...
- module2/
- mod2_file1.go
- Dockerfile
- ...
- docker-compose.yml
What I'd like to do is that when module1 and module2 containers start up, they each have a copy of all the commonsN directories in their GOPATH's so that each can access the common libraries exposed by each of the commonsN directories.
For example, I would like to see something like this in the container for module1:
/go/
- src/
- commons1/
- com1_file1.go
- ...
- commons2/
- com2_file1.go
- ...
- module1/
- mod1_file1.go
- ...
Reason being is that this is basically how my local GOPATH looks (with the addition of the other modules of course) so that I can do something like this in my source files:
package main
import(
"fmt"
"myproj/commons1"
)
func main() {
fmt.Println("Some thing from common library :", commons1.SomethingFromCommons)
}
From my naive understanding of Docker, it appears I'm not allowed to modify my Dockerfiles to do something along the lines of COPY ../commons1 /go/src/commons1, so I'm wondering how I would go about accomplishing this?
I would strongly prefer to not go the Github route since the source code is all behind company proxies and whatnot and I'm imagining configuring all that is going to take way longer than simply copying some directories.
Edit
I have updated my docker-compose.yml file to look something like this per suggestion from barat:
version: '2'
services:
module1:
volumes:
- ./commons1:/go/src/myproj/commons1
build: module1/
Dockerfile for module1 looks like this:
FROM golang:1.8.0
RUN mkdir -p /go/src/app
WORKDIR /go/src/app
COPY . /go/src/app
RUN go get -d -v
RUN go install -v
ENTRYPOINT /go/bin/app
EXPOSE 8080
docker-compose build fails on the go get -d -v with error:
package myproj/commons1: unrecognized import path "myproj/commons1" (import path does not begin with hostname)
If myproj/commons1 was copied into /go/src/, then this shouldn't be an issue right? I'm guessing then it hasn't been copied over then?
You could build an image including commons1 and commons2 that your other images are based on.
FROM golang:1.8.0
RUN mkdir -p /go/src/myproj/commons1 && mkdir -p /go/src/myproj/commons2
COPY commons1/ /go/src/myproj/commons1/
COPY commons2/ /go/src/myproj/commons2/
The downside is this requires an external build step whenever you update one of the common projects:
docker build -t me/myproj:commons .
Then your compose apps can rely on the commons image instead of golang and build as normal without the volumes.
FROM me/myproj:commons
...
So problem was the go get -d -v command since it was complaining myproj/commons1 wasn't installed in $GOPATH/src basically. This I of course suspect was because Docker Compose wasn't mounting the volumes I mentioned before it ran the go get on docker-compose build so I made a work around in my docker-compose.yml but it is far from elegant:
version: '2'
services:
module1:
volumes:
- ./commons1:/go/src/myproj/commons1
build: module1/
ports:
- "8080:8080"
command: bash -c "go get -d -v && go install -v && /go/bin/app
This is obviously far from ideal because my Go binary is rebuilt every time I do a docker-compose up regardless of whether or not I ran docker-compose build.
This is also problematic because I wanted to use dockerize for certain containers to wait until another container has started up completely and it becomes quite messy now I think.

Writing an openshift extension

Trying to understand how one builds a tool consuming the Openshift Origin and Kubernetes APIs (I'm new to Go). It seems that I have a version of Kubernetes installed from Openshift and another version installed by godep and I'm getting all sorts of exciting version mismatches.
Here's my code so far:
package main
import (
kclient "k8s.io/kubernetes/pkg/client/unversioned"
client "github.com/openshift/origin/pkg/client"
)
func main() {
config := kclient.Config{
Host: "...",
}
client.SetOpenShiftDefaults(&config)
client := client.NewOrDie(&config)
}
I then did a go get github.com/openshift/origin which I can see has a copy of Kubernetes in it's Godeps/_workspace/src directory.
However when I do a godep save I get:
godep: Package (k8s.io/kubernetes/pkg/client/unversioned) not found
If I do a go get to install Kubernetes I get a version that doesn't match with the version used by Openshift. What's the correct way to do this?

Resources