Skip to content

Commit 52a0c94

Browse files
authored
feat(server/database): add SQLite support and configuration (#1195)
* feat(server/database): add SQLite support and configuration Signed-off-by: Catarina Paralta <clouropa@cisco.com> * feat(database): enable foreign key support in SQLite configuration Signed-off-by: Catarina Paralta <clouropa@cisco.com> * fix(tests): update database configuration in tests to include SQLite support Signed-off-by: Catarina Paralta <clouropa@cisco.com> * feat(database/config): database path Signed-off-by: Catarina Paralta <clouropa@cisco.com> * feat(database): add in-memory SQLite connection string support Signed-off-by: Catarina Paralta <clouropa@cisco.com> --------- Signed-off-by: Catarina Paralta <clouropa@cisco.com>
1 parent 594b92e commit 52a0c94

File tree

10 files changed

+506
-4
lines changed

10 files changed

+506
-4
lines changed

reconciler/config/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ func LoadConfig() (*Config, error) {
9191
_ = v.BindEnv("database.type")
9292
v.SetDefault("database.type", dbconfig.DefaultType)
9393

94+
// SQLite configuration
95+
_ = v.BindEnv("database.sqlite.path")
96+
v.SetDefault("database.sqlite.path", dbconfig.DefaultSQLitePath)
97+
9498
// PostgreSQL configuration
9599
_ = v.BindEnv("database.postgres.host")
96100
v.SetDefault("database.postgres.host", dbconfig.DefaultPostgresHost)

reconciler/go.mod

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ require (
5555
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
5656
github.com/fsnotify/fsnotify v1.9.0 // indirect
5757
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
58+
github.com/glebarez/go-sqlite v1.21.2 // indirect
59+
github.com/glebarez/sqlite v1.11.0 // indirect
5860
github.com/go-chi/chi/v5 v5.2.5 // indirect
5961
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
6062
github.com/go-logr/logr v1.4.3 // indirect
@@ -146,6 +148,7 @@ require (
146148
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
147149
github.com/pkg/errors v0.9.1 // indirect
148150
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
151+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
149152
github.com/sagikazarmark/locafero v0.12.0 // indirect
150153
github.com/sassoftware/relic v7.2.1+incompatible // indirect
151154
github.com/secure-systems-lab/go-securesystemslib v0.10.0 // indirect
@@ -212,6 +215,10 @@ require (
212215
k8s.io/kube-openapi v0.0.0-20260319004828-5883c5ee87b9 // indirect
213216
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect
214217
lukechampine.com/blake3 v1.4.1 // indirect
218+
modernc.org/libc v1.22.5 // indirect
219+
modernc.org/mathutil v1.5.0 // indirect
220+
modernc.org/memory v1.5.0 // indirect
221+
modernc.org/sqlite v1.23.1 // indirect
215222
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
216223
sigs.k8s.io/randfill v1.0.0 // indirect
217224
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect

reconciler/go.sum

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S
164164
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
165165
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
166166
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
167+
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
168+
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
169+
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
170+
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
167171
github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=
168172
github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
169173
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
@@ -270,6 +274,8 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
270274
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
271275
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
272276
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
277+
github.com/google/pprof v0.0.0-20250602020802-c6617b811d0e h1:FJta/0WsADCe1r9vQjdHbd3KuiLPu7Y9WlyLGwMUNyE=
278+
github.com/google/pprof v0.0.0-20250602020802-c6617b811d0e/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
273279
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
274280
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
275281
github.com/google/trillian v1.7.2 h1:EPBxc4YWY4Ak8tcuhyFleY+zYlbCDCa4Sn24e1Ka8Js=
@@ -480,6 +486,9 @@ github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTU
480486
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
481487
github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=
482488
github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
489+
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
490+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
491+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
483492
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
484493
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
485494
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
@@ -774,6 +783,14 @@ k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0x
774783
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
775784
lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg=
776785
lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo=
786+
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
787+
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
788+
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
789+
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
790+
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
791+
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
792+
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
793+
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
777794
oras.land/oras-go/v2 v2.6.0 h1:X4ELRsiGkrbeox69+9tzTu492FMUu7zJQW6eJU+I2oc=
778795
oras.land/oras-go/v2 v2.6.0/go.mod h1:magiQDfG6H1O9APp+rOsvCPcW1GD2MM7vgnKY0Y+u1o=
779796
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=

server/config/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,10 @@ func LoadConfig() (*Config, error) {
448448
_ = v.BindEnv("database.type")
449449
v.SetDefault("database.type", dbconfig.DefaultType)
450450

451+
// SQLite configuration
452+
_ = v.BindEnv("database.sqlite.path")
453+
v.SetDefault("database.sqlite.path", dbconfig.DefaultSQLitePath)
454+
451455
// PostgreSQL configuration
452456
_ = v.BindEnv("database.postgres.host")
453457
v.SetDefault("database.postgres.host", dbconfig.DefaultPostgresHost)

server/config/config_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ func TestConfig(t *testing.T) {
100100
},
101101
Database: dbconfig.Config{
102102
Type: "postgres",
103+
SQLite: dbconfig.SQLiteConfig{
104+
Path: dbconfig.DefaultSQLitePath,
105+
},
103106
Postgres: dbconfig.PostgresConfig{
104107
Host: "localhost",
105108
Port: 5432,
@@ -167,6 +170,9 @@ func TestConfig(t *testing.T) {
167170
},
168171
Database: dbconfig.Config{
169172
Type: dbconfig.DefaultType,
173+
SQLite: dbconfig.SQLiteConfig{
174+
Path: dbconfig.DefaultSQLitePath,
175+
},
170176
Postgres: dbconfig.PostgresConfig{
171177
Host: dbconfig.DefaultPostgresHost,
172178
Port: dbconfig.DefaultPostgresPort,

server/database/config/config.go

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33

44
package config
55

6+
import (
7+
"os"
8+
"path/filepath"
9+
)
10+
611
const (
712
// DefaultType is the default database type if not specified in the config.
813
DefaultType = "postgres"
@@ -14,14 +19,52 @@ const (
1419
DefaultPostgresSSLMode = "disable"
1520
)
1621

22+
// DefaultDataDir is the persistent data directory (~/.dir).
23+
var DefaultDataDir = EnsureFilePath(filepath.Join(GetDataDir(), ".dir"))
24+
25+
// DefaultSQLitePath is the default path for the SQLite database file.
26+
var DefaultSQLitePath = EnsureFilePath(filepath.Join(DefaultDataDir, "dir.db"))
27+
28+
// GetDataDir returns the user home directory, falling back to os.TempDir().
29+
func GetDataDir() string {
30+
homeDir, err := os.UserHomeDir()
31+
if err != nil {
32+
return os.TempDir()
33+
}
34+
35+
return homeDir
36+
}
37+
38+
// EnsureFilePath creates parent directories for path and returns its absolute form.
39+
func EnsureFilePath(path string) string {
40+
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil { //nolint:mnd
41+
panic(err)
42+
}
43+
44+
absPath, err := filepath.Abs(path)
45+
if err != nil {
46+
return path
47+
}
48+
49+
return absPath
50+
}
51+
1752
type Config struct {
18-
// Type is the type of the database (postgres).
53+
// Type is the type of the database (sqlite or postgres).
1954
Type string `json:"type,omitempty" mapstructure:"type"`
2055

56+
// SQLite database configuration.
57+
SQLite SQLiteConfig `json:"sqlite" mapstructure:"sqlite"`
58+
2159
// PostgreSQL database configuration.
2260
Postgres PostgresConfig `json:"postgres" mapstructure:"postgres"`
2361
}
2462

63+
type SQLiteConfig struct {
64+
// Path is the filesystem path to the SQLite database file.
65+
Path string `json:"path,omitempty" mapstructure:"path"`
66+
}
67+
2568
type PostgresConfig struct {
2669
// Host is the PostgreSQL server hostname.
2770
Host string `json:"host,omitempty" mapstructure:"host"`

server/database/database.go

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import (
77
"fmt"
88
"log"
99
"os"
10+
"strings"
1011
"time"
1112

1213
"github.com/agntcy/dir/server/database/config"
1314
gormdb "github.com/agntcy/dir/server/database/gorm"
1415
"github.com/agntcy/dir/server/types"
16+
"github.com/glebarez/sqlite"
1517
"gorm.io/driver/postgres"
1618
"gorm.io/gorm"
1719
gormlogger "gorm.io/gorm/logger"
@@ -20,13 +22,21 @@ import (
2022
type DB string
2123

2224
const (
25+
SQLite DB = "sqlite"
2326
Postgres DB = "postgres"
2427
)
2528

26-
func New(config config.Config) (types.DatabaseAPI, error) {
27-
switch db := DB(config.Type); db {
29+
func New(cfg config.Config) (types.DatabaseAPI, error) {
30+
switch db := DB(cfg.Type); db {
31+
case SQLite:
32+
sqliteDB, err := newSQLite(cfg.SQLite)
33+
if err != nil {
34+
return nil, fmt.Errorf("failed to create SQLite database: %w", err)
35+
}
36+
37+
return sqliteDB, nil
2838
case Postgres:
29-
postgresDB, err := newPostgres(config.Postgres)
39+
postgresDB, err := newPostgres(cfg.Postgres)
3040
if err != nil {
3141
return nil, fmt.Errorf("failed to create PostgreSQL database: %w", err)
3242
}
@@ -51,6 +61,43 @@ func newCustomLogger() gormlogger.Interface {
5161
)
5262
}
5363

64+
// isMemoryDSN returns true for SQLite in-memory connection strings
65+
// (e.g. ":memory:", "file::memory:", "file::memory:?cache=shared").
66+
func isMemoryDSN(path string) bool {
67+
return path == ":memory:" || strings.HasPrefix(path, "file::memory:")
68+
}
69+
70+
// newSQLite creates a new database connection using the pure-Go SQLite driver.
71+
func newSQLite(cfg config.SQLiteConfig) (*gormdb.DB, error) {
72+
path := cfg.Path
73+
if path == "" {
74+
path = config.DefaultSQLitePath
75+
}
76+
77+
if !isMemoryDSN(path) {
78+
path = config.EnsureFilePath(path)
79+
}
80+
81+
db, err := gorm.Open(sqlite.Open(path), &gorm.Config{
82+
Logger: newCustomLogger(),
83+
})
84+
if err != nil {
85+
return nil, fmt.Errorf("failed to connect to SQLite database: %w", err)
86+
}
87+
88+
// SQLite does not enforce foreign keys by default; enable for CASCADE support.
89+
if err := db.Exec("PRAGMA foreign_keys = ON").Error; err != nil {
90+
return nil, fmt.Errorf("failed to enable SQLite foreign keys: %w", err)
91+
}
92+
93+
gdb, err := gormdb.New(db)
94+
if err != nil {
95+
return nil, fmt.Errorf("failed to initialize SQLite database: %w", err)
96+
}
97+
98+
return gdb, nil
99+
}
100+
54101
// newPostgres creates a new database connection using PostgreSQL driver.
55102
func newPostgres(cfg config.PostgresConfig) (*gormdb.DB, error) {
56103
host := cfg.Host

0 commit comments

Comments
 (0)