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 5 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://SA:MyPassword123@{{.SQLSERVER_HOST}}/master?encrypt=disable'
HEALTH_GO_MD_DSN: 'memcached://localhost:{{.MEMCACHED_HOST}}/'
HEALTH_GO_INFLUXDB_URL: 'http://{{.INFLUX_HOST}}'
4 changes: 0 additions & 4 deletions checks/rabbitmq/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,6 @@ func New(config Config) func(ctx context.Context) error {

// release the channel resources, and unblock the receive on done below
close(done)

// now drain any incidental remaining messages
for range messages {
}
}()

p := amqp.Publishing{Body: []byte(time.Now().Format(time.RFC3339Nano))}
Expand Down
56 changes: 56 additions & 0 deletions checks/sqlserver/check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
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
}
defer func() {
// override checkErr only if there were no other errors
if err = rows.Close(); err != nil && checkErr == nil {
checkErr = fmt.Errorf("SQL Server health check failed on rows closing: %w", err)
}
}()

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

import (
"context"
"database/sql"
"fmt"
"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, ok := os.LookupEnv(sqlServerDSNEnv)
fmt.Println(sqlServerDSN)
require.True(t, ok)

return sqlServerDSN
}

var dbInit sync.Once

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

dbInit.Do(func() {
// sqlDSN := getDSN(t)

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