Skip to content

Commit

Permalink
added api structure, container and health endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
aorticweb committed Jan 27, 2022
1 parent 386fbdd commit 1162f72
Show file tree
Hide file tree
Showing 9 changed files with 441 additions and 2 deletions.
36 changes: 36 additions & 0 deletions .air.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"

[build]
bin = "./tmp/main"
cmd = "go build -o ./tmp/main ./app/cmd"
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
kill_delay = "0s"
log = "build-errors.log"
send_interrupt = false
stop_on_error = true

[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"

[log]
time = false

[misc]
clean_on_exit = false

[screen]
clear_on_rebuild = false
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ create-migration:

init-db:
docker-compose run migration
docker-compose up api
117 changes: 117 additions & 0 deletions app/cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package main

import (
"context"
"errors"
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"time"

api "github.com/aorticweb/msg-app/app/handlers"
"github.com/sirupsen/logrus"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)

var dbConnectionWaitTime time.Duration = 5 * time.Minute
var shutdownTimeout time.Duration = 3 * time.Second
var serverListenAddr string = ":8080"

func dbConnection(url string) (*gorm.DB, error) {
return gorm.Open(postgres.Open(url), &gorm.Config{})
}

func dbUrl() (string, error) {
url, exist := os.LookupEnv("POSTGRES_URL")
if !exist {
return "", errors.New("Env varialbe POSTGRES_URL is not set")
}
return url, nil
}

// waitForDB ... wait up to timeout for database to be up then return connection
func waitForDB(timeout time.Duration) (*gorm.DB, error) {
url, err := dbUrl()
if err != nil {
return nil, err
}
db, err := dbConnection(url)
for start := time.Now(); time.Since(start) < timeout; {
if err == nil {
break
}
db, err = dbConnection(url)
time.Sleep(1 * time.Second)
}
return db, err
}

func registerKillSwitch() chan os.Signal {
shutdown := make(chan os.Signal, 1)
signal.Notify(shutdown, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
return shutdown
}

func gracefullyShutdown(server *http.Server) error {
ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
defer cancel()

err := server.Shutdown(ctx)
if err == nil {
return nil
}
logrus.Error("Server shutdown did not succeed in %v: %v", shutdownTimeout, err)
err = server.Close()

if err != nil {
return fmt.Errorf("Server close failed: %v", err)
}
logrus.Info("Bye Bye")
return nil
}

func waitForKillSwitch(kill chan os.Signal, server *http.Server) {
<-kill
gracefullyShutdown(server)
}

func setupServer(logger *logrus.Logger) (*http.Server, error) {
db, err := waitForDB(dbConnectionWaitTime)
if err != nil {
logger.Error("Failed to fetch database connection")
return nil, err
}
killSwitch := registerKillSwitch()
API := api.NewAPI(db, logger)
server := http.Server{
Addr: serverListenAddr,
Handler: API,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
go waitForKillSwitch(killSwitch, &server)
return &server, nil
}

func must(err error, logger *logrus.Logger) {
if err != nil {
logger.Fatal(err)
os.Exit(1)
}
}

func main() {
logger := logrus.New()
server, err := setupServer(logger)
must(err, logger)
logger.Info("API says Hello\n")
err = server.ListenAndServe()
if err == http.ErrServerClosed {
logger.Info("API says Goodbye\n")
os.Exit(0)
}
must(err, logger)
}
19 changes: 19 additions & 0 deletions app/handlers/health.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package api

import (
"net/http"
)

func (a *API) handleHealth() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var c int
a.db.Raw("SELECT COUNT(table_name) FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'user';").Scan(&c)
if c != 1 {
a.logger.Error("Health check failed, could not reach database")
w.WriteHeader(http.StatusExpectationFailed)
return
}
w.WriteHeader(http.StatusOK)
return
}
}
29 changes: 29 additions & 0 deletions app/handlers/routes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package api

import (
"net/http"

"github.com/gorilla/mux"
"github.com/sirupsen/logrus"
"gorm.io/gorm"
)

type API struct {
db *gorm.DB
router *mux.Router
logger *logrus.Logger
}

func (a *API) ServeHTTP(w http.ResponseWriter, r *http.Request) {
a.router.ServeHTTP(w, r)
}

func NewAPI(db *gorm.DB, logger *logrus.Logger) *API {
a := &API{db, mux.NewRouter(), logger}
a.routes()
return a
}

func (a *API) routes() {
a.router.HandleFunc("/health", a.handleHealth()).Methods("GET")
}
21 changes: 21 additions & 0 deletions containers/api/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
FROM golang:latest as dev

EXPOSE 8080

COPY . /msg-app
WORKDIR /msg-app

RUN curl -sSfL https://raw.githubusercontent.com/cosmtrek/air/master/install.sh | sh -s -- -b $(go env GOPATH)/bin
RUN cd app/cmd/ && CGO_ENABLED=0 GOOS=linux go build -v -o api

CMD ["air"]

FROM scratch as prod

# FROM ubuntu:bionic as prod

EXPOSE 8080

COPY --from=dev /msg-app/app/cmd/api /bin/api

CMD ["/bin/api"]
15 changes: 15 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@
version: "3.9"
services:

api:
image: aorticweb/msg-app-api
build:
context: .
dockerfile: containers/api/Dockerfile
target: dev
environment:
POSTGRES_URL: "postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=disable"
volumes:
- ./:/msg-app:rw
depends_on:
- migration
ports:
- 8080:8080

migration:
image: aorticweb/msg-app-migration
build:
Expand Down
22 changes: 20 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
module github.com/aorticweb/msg-app.git
module github.com/aorticweb/msg-app

go 1.17

require github.com/gorilla/mux v1.8.0 // indirect
require (
github.com/gorilla/mux v1.8.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.10.1 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.2.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.9.0 // indirect
github.com/jackc/pgx/v4 v4.14.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.4 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
golang.org/x/text v0.3.7 // indirect
gorm.io/driver/postgres v1.2.3 // indirect
gorm.io/gorm v1.22.5 // indirect
)
Loading

0 comments on commit 1162f72

Please sign in to comment.