feat: add skaffold.yml (#9)
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: twhelp/core#9
This commit is contained in:
parent
602f5456bc
commit
682939a3cf
10
README.md
Normal file
10
README.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
# TWHelp
|
||||
|
||||
## Local development
|
||||
|
||||
**Prerequisites:**
|
||||
1. Kubernetes
|
||||
2. kustomize
|
||||
3. helm
|
||||
4. skaffold
|
||||
|
18
build/docker/twhelp/dev/Dockerfile
Normal file
18
build/docker/twhelp/dev/Dockerfile
Normal file
|
@ -0,0 +1,18 @@
|
|||
FROM golang:1.18 as builder
|
||||
WORKDIR /app
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY . .
|
||||
# `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
|
||||
ARG SKAFFOLD_GO_GCFLAGS
|
||||
RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o twhelp ./cmd/twhelp/main.go
|
||||
|
||||
FROM ubuntu:22.04
|
||||
# Define GOTRACEBACK to mark this container as using the Go language runtime
|
||||
# for `skaffold debug` (https://skaffold.dev/docs/workflows/debug/).
|
||||
WORKDIR /root
|
||||
ENV GOTRACEBACK=single
|
||||
RUN apt update
|
||||
RUN apt install -y ca-certificates tzdata
|
||||
COPY --from=builder /app/twhelp .
|
||||
ENTRYPOINT ["./twhelp"]
|
|
@ -21,7 +21,7 @@ const (
|
|||
defaultConnPerCpu = 10
|
||||
defaultConnLifetime = "3m"
|
||||
defaultConnIdleTime = "5m"
|
||||
maxOpenConnsDivider = 2
|
||||
maxOpenConnsDivisor = 2
|
||||
dbPingTimeout = 10 * time.Second
|
||||
dbTypePostgres = "postgres"
|
||||
)
|
||||
|
@ -34,7 +34,7 @@ func NewBunDB() (*bun.DB, error) {
|
|||
|
||||
maxIdleConnections, _ := getenvInt("DB_MAX_IDLE_CONNECTIONS")
|
||||
if maxIdleConnections == 0 {
|
||||
maxIdleConnections = maxOpenConnections / maxOpenConnsDivider
|
||||
maxIdleConnections = maxOpenConnections / maxOpenConnsDivisor
|
||||
}
|
||||
|
||||
maxConnectionLifetimeStr, _ := getenvString("DB_MAX_CONNECTION_LIFETIME")
|
||||
|
|
143
cmd/twhelp/internal/serve/serve.go
Normal file
143
cmd/twhelp/internal/serve/serve.go
Normal file
|
@ -0,0 +1,143 @@
|
|||
package serve
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"gitea.dwysokinski.me/twhelp/core/internal/rest"
|
||||
|
||||
"gitea.dwysokinski.me/twhelp/core/internal/bundb"
|
||||
"gitea.dwysokinski.me/twhelp/core/internal/service"
|
||||
|
||||
"github.com/uptrace/bun"
|
||||
|
||||
"github.com/go-chi/cors"
|
||||
|
||||
"github.com/Kichiyaki/appmode/v2"
|
||||
"github.com/Kichiyaki/chizap"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"gitea.dwysokinski.me/twhelp/core/cmd/twhelp/internal"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultPort = "9234"
|
||||
readTimeout = 2 * time.Second
|
||||
readHeaderTimeout = 2 * time.Second
|
||||
writeTimeout = 2 * time.Second
|
||||
idleTimeout = 2 * time.Second
|
||||
serverShutdownTimeout = 10 * time.Second
|
||||
corsMaxAge = 300
|
||||
)
|
||||
|
||||
func New() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "serve",
|
||||
Usage: "Runs the http server",
|
||||
Action: func(c *cli.Context) error {
|
||||
db, err := internal.NewBunDB()
|
||||
if err != nil {
|
||||
return fmt.Errorf("internal.NewBunDB: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = db.Close()
|
||||
}()
|
||||
|
||||
logger := zap.L()
|
||||
|
||||
srv, err := newServer(serverConfig{
|
||||
logger: logger,
|
||||
db: db,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("newServer: %w", err)
|
||||
}
|
||||
|
||||
go startServer(srv, logger)
|
||||
logger.Info("Server is listening on the port "+defaultPort, zap.String("port", defaultPort))
|
||||
|
||||
waitForSignal(context.Background())
|
||||
|
||||
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), serverShutdownTimeout)
|
||||
defer cancelShutdown()
|
||||
if err := srv.Shutdown(ctxShutdown); err != nil {
|
||||
return fmt.Errorf("srv.Shutdown: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type serverConfig struct {
|
||||
logger *zap.Logger
|
||||
db *bun.DB
|
||||
}
|
||||
|
||||
func newServer(cfg serverConfig) (*http.Server, error) {
|
||||
// repos
|
||||
versionRepo := bundb.NewVersion(cfg.db)
|
||||
|
||||
// services
|
||||
versionSvc := service.NewVersion(versionRepo)
|
||||
|
||||
// router
|
||||
r := chi.NewRouter()
|
||||
r.Use(getChiMiddlewares(cfg.logger)...)
|
||||
rest.NewVersion(versionSvc).Register(r)
|
||||
|
||||
return &http.Server{
|
||||
Addr: ":" + defaultPort,
|
||||
Handler: r,
|
||||
ReadTimeout: readTimeout,
|
||||
ReadHeaderTimeout: readHeaderTimeout,
|
||||
WriteTimeout: writeTimeout,
|
||||
IdleTimeout: idleTimeout,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getChiMiddlewares(logger *zap.Logger) chi.Middlewares {
|
||||
mws := chi.Middlewares{
|
||||
middleware.RealIP,
|
||||
chizap.Logger(logger),
|
||||
middleware.Recoverer,
|
||||
middleware.Heartbeat("/health"),
|
||||
}
|
||||
if appmode.Equal(appmode.DevelopmentMode) {
|
||||
mws = append(mws, newCORSMiddlewareForDevMode())
|
||||
}
|
||||
return mws
|
||||
}
|
||||
|
||||
func newCORSMiddlewareForDevMode() func(next http.Handler) http.Handler {
|
||||
return cors.Handler(cors.Options{
|
||||
AllowOriginFunc: func(*http.Request, string) bool {
|
||||
return true
|
||||
},
|
||||
AllowCredentials: true,
|
||||
ExposedHeaders: []string{"Authorization"},
|
||||
AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"},
|
||||
AllowedHeaders: []string{"Origin", "Content-Length", "Content-Type", "Authorization"},
|
||||
MaxAge: corsMaxAge,
|
||||
})
|
||||
}
|
||||
|
||||
func startServer(srv *http.Server, logger *zap.Logger) {
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
logger.Fatal("srv.ListenAndServe:" + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func waitForSignal(ctx context.Context) {
|
||||
ctx, stop := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
<-ctx.Done()
|
||||
}
|
|
@ -4,6 +4,8 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
|
||||
"gitea.dwysokinski.me/twhelp/core/cmd/twhelp/internal/serve"
|
||||
|
||||
"gitea.dwysokinski.me/twhelp/core/cmd/twhelp/internal"
|
||||
"gitea.dwysokinski.me/twhelp/core/cmd/twhelp/internal/db"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
@ -38,6 +40,7 @@ func newApp() *cli.App {
|
|||
app.EnableBashCompletion = true
|
||||
app.Commands = []*cli.Command{
|
||||
db.New(),
|
||||
serve.New(),
|
||||
}
|
||||
return app
|
||||
}
|
||||
|
|
6
go.mod
6
go.mod
|
@ -4,8 +4,10 @@ go 1.18
|
|||
|
||||
require (
|
||||
github.com/Kichiyaki/appmode/v2 v2.0.1
|
||||
github.com/Kichiyaki/chizap v0.1.1
|
||||
github.com/elliotchance/phpserialize v1.3.3
|
||||
github.com/go-chi/chi/v5 v5.0.7
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/google/go-cmp v0.5.5
|
||||
github.com/ory/dockertest/v3 v3.9.1
|
||||
github.com/stretchr/testify v1.8.0
|
||||
|
@ -48,8 +50,8 @@ require (
|
|||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.7.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
|
||||
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d // indirect
|
||||
|
|
10
go.sum
10
go.sum
|
@ -3,6 +3,8 @@ github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX
|
|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Kichiyaki/appmode/v2 v2.0.1 h1:KUjxgSwlWUfV6n9HFCsBZNdlLOuGCPF26eXJphLS9is=
|
||||
github.com/Kichiyaki/appmode/v2 v2.0.1/go.mod h1:zO9n0/t5BApADvRu4+GpK8rWBatXyjtObQuvXGLSqKk=
|
||||
github.com/Kichiyaki/chizap v0.1.1 h1:8GacY8tPwoYMdSqwzy9IfPXHgbwzK3RnORqAA8FKQEc=
|
||||
github.com/Kichiyaki/chizap v0.1.1/go.mod h1:PJpJQVJyJmtDAKPwT7rdWuscezD2T1hnU/Yj25PLVSs=
|
||||
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
|
||||
|
@ -39,6 +41,8 @@ github.com/elliotchance/phpserialize v1.3.3/go.mod h1:gt7XX9+ETUcLXbtTKEuyrqW3lc
|
|||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||
github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
|
||||
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
||||
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
|
@ -133,12 +137,14 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsr
|
|||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
|
||||
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
|
|
54
k8s/base/api.yml
Normal file
54
k8s/base/api.yml
Normal file
|
@ -0,0 +1,54 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: twhelp-api-deployment
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: twhelp-api
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: twhelp-api
|
||||
spec:
|
||||
containers:
|
||||
- name: twhelp-api
|
||||
image: twhelp
|
||||
args: ["serve"]
|
||||
ports:
|
||||
- name: container-port
|
||||
containerPort: 9234
|
||||
env:
|
||||
- name: APP_MODE
|
||||
value: development
|
||||
- name: DB_DSN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: twhelp-secret
|
||||
key: db-dsn
|
||||
- name: DB_MAX_OPEN_CONNECTIONS
|
||||
value: "10"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: container-port
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: container-port
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: twhelp-api-service
|
||||
spec:
|
||||
ports:
|
||||
- port: 9234
|
||||
targetPort: 9234
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: twhelp-api
|
9
k8s/base/kustomization.yml
Normal file
9
k8s/base/kustomization.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- migrations.yml
|
||||
- api.yml
|
||||
images:
|
||||
- name: twhelp
|
||||
newName: twhelp
|
||||
newTag: latest
|
20
k8s/base/migrations.yml
Normal file
20
k8s/base/migrations.yml
Normal file
|
@ -0,0 +1,20 @@
|
|||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: twhelp-migrations-job
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: twhelp-migrations
|
||||
image: twhelp
|
||||
args: ["db", "migrate"]
|
||||
env:
|
||||
- name: DB_MAX_OPEN_CONNECTIONS
|
||||
value: "1"
|
||||
- name: DB_DSN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: twhelp-secret
|
||||
key: db-dsn
|
||||
restartPolicy: Never
|
6
k8s/overlays/dev/kustomization.yml
Normal file
6
k8s/overlays/dev/kustomization.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
nameSuffix: -dev
|
||||
resources:
|
||||
- secret.yml
|
||||
- ../../base
|
8
k8s/overlays/dev/secret.yml
Normal file
8
k8s/overlays/dev/secret.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: twhelp-secret
|
||||
type: Opaque
|
||||
data:
|
||||
# postgres://twhelp:twhelp@twhelpdb-postgresql:5432/twhelp?sslmode=disable
|
||||
db-dsn: cG9zdGdyZXM6Ly90d2hlbHA6dHdoZWxwQHR3aGVscGRiLXBvc3RncmVzcWw6NTQzMi90d2hlbHA/c3NsbW9kZT1kaXNhYmxl
|
26
skaffold.yml
Normal file
26
skaffold.yml
Normal file
|
@ -0,0 +1,26 @@
|
|||
apiVersion: skaffold/v2beta27
|
||||
kind: Config
|
||||
build:
|
||||
tagPolicy:
|
||||
customTemplate:
|
||||
template: latest
|
||||
artifacts:
|
||||
- image: twhelp
|
||||
context: .
|
||||
docker:
|
||||
dockerfile: ./build/docker/twhelp/dev/Dockerfile
|
||||
deploy:
|
||||
helm:
|
||||
releases:
|
||||
- name: twhelpdb
|
||||
repo: https://charts.bitnami.com/bitnami
|
||||
remoteChart: postgresql
|
||||
version: 11.6.19
|
||||
wait: true
|
||||
setValues:
|
||||
auth.username: twhelp
|
||||
auth.password: twhelp
|
||||
auth.database: twhelp
|
||||
kustomize:
|
||||
paths:
|
||||
- k8s/overlays/dev
|
Reference in New Issue
Block a user