Skip to content

Commit 3cb79d6

Browse files
authored
Merge pull request #6 from Hyperkid123/setup-postgres
Setup postgres connection via GORM
2 parents dd5c650 + 3bc1599 commit 3cb79d6

File tree

12 files changed

+217
-14
lines changed

12 files changed

+217
-14
lines changed

.env.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
# Copy this file to .env and fill in values as needed, this file contains default env values
22

33
LOG_LEVEL=debug
4+
5+
PGSQL_USER=widget-layout
6+
PGSQL_PASSWORD=widget-layout
7+
PGSQL_HOSTNAME=0.0.0.0
8+
PGSQL_PORT=5432
9+
PGSQL_DATABASE=widget-layout

Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
FROM registry.access.redhat.com/ubi9/go-toolset:9.6-1749636489 AS builder
22
COPY api api
33
COPY pkg pkg
4+
COPY cmd cmd
45
COPY go.mod go.mod
56
COPY go.sum go.sum
67
COPY main.go main.go
@@ -12,6 +13,7 @@ USER root
1213
RUN go get -d -v
1314
RUN make generate
1415
RUN CGO_ENABLED=1 go build -o /go/bin/widget-layout-backend
16+
RUN CGO_ENABLED=1 go build -o /go/bin/widget-layout-backend-migrate cmd/database/migrate.go
1517

1618
FROM registry.access.redhat.com/ubi9-minimal:latest
1719

@@ -25,6 +27,7 @@ RUN mkdir -p /app/spec
2527
RUN chgrp -R 0 /app/spec && \
2628
chmod -R g=u /app/spec
2729
COPY --from=builder /go/bin/widget-layout-backend /app/widget-layout-backend
30+
COPY --from=builder /go/bin/widget-layout-backend-migrate /usr/bin
2831
# Spec is used for request payload validation
2932
COPY spec/openapi.yaml /app/spec/openapi.yaml
3033

Makefile

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
.PHONY: build dev generate
1+
.PHONY: build dev generate test infra migrate-db help
22

33
help:
44
@echo "Available commands:"
5-
@echo " build Build the widget-layout-backend binary"
6-
@echo " dev Run the application in development mode"
7-
@echo " generate Run go generate on all packages"
8-
@echo " test Run all unit tests with coverage"
9-
@echo " help Show this help message"
5+
@echo " build Build the widget-layout-backend binary"
6+
@echo " dev Run the application in development mode"
7+
@echo " generate Run go generate on all packages"
8+
@echo " test Run all unit tests with coverage"
9+
@echo " infra Start local infrastructure with Docker Compose"
10+
@echo " migrate-db Run the database migration script"
11+
@echo " help Show this help message"
1012

1113
build:
1214
go build -o bin/widget-layout-backend .
@@ -19,4 +21,10 @@ generate:
1921

2022
test:
2123
go test -coverprofile=coverage.out ./... $(ARGS)
22-
@go tool cover -html=coverage.out -o coverage.html
24+
@go tool cover -html=coverage.out -o coverage.html
25+
26+
infra:
27+
docker-compose --env-file .env -f local/database-compose.yaml up
28+
29+
migrate-db:
30+
go run cmd/database/migrate.go

cmd/database/migrate.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package main
2+
3+
import (
4+
"github.com/RedHatInsights/widget-layout-backend/pkg/database"
5+
"github.com/RedHatInsights/widget-layout-backend/pkg/models"
6+
"github.com/joho/godotenv"
7+
"github.com/sirupsen/logrus"
8+
"gorm.io/gorm"
9+
"gorm.io/gorm/logger"
10+
)
11+
12+
func main() {
13+
godotenv.Load()
14+
15+
// migrate models
16+
tx := database.DB.Begin().Session(&gorm.Session{
17+
Logger: logger.Default.LogMode(logger.Info),
18+
})
19+
// rollback changes
20+
defer func() {
21+
if r := recover(); r != nil {
22+
logrus.Errorln("Migration failed:", r)
23+
tx.Rollback()
24+
}
25+
}()
26+
27+
// migrate the base models
28+
if err := tx.AutoMigrate(
29+
&models.DashboardTemplate{},
30+
); err != nil {
31+
logrus.Errorln("Failed to migrate models:", err.Error())
32+
tx.Rollback()
33+
return
34+
}
35+
36+
err := tx.Commit().Error
37+
if err != nil {
38+
logrus.Errorln("Failed to commit migration transaction:", err.Error())
39+
return
40+
}
41+
logrus.Infoln("Database migration completed successfully")
42+
}

deploy/clowdapp.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ objects:
2424
- /api/widget-layout/v1/openapi.yaml
2525
podSpec:
2626
image: ${IMAGE}:${IMAGE_TAG}
27+
initContainers:
28+
- name: run-db-migration
29+
command:
30+
- bash
31+
- -c
32+
- widget-layout-backend-migrate
2733
livenessProbe:
2834
failureThreshold: 3
2935
httpGet:

go.mod

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ require (
88
github.com/joho/godotenv v1.5.1
99
github.com/oapi-codegen/nethttp-middleware v1.1.2
1010
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1
11-
github.com/oapi-codegen/runtime v1.1.1
11+
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037
1212
github.com/prometheus/client_golang v1.22.0
1313
github.com/redhatinsights/app-common-go v1.6.8
1414
github.com/sirupsen/logrus v1.8.1
1515
github.com/stretchr/testify v1.10.0
16+
gorm.io/driver/postgres v1.6.0
17+
gorm.io/gorm v1.30.0
1618
)
1719

1820
require (
@@ -23,11 +25,16 @@ require (
2325
github.com/go-openapi/jsonpointer v0.21.0 // indirect
2426
github.com/go-openapi/swag v0.23.0 // indirect
2527
github.com/gorilla/mux v1.8.1 // indirect
28+
github.com/jackc/pgpassfile v1.0.0 // indirect
29+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
30+
github.com/jackc/pgx/v5 v5.6.0 // indirect
31+
github.com/jackc/puddle/v2 v2.2.2 // indirect
32+
github.com/jinzhu/inflection v1.0.0 // indirect
33+
github.com/jinzhu/now v1.1.5 // indirect
2634
github.com/josharian/intern v1.0.0 // indirect
2735
github.com/mailru/easyjson v0.7.7 // indirect
2836
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
2937
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
30-
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
3138
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
3239
github.com/perimeterx/marshmallow v1.1.5 // indirect
3340
github.com/pmezard/go-difflib v1.0.0 // indirect
@@ -36,7 +43,9 @@ require (
3643
github.com/prometheus/procfs v0.15.1 // indirect
3744
github.com/speakeasy-api/openapi-overlay v0.9.0 // indirect
3845
github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect
46+
golang.org/x/crypto v0.31.0 // indirect
3947
golang.org/x/mod v0.17.0 // indirect
48+
golang.org/x/sync v0.10.0 // indirect
4049
golang.org/x/sys v0.30.0 // indirect
4150
golang.org/x/text v0.21.0 // indirect
4251
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect

go.sum

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
4545
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
4646
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
4747
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
48+
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
49+
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
50+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
51+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
52+
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
53+
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
54+
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
55+
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
56+
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
57+
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
58+
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
59+
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
4860
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
4961
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
5062
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -73,8 +85,6 @@ github.com/oapi-codegen/nethttp-middleware v1.1.2 h1:TQwEU3WM6ifc7ObBEtiJgbRPaCe
7385
github.com/oapi-codegen/nethttp-middleware v1.1.2/go.mod h1:5qzjxMSiI8HjLljiOEjvs4RdrWyMPKnExeFS2kr8om4=
7486
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1 h1:ykgG34472DWey7TSjd8vIfNykXgjOgYJZoQbKfEeY/Q=
7587
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1/go.mod h1:N5+lY1tiTDV3V1BeHtOxeWXHoPVeApvsvjJqegfoaz8=
76-
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
77-
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
7888
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
7989
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
8090
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
@@ -116,8 +126,10 @@ github.com/speakeasy-api/openapi-overlay v0.9.0 h1:Wrz6NO02cNlLzx1fB093lBlYxSI54
116126
github.com/speakeasy-api/openapi-overlay v0.9.0/go.mod h1:f5FloQrHA7MsxYg9djzMD5h6dxrHjVVByWKh7an8TRc=
117127
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
118128
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
129+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
119130
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
120131
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
132+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
121133
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
122134
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
123135
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
@@ -128,6 +140,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
128140
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
129141
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
130142
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
143+
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
144+
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
131145
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
132146
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
133147
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
@@ -206,3 +220,7 @@ gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C
206220
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
207221
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
208222
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
223+
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
224+
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
225+
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
226+
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=

local/database-compose.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
services:
3+
widget-layout-db:
4+
image: quay.io/sclorg/postgresql-16-c9s:latest
5+
restart: always
6+
environment:
7+
- POSTGRESQL_USER=${PGSQL_USER}
8+
- POSTGRESQL_PASSWORD=${PGSQL_PASSWORD}
9+
- POSTGRESQL_DATABASE=${PGSQL_DATABASE}
10+
ports:
11+
- "${PGSQL_PORT}:5432"
12+
volumes:
13+
- widget-layout:/var/lib/postgresql/data
14+
15+
volumes:
16+
widget-layout:
17+
driver: local

pkg/config/config.go

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,52 @@
11
package config
22

33
import (
4+
"fmt"
45
"os"
6+
"strconv"
57

68
"github.com/joho/godotenv"
79
clowder "github.com/redhatinsights/app-common-go/pkg/api/v1"
810
)
911

12+
type DatabaseConfig struct {
13+
DBHost string
14+
DBUser string
15+
DBPassword string
16+
DBPort int
17+
DBName string
18+
DBSSLMode string
19+
DBSSLRootCert string
20+
DBDNS string
21+
}
22+
1023
type WidgetLayoutConfig struct {
11-
LogLevel string
12-
WebPort int
13-
MetricsPort int
24+
LogLevel string
25+
WebPort int
26+
MetricsPort int
27+
DatabaseConfig DatabaseConfig
1428
}
1529

1630
var config *WidgetLayoutConfig
1731

32+
// The location of certificates is dictated by Clowder
33+
const RdsCaLocation = "/app/rdsca.cert"
34+
35+
func (c *WidgetLayoutConfig) getCert(cfg *clowder.AppConfig) string {
36+
cert := ""
37+
if cfg.Database.SslMode != "verify-full" {
38+
return cert
39+
}
40+
if cfg.Database.RdsCa != nil {
41+
err := os.WriteFile(RdsCaLocation, []byte(*cfg.Database.RdsCa), 0644)
42+
if err != nil {
43+
panic(err)
44+
}
45+
cert = RdsCaLocation
46+
}
47+
return cert
48+
}
49+
1850
func init() {
1951
godotenv.Load()
2052
config = &WidgetLayoutConfig{}
@@ -28,9 +60,30 @@ func init() {
2860
cfg := clowder.LoadedConfig
2961
config.WebPort = *cfg.PublicPort
3062
config.MetricsPort = cfg.MetricsPort
63+
config.DatabaseConfig = DatabaseConfig{
64+
DBHost: cfg.Database.Hostname,
65+
DBUser: cfg.Database.Username,
66+
DBPassword: cfg.Database.Password,
67+
DBPort: cfg.Database.Port,
68+
DBName: cfg.Database.Name,
69+
DBSSLMode: cfg.Database.SslMode,
70+
DBSSLRootCert: config.getCert(cfg),
71+
}
72+
config.DatabaseConfig.DBDNS = fmt.Sprintf("host=%v user=%v password=%v dbname=%v port=%v sslmode=%v", cfg.Database.Hostname, cfg.Database.Username, cfg.Database.Password, cfg.Database.Name, cfg.Database.Port, cfg.Database.SslMode)
3173
} else {
3274
config.WebPort = 8000
3375
config.MetricsPort = 9000
76+
77+
// Make sure the .env file is loaded and has correct values!
78+
config.DatabaseConfig.DBUser = os.Getenv("PGSQL_USER")
79+
config.DatabaseConfig.DBPassword = os.Getenv("PGSQL_PASSWORD")
80+
config.DatabaseConfig.DBHost = os.Getenv("PGSQL_HOSTNAME")
81+
port, _ := strconv.Atoi(os.Getenv("PGSQL_PORT"))
82+
config.DatabaseConfig.DBPort = port
83+
config.DatabaseConfig.DBName = os.Getenv("PGSQL_DATABASE")
84+
// Disable SSL mode for local development
85+
config.DatabaseConfig.DBSSLMode = "disable"
86+
config.DatabaseConfig.DBDNS = fmt.Sprintf("host=%v user=%v password=%v dbname=%v port=%v sslmode=%v", config.DatabaseConfig.DBHost, config.DatabaseConfig.DBUser, config.DatabaseConfig.DBPassword, config.DatabaseConfig.DBName, config.DatabaseConfig.DBPort, config.DatabaseConfig.DBSSLMode)
3487
}
3588
}
3689

pkg/database/database.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package database
2+
3+
import (
4+
"github.com/RedHatInsights/widget-layout-backend/pkg/config"
5+
"github.com/joho/godotenv"
6+
"gorm.io/driver/postgres"
7+
"gorm.io/gorm"
8+
)
9+
10+
var DB *gorm.DB
11+
12+
func init() {
13+
godotenv.Load()
14+
cfg := config.GetConfig()
15+
dns := cfg.DatabaseConfig.DBDNS
16+
db, err := gorm.Open(postgres.Open(dns), &gorm.Config{})
17+
if err != nil {
18+
panic("failed to connect to database: " + err.Error())
19+
}
20+
DB = db
21+
}

0 commit comments

Comments
 (0)