1
0
mirror of https://github.com/alm494/sql_proxy.git synced 2025-10-08 22:01:51 +02:00

systemd support

This commit is contained in:
Almaz Sharipov
2025-10-01 08:17:59 +03:00
parent a1d557dd36
commit f8c4a5295c
11 changed files with 334 additions and 84 deletions

View File

@@ -1,5 +1,5 @@
PROJECT_NAME := sql-proxy
BUILD_VERSION := 1.2.1
BUILD_VERSION := 1.3.1
BUILD_TIME := $(shell date -u '+%Y-%m-%d_%H:%M:%S')
BUILD_DIR := build
GO_FILES := src/main.go
@@ -15,7 +15,6 @@ GOARCH := amd64
GOAMD64 := v2
# Application settings to run:
LOG_LEVEL := 6
BIND_PORT := 8080
BIND_ADDR := localhost
MAX_ROWS := 10000
@@ -50,7 +49,7 @@ debug: clean
# Run
run: debug
@echo "Running $(PROJECT_NAME) in debug mode..."
BIND_ADDR=$(BIND_ADDR) BIND_PORT=$(BIND_PORT) MAX_ROWS=$(MAX_ROWS) TLS_CERT=$(TLS_CERT) TLS_KEY=$(TLS_KEY) LOG_LEVEL=$(LOG_LEVEL) $(BUILD_DIR)/$(PROJECT_NAME)-debug
BIND_ADDR=$(BIND_ADDR) BIND_PORT=$(BIND_PORT) MAX_ROWS=$(MAX_ROWS) TLS_CERT=$(TLS_CERT) TLS_KEY=$(TLS_KEY) $(BUILD_DIR)/$(PROJECT_NAME)-debug
# Run test
test:

26
go.mod
View File

@@ -1,19 +1,23 @@
module sql-proxy
go 1.23.5
go 1.24.0
require (
github.com/denisenkom/go-mssqldb v0.12.3
github.com/go-sql-driver/mysql v1.8.1
github.com/go-sql-driver/mysql v1.9.3
github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.1
github.com/lib/pq v1.10.9
github.com/prometheus/client_golang v1.20.5
github.com/prometheus/client_golang v1.23.2
github.com/sirupsen/logrus v1.9.3
golang.org/x/crypto v0.33.0
golang.org/x/crypto v0.42.0
)
require github.com/gorilla/securecookie v1.1.2 // indirect
require (
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/kardianos/service v1.2.4 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
@@ -22,11 +26,11 @@ require (
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/gorilla/sessions v1.4.0
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
golang.org/x/sys v0.30.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/procfs v0.17.0 // indirect
golang.org/x/sys v0.36.0 // indirect
google.golang.org/protobuf v1.36.9 // indirect
)

24
go.sum
View File

@@ -14,6 +14,8 @@ github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDror
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
@@ -28,8 +30,12 @@ github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kX
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
github.com/kardianos/service v1.2.4 h1:XNlGtZOYNx2u91urOdg/Kfmc+gfmuIo1Dd3rEi2OgBk=
github.com/kardianos/service v1.2.4/go.mod h1:E4V9ufUuY82F7Ztlu1eN9VXWIQxg8NoLQlmFe0MtrXc=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
@@ -39,16 +45,26 @@ github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@@ -56,6 +72,8 @@ golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -69,6 +87,10 @@ golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -77,6 +99,8 @@ google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNI
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

View File

@@ -17,7 +17,7 @@ func GetEnvInt(key string, defaultValue int) int {
if intValue, err := strconv.Atoi(value); err == nil {
return intValue
}
Log.Errorf("Invalid integer value for %s, using default value: %d", key, defaultValue)
Logger.Errorf("Invalid integer value for %s, using default value: %d", key, defaultValue)
}
return defaultValue
}

View File

@@ -1,14 +1,103 @@
package app
import (
"io"
"os"
"github.com/kardianos/service"
"github.com/sirupsen/logrus"
)
var Log = &logrus.Logger{
Out: os.Stderr,
Formatter: new(logrus.TextFormatter),
Hooks: make(logrus.LevelHooks),
Level: logrus.ErrorLevel,
type LoggerInterface interface {
Info(args ...interface{})
Infof(format string, args ...interface{})
Error(args ...interface{})
Errorf(format string, args ...interface{})
Warn(args ...interface{})
Warnf(format string, args ...interface{})
}
// Global logger:
var Logger LoggerInterface
// service logger:
type ServiceLogger struct {
Logger service.Logger
}
func (s *ServiceLogger) Info(args ...interface{}) {
s.Logger.Info(args...)
}
func (s *ServiceLogger) Infof(format string, args ...interface{}) {
s.Logger.Infof(format, args...)
}
func (s *ServiceLogger) Error(args ...interface{}) {
s.Logger.Error(args...)
}
func (s *ServiceLogger) Errorf(format string, args ...interface{}) {
s.Logger.Errorf(format, args...)
}
func (s *ServiceLogger) Warn(args ...interface{}) {
s.Logger.Warning(args...)
}
func (s *ServiceLogger) Warnf(format string, args ...interface{}) {
s.Logger.Warningf(format, args...)
}
// Console logger:
type ConsoleLogger struct {
Logger *logrus.Logger
}
func (c *ConsoleLogger) Info(args ...interface{}) {
c.Logger.Info(args...)
}
func (c *ConsoleLogger) Infof(format string, args ...interface{}) {
c.Logger.Infof(format, args...)
}
func (c *ConsoleLogger) Error(args ...interface{}) {
c.Logger.Error(args...)
}
func (c *ConsoleLogger) Errorf(format string, args ...interface{}) {
c.Logger.Errorf(format, args...)
}
func (c *ConsoleLogger) Warn(args ...interface{}) {
c.Logger.Warn(args...)
}
func (c *ConsoleLogger) Warnf(format string, args ...interface{}) {
c.Logger.Warnf(format, args...)
}
// Create logger variants
func NewConsoleLogger() *ConsoleLogger {
l := logrus.New()
l.SetOutput(io.Discard)
l.SetFormatter(&logrus.TextFormatter{
ForceColors: true,
FullTimestamp: true,
})
l.SetLevel(logrus.InfoLevel)
l.SetOutput(os.Stdout)
return &ConsoleLogger{Logger: l}
}
func NewServiceLogger(svcLogger service.Logger) *ServiceLogger {
return &ServiceLogger{Logger: svcLogger}
}
// Init
func InitLogger(logger LoggerInterface) {
Logger = logger
}

View File

@@ -12,7 +12,6 @@ import (
"slices"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
)
// Init map
@@ -41,7 +40,7 @@ func (o *DbList) GetById(id string, updateTimestamp bool) (*sql.DB, bool) {
o.mu.RUnlock()
app.Log.Errorf("SQL connection with guid='%s' not found", id)
app.Logger.Errorf("SQL connection with guid='%s' not found", id)
return nil, false
}
@@ -52,7 +51,7 @@ func (o *DbList) GetByParams(connInfo *DbConnInfo) (string, bool) {
hash, err := connInfo.GetHash()
if err != nil {
errMsg := "Hash calculation failed"
app.Log.WithError(err).Error(errMsg)
app.Logger.Error(errMsg)
return errMsg, false
}
@@ -65,7 +64,7 @@ func (o *DbList) GetByParams(connInfo *DbConnInfo) (string, bool) {
// Search existing connection by hash to reuse
if bytes.Equal(dbConn.Hash[:], hash[:]) {
guid = key
app.Log.Debugf("DB connection with id %s found in the pool", guid)
app.Logger.Infof("DB connection with id %s found in the pool", guid)
// Perform checks
if err = dbConn.DB.Ping(); err == nil {
@@ -79,7 +78,7 @@ func (o *DbList) GetByParams(connInfo *DbConnInfo) (string, bool) {
delete(o.items, guid)
o.mu.Unlock()
o.mu.RLock()
app.Log.Debugf("DB connection with id %s is dead and removed from the pool", guid)
app.Logger.Infof("DB connection with id %s is dead and removed from the pool", guid)
}
}
}
@@ -117,7 +116,7 @@ func (o *DbList) getNewConnection(connInfo *DbConnInfo, hash [32]byte) (string,
connInfo.User, encodedPassword, connInfo.Host, connInfo.Port, connInfo.DbName)
default:
errMsg := fmt.Sprintf("No suitable driver implemented for server type '%s'", connInfo.DbType)
app.Log.Error(errMsg)
app.Logger.Error(errMsg)
return errMsg, false
}
@@ -130,14 +129,14 @@ func (o *DbList) getNewConnection(connInfo *DbConnInfo, hash [32]byte) (string,
// Check for failure
if err != nil {
errMsg := "Error establishing SQL server connection"
app.Log.WithError(err).Error(errMsg)
app.Logger.Error(errMsg)
return errMsg, false
}
// Check if alive
if err = newDb.Ping(); err != nil {
errMsg := "Just created SQL connection is dead"
app.Log.WithError(err).Error(errMsg)
app.Logger.Error(errMsg)
return errMsg, false
}
@@ -151,14 +150,16 @@ func (o *DbList) getNewConnection(connInfo *DbConnInfo, hash [32]byte) (string,
o.items[newId] = newItem
app.Log.WithFields(logrus.Fields{
"Host": connInfo.Host,
"Port": connInfo.Port,
"dbName": connInfo.DbName,
"user": connInfo.User,
"dbType": connInfo.DbType,
"Id": newId,
}).Infof("New SQL connection with id %s was added to the pool", newId)
app.Logger.Infof("New SQL connection with id %s was added to the pool: "+
"Host=%s, Port=%d, dbName=%s, user=%s, dbType=%s, Id=%s",
newId,
connInfo.Host,
connInfo.Port,
connInfo.DbName,
connInfo.User,
connInfo.DbType,
newId,
)
return newId, true
}
@@ -169,7 +170,7 @@ func (o *DbList) Delete(id string) {
defer o.mu.Unlock()
delete(o.items, id)
app.Log.Debugf("DB connection with id %s was deleted by query", id)
app.Logger.Infof("DB connection with id %s was deleted by query", id)
}
@@ -302,8 +303,8 @@ func (o *DbList) RunMaintenance() {
o.mu.Unlock()
app.Log.Debugf("Regular task: SQL connection pool size = %d", countConn)
app.Log.Debugf("Regular task: %d dead connections removed", countDeadConn)
app.Log.Debugf("Regular task: %d lost prepared statements removed", countStmt)
app.Logger.Infof("Regular task: SQL connection pool size = %d", countConn)
app.Logger.Infof("Regular task: %d dead connections removed", countDeadConn)
app.Logger.Infof("Regular task: %d lost prepared statements removed", countStmt)
}
}

View File

@@ -22,7 +22,7 @@ func checkApiVersion(w http.ResponseWriter, r *http.Request) bool {
apiVersion := r.Header.Get("API-Version")
if apiVersion != app.ApiVersion {
message := "Unsupported API version"
app.Log.Error(message)
app.Logger.Error(message)
http.Error(w, message, http.StatusNotImplemented)
return false
} else {
@@ -33,7 +33,7 @@ func checkApiVersion(w http.ResponseWriter, r *http.Request) bool {
func errorResponce(w http.ResponseWriter, message string, httpStatus int) {
app.Log.Error(message)
app.Logger.Error(message)
http.Error(w, message, httpStatus)
}

View File

@@ -6,8 +6,6 @@ import (
"net/http"
"sql-proxy/src/app"
"sql-proxy/src/db"
"github.com/sirupsen/logrus"
)
func PrepareStatement(w http.ResponseWriter, r *http.Request) {
@@ -108,10 +106,7 @@ func ClosePreparedStatement(w http.ResponseWriter, r *http.Request) {
return
}
app.Log.WithFields(logrus.Fields{
"connection_id": connId,
"prepared_statement": stmtId,
}).Debug("Delete prepared statememt received:")
app.Logger.Infof("Delete prepared statememt received: connection_id=%s, prepared_statement=%s", connId, stmtId)
if ok := db.Handler.ClosePreparedStatement(connId, stmtId); !ok {
errorResponce(w, "Forbidden", http.StatusForbidden)
@@ -131,10 +126,7 @@ func parsePrepareStatementHttpHeadersAndBody(w http.ResponseWriter, r *http.Requ
defer r.Body.Close()
sqlQuery := string(body)
app.Log.WithFields(logrus.Fields{
"sql": sqlQuery,
"connection_id": connId,
}).Debug("Prepared statement received:")
app.Logger.Infof("Prepared statement received: sql=%s, connection_id=%s", sqlQuery, connId)
return connId, sqlQuery, true
@@ -163,10 +155,7 @@ func parseExecuteStatementHttpHeadersAndBody(w http.ResponseWriter, r *http.Requ
}
}
app.Log.WithFields(logrus.Fields{
"connection_id": connId,
"statement_id": stmtId,
}).Debug("Execute prepared statement received:")
app.Logger.Infof("Execute prepared statement received: connection_id=%s, statement_id=%s", connId, stmtId)
return connId, stmtId, params, true

View File

@@ -6,8 +6,6 @@ import (
"sql-proxy/src/app"
"sql-proxy/src/db"
"github.com/sirupsen/logrus"
)
func SelectQuery(w http.ResponseWriter, r *http.Request) {
@@ -74,10 +72,7 @@ func parseQueryHttpHeadersAndBody(w http.ResponseWriter, r *http.Request) (strin
defer r.Body.Close()
sqlQuery := string(body)
app.Log.WithFields(logrus.Fields{
"sql": sqlQuery,
"connection_id": connId,
}).Debug("SQL query received:")
app.Logger.Infof("SQL query received: sql=%s, connection_id=%s", sqlQuery, connId)
return connId, sqlQuery, true

View File

@@ -1,23 +1,40 @@
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"sql-proxy/src/app"
"sql-proxy/src/db"
"sql-proxy/src/handlers"
"github.com/gorilla/mux"
"github.com/kardianos/service"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/sirupsen/logrus"
)
func main() {
var err error
var svcLogger service.Logger
type program struct {
exit chan struct{}
}
func (p *program) Start(s service.Service) error {
app.Logger.Info("Starting sql-proxy service...")
p.exit = make(chan struct{})
go p.run()
return nil
}
func (p *program) run() {
// Application params taken from OS environment
app.Log.SetLevel(logrus.Level(app.GetEnvInt("LOG_LEVEL", 2)))
bindAddress := app.GetEnvString("BIND_ADDR", "localhost")
if bindAddress == "*" {
bindAddress = ""
@@ -47,26 +64,95 @@ func main() {
router.HandleFunc("/livez", handlers.Livez).Methods("GET")
router.Handle("/metrics", promhttp.Handler())
app.Log.Info("(c) 2025 Almaz Sharipov, MIT license, https://github.com/alm494/sql_proxy")
app.Log.WithFields(logrus.Fields{
"build_version": app.BuildVersion,
"build_time": app.BuildTime,
}).Info("Starting server sql-proxy:")
app.Log.WithFields(logrus.Fields{
"bind_port": bindPort,
"bind_address": bindAddress,
"tls_cert": tlsCert,
"tls_key": tlsKey,
}).Info("Server started with the following parameters:")
app.Logger.Info("(c) 2025 Almaz Sharipov, MIT license, https://github.com/alm494/sql_proxy ")
app.Logger.Infof("build_version=%s, build_time=%s", app.BuildVersion, app.BuildTime)
app.Logger.Infof("Server started with the following parameters: "+
"bind_port=%d, bind_address=%s, tls_cert=%s, tls_key=%s", bindPort, bindAddress, tlsCert, tlsKey)
addr := fmt.Sprintf("%s:%d", bindAddress, bindPort)
if len(tlsCert) > 0 && len(tlsKey) > 0 {
err = http.ListenAndServeTLS(addr, tlsCert, tlsKey, router)
} else {
err = http.ListenAndServe(addr, router)
srv := &http.Server{
Addr: addr,
Handler: router,
}
if err != nil {
app.Log.WithError(err).Fatal("Fatal error occurred, service stopped")
go func() {
var err error
if len(tlsCert) > 0 && len(tlsKey) > 0 {
err = srv.ListenAndServeTLS(tlsCert, tlsKey)
} else {
err = srv.ListenAndServe()
}
if err != nil && err != http.ErrServerClosed {
app.Logger.Errorf("Fatal error occurred, service stopped: %v", err)
}
}()
// Wait for exit signal
<-p.exit
// Shutdown server gracefully
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
app.Logger.Errorf("Server shutdown failed: %v", err)
} else {
app.Logger.Info("Server exited properly")
}
}
func (p *program) Stop(s service.Service) error {
app.Logger.Info("Stopping sql-proxy service...")
close(p.exit)
return nil
}
func main() {
svcConfig := &service.Config{
Name: "sql-proxy",
DisplayName: "SQL Proxy Service",
Description: "A lightweight REST service designed to replace ADODB calls in legacy software systems that support web requests",
Arguments: []string{},
}
prg := &program{}
s, err := service.New(prg, svcConfig)
if err != nil {
log.Fatal(err)
}
svcLogger, err = s.Logger(nil)
if err != nil {
log.Fatal(err)
}
if len(os.Args) > 1 {
// Handle service commands: install, start, stop, uninstall
err := service.Control(s, os.Args[1])
if err != nil {
log.Fatal(err)
}
return
}
// Run as a regular app
if !service.Interactive() {
app.InitLogger(app.NewServiceLogger(svcLogger))
err = s.Run()
if err != nil {
svcLogger.Error(err)
}
} else {
// Run in console mode
app.InitLogger(app.NewConsoleLogger())
fmt.Println("Running in console mode...")
prg.Start(nil)
// Wait for interrupt signal
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
fmt.Println("Received interrupt signal, shutting down...")
prg.Stop(nil)
}
}

63
systemd/install.sh Executable file
View File

@@ -0,0 +1,63 @@
#!/bin/bash
SERVICE_USER="sql-proxy"
SERVICE_GROUP="sql-proxy"
SERVICE_DIR="/opt/sql-proxy"
LOG_DIR="/var/log/sql-proxy"
EXECUTABLE="sql-proxy"
SERVICE_FILE="/etc/systemd/system/sql-proxy.service"
if ! id "$SERVICE_USER" &>/dev/null; then
useradd --system --no-create-home --shell /bin/false "$SERVICE_USER"
echo "User $SERVICE_USER created."
else
echo "User $SERVICE_USER already exists."
fi
mkdir -p "$SERVICE_DIR"
mkdir -p "$LOG_DIR"
chown "$SERVICE_USER:$SERVICE_GROUP" "$SERVICE_DIR"
chown "$SERVICE_USER:$SERVICE_GROUP" "$LOG_DIR"
cp "./$EXECUTABLE" "$SERVICE_DIR/"
chown "$SERVICE_USER:$SERVICE_GROUP" "$SERVICE_DIR/$EXECUTABLE"
chmod +x "$SERVICE_DIR/$EXECUTABLE"
cat > "$SERVICE_FILE" <<EOF
[Unit]
Description=SQL Proxy Service
After=network.target
[Service]
Environment="BIND_ADDR=127.0.0.1"
Environment="BIND_PORT=8080"
Environment="MAX_ROWS=10000"
#Environment="TLS_CERT=/etc/ssl/certs/cert.pem"
#Environment="TLS_KEY=/etc/ssl/private/key.pem"
Type=simple
User=$SERVICE_USER
Group=$SERVICE_GROUP
WorkingDirectory=$SERVICE_DIR
ExecStartPre=-/bin/mkdir -p $LOG_DIR
ExecStartPre=-/bin/chown $SERVICE_USER:$SERVICE_GROUP $LOG_DIR
ExecStart=$SERVICE_DIR/$EXECUTABLE
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable sql-proxy --now
if systemctl is-active --quiet sql-proxy; then
echo "Service is running. Restarting..."
systemctl restart sql-proxy
else
echo "Starting sql-proxy service..."
systemctl start sql-proxy
fi
echo "SQL Proxy service installed and running."