Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(healthchecks): Add healthchecks for SQL Server in health-go/v5 library #92

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* HTTP
* MongoDB
* MySQL
* SQLServer
* gRPC
* Memcached
* InfluxDB
Expand Down
3 changes: 3 additions & 0 deletions Taskfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ tasks:
sh: docker-compose port mongo 27017
MYSQL_HOST:
sh: docker-compose port mysql 3306
SQLSERVER_HOST:
sh: docker-compose port sqlserver 1433
MEMCACHED_HOST:
sh: docker-compose port memcached 11211
INFLUX_HOST:
Expand All @@ -59,5 +61,6 @@ tasks:
HEALTH_GO_RD_DSN: 'redis://{{.REDIS_HOST}}/'
HEALTH_GO_MG_DSN: 'mongodb://{{.MONGO_HOST}}/'
HEALTH_GO_MS_DSN: 'test:test@tcp({{.MYSQL_HOST}})/test?charset=utf8'
HEALTH_GO_SS_DSN: 'sqlserver://Test:MyTestPassword456@{{.SQLSERVER_HOST}}/master?encrypt=disable'
HEALTH_GO_MD_DSN: 'memcached://localhost:{{.MEMCACHED_HOST}}/'
HEALTH_GO_INFLUXDB_URL: 'http://{{.INFLUX_HOST}}'
53 changes: 53 additions & 0 deletions checks/sqlserver/check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package sqlserver

import (
"context"
"database/sql"
"fmt"

"github.com/microsoft/go-mssqldb/azuread"
)

// Config is the SQL Server checker configuration settings container.
type Config struct {
// DSN is the SQL Server instance connection DSN. Required.
DSN string
}

// New creates new SQL Server health check that verifies the following:
// - connection establishing
// - doing the ping command
// - selecting SQL Server version
func New(config Config) func(ctx context.Context) error {
return func(ctx context.Context) (checkErr error) {
db, err := sql.Open(azuread.DriverName, config.DSN)
if err != nil {
checkErr = fmt.Errorf("SQL Server health check failed on connect: %w", err)
return
}

defer func() {
// override checkErr only if there were no other errors
if err = db.Close(); err != nil && checkErr == nil {
checkErr = fmt.Errorf("SQL Server health check failed on connection closing: %w", err)
}
}()

err = db.PingContext(ctx)
if err != nil {
checkErr = fmt.Errorf("SQL Server health check failed on ping: %w", err)
return
}

rows, err := db.QueryContext(ctx, `SELECT @@VERSION`)
if err != nil {
checkErr = fmt.Errorf("SQL Server health check failed on select: %w", err)
return
}
if err = rows.Close(); err != nil {
checkErr = fmt.Errorf("SQL Server health check failed on rows closing: %w", err)
}

return
}
}
104 changes: 104 additions & 0 deletions checks/sqlserver/check_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package sqlserver

import (
"context"
"database/sql"
"os"
"sync"
"testing"
"time"

"github.com/microsoft/go-mssqldb/azuread"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

const sqlServerDSNEnv = "HEALTH_GO_SS_DSN"

func TestNew(t *testing.T) {
initDB(t)

check := New(Config{
DSN: getDSN(t),
})

err := check(context.Background())
require.NoError(t, err)
}

func TestEnsureConnectionIsClosed(t *testing.T) {
initDB(t)

sqlDSN := getDSN(t)

db, err := sql.Open(azuread.DriverName, sqlDSN)
require.NoError(t, err)

defer func() {
err := db.Close()
assert.NoError(t, err)
}()

var initialConnections int
row := db.QueryRow(`SELECT COUNT(session_id) FROM sys.dm_exec_sessions`)
err = row.Scan(&initialConnections)
require.NoError(t, err)

check := New(Config{
DSN: sqlDSN,
})

ctx := context.Background()
for i := 0; i < 10; i++ {
err := check(ctx)
assert.NoError(t, err)
time.Sleep(100 * time.Millisecond)
}

var currentConnections int
row = db.QueryRow(`SELECT COUNT(session_id) FROM sys.dm_exec_sessions`)
err = row.Scan(&currentConnections)
require.NoError(t, err)

assert.Equal(t, initialConnections, currentConnections)
}

func getDSN(t *testing.T) string {
t.Helper()

//get env
sqlServerDSN := os.Getenv(sqlServerDSNEnv)
require.NotEmpty(t, sqlServerDSN)

return sqlServerDSN
}

var dbInit sync.Once

func initDB(t *testing.T) {
t.Helper()

dbInit.Do(func() {
db, err := sql.Open(azuread.DriverName, getDSN(t))
require.NoError(t, err)

defer func() {
err := db.Close()
assert.NoError(t, err)
}()

_, err = db.Exec(`
IF OBJECT_ID('dbo.test_mssql', 'U') IS NULL
BEGIN
CREATE TABLE test_mssql (
id NVARCHAR(255) NOT NULL PRIMARY KEY,
secret NVARCHAR(255) NOT NULL,
extra NVARCHAR(255) NOT NULL,
redirect_uri NVARCHAR(255) NOT NULL
);
END
`)
require.NoError(t, err)
})
}
16 changes: 16 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,22 @@ services:
timeout: 5s
retries: 5

sqlserver:
image: mcr.microsoft.com/mssql/server:2019-latest
container_name: sqlserver
ports:
- "1433:1433"
environment:
SA_PASSWORD: "MyPassword123"
ACCEPT_EULA: "Y"
MSSQL_USER: "Test"
MSSQL_PASSWORD: "MyTestPassword456"
healthcheck:
test: [ "CMD-SHELL", "pidof sqlservr || exit 1" ]
interval: 30s
timeout: 10s
retries: 5

memcached:
image: memcached:1.6.9-alpine
ports:
Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* HTTP
* MongoDB
* MySQL
* SQLServer
* gRPC
* Memcached

Expand Down
12 changes: 11 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ require (
github.com/go-sql-driver/mysql v1.6.0
github.com/influxdata/influxdb-client-go/v2 v2.9.0
github.com/jackc/pgx/v4 v4.16.1
github.com/jackc/pgx/v5 v5.2.0
github.com/lib/pq v1.10.6
github.com/microsoft/go-mssqldb v0.21.0
github.com/rabbitmq/amqp091-go v1.3.4
github.com/stretchr/testify v1.8.0
github.com/vitorsalgado/mocha/v2 v2.0.2
Expand All @@ -19,11 +21,18 @@ require (
)

require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v0.8.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deepmap/oapi-codegen v1.11.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/google/uuid v1.3.0 // indirect
Expand All @@ -35,8 +44,9 @@ require (
github.com/jackc/pgproto3/v2 v2.3.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.11.0 // indirect
github.com/jackc/pgx/v5 v5.2.0 // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.4.0 // indirect
Expand Down
Loading