Issue serving Swagger file in a containerised go app - go

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"]

Related

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

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.

How to read env files by runnnig Go application?

I have an application which is developed in Go. I have a config.env file and get some critical variables from it by using the godotenv library. Here is the code:
func InitializeEnvVars() error {
err := godotenv.Load("./config.env")
return err
}
When I build my project with go build . on MacOS and I want to run the application, the app gives an error about reading the .env file:
2021/03/07 17:42:21 [ERROR]: Error loading .env file
But when I run my app with go run main.go command, everything works well.
How can I solve this problem?
As per the comments godotenv.Load("./config.env") will attempt to load the .env file from the working directory. The method used to set the working directory depends upon how you are starting the application (i.e. command line/gui).
If you would prefer that the .env be loaded from the folder holding the executable then try the following (note that there are some caveats).
ex, err := os.Executable()
if err != nil {
panic(fmt.Sprintf("Failed to get executable path: %s", err))
}
envPath := filepath.Join(filepath.Dir(ex), ".env")
err = godotenv.Load(envPath)
You can find the solution below step by step:
Create a folder like cmd
Move executable file to the folder
Create env file with written named in the code into this folder
Open terminal (zsh - MacOS) and run this command: open <executableFileName>
Output file should be in separated folder.

Flutter web with Golang Server

Is it possible to run flutter web build using golang server? Golang has facility to serve html file and flutter web gives output as index.html and js files.
if it is possible then how golang code should look like?
as the friendly doc mentions it, i believe you got to build your app.
https://flutter.dev/docs/get-started/web#build
Run the following command to generate a release build:
flutter build web
This populates a build/web directory with built files, including an assets directory, which need to be served together.
how golang code should look like?
like any other regular HTTP golang server.
http.Handle("/build/web/", http.StripPrefix("/build/web/", http.FileServer(http.Dir("build/web"))))
http.ListenAndServe(":8080", nil)
package main
import (
"flag"
"log"
"net/http"
)
func main() {
port := flag.String("p", "8181", "port to serve on")
directory := flag.String("d", "web", "the directory of static file to host")
flag.Parse()
http.Handle("/", http.FileServer(http.Dir(*directory)))
log.Printf("Serving %s on HTTP port: %s\n", *directory, *port)
log.Fatal(http.ListenAndServe(":"+*port, nil))
}
Just copy web folder to your's go app.

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

Resources