whatcanGOwrong

This commit is contained in:
2024-09-19 21:38:24 -04:00
commit d0ae4d841d
17908 changed files with 4096831 additions and 0 deletions
@@ -0,0 +1,33 @@
# Microsoft SQL Server
`sqlserver://username:password@host/instance?param1=value&param2=value`
`sqlserver://username:password@host:port?param1=value&param2=value`
| URL Query | WithInstance Config | Description |
|------------|---------------------|-------------|
| `x-migrations-table` | `MigrationsTable` | Name of the migrations table |
| `username` | | enter the SQL Server Authentication user id or the Windows Authentication user id in the DOMAIN\User format. On Windows, if user id is empty or missing Single-Sign-On is used. |
| `password` | | The user's password. |
| `host` | | The host to connect to. |
| `port` | | The port to connect to. |
| `instance` | | SQL Server instance name. |
| `database` | `DatabaseName` | The name of the database to connect to |
| `connection+timeout` | | in seconds (default is 0 for no timeout), set to 0 for no timeout. |
| `dial+timeout` | | in seconds (default is 15), set to 0 for no timeout. |
| `encrypt` | | `disable` - Data send between client and server is not encrypted. `false` - Data sent between client and server is not encrypted beyond the login packet (Default). `true` - Data sent between client and server is encrypted. |
| `app+name` || The application name (default is go-mssqldb). |
| `useMsi` | | `true` - Use Azure MSI Authentication for connecting to Sql Server. Must be running from an Azure VM/an instance with MSI enabled. `false` - Use password authentication (Default). See [here for Azure MSI Auth details](https://docs.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-connect-msi). NOTE: Since this cannot be tested locally, this is not officially supported.
See https://github.com/microsoft/go-mssqldb for full parameter list.
## Driver Support
### Which go-mssqldb driver to us?
Please note that the deprecated `mssql` driver is not supported. Please use the newer `sqlserver` driver.
See https://github.com/microsoft/go-mssqldb#deprecated for more information.
### Official Support by migrate
Versions of MS SQL Server 2019 newer than CTP3.1 are not officially supported since there are issues testing against the Docker image.
For more info, see: https://github.com/golang-migrate/migrate/issues/160#issuecomment-522433269
@@ -0,0 +1,5 @@
CREATE TABLE users (
user_id integer unique,
name varchar(40),
email varchar(40)
);
@@ -0,0 +1,3 @@
CREATE UNIQUE INDEX users_email_index ON users (email);
-- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean sed interdum velit, tristique iaculis justo. Pellentesque ut porttitor dolor. Donec sit amet pharetra elit. Cras vel ligula ex. Phasellus posuere.
@@ -0,0 +1,5 @@
CREATE TABLE books (
user_id integer,
name varchar(40),
author varchar(40)
);
@@ -0,0 +1,5 @@
CREATE TABLE movies (
user_id integer,
name varchar(40),
director varchar(40)
);
@@ -0,0 +1 @@
-- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean sed interdum velit, tristique iaculis justo. Pellentesque ut porttitor dolor. Donec sit amet pharetra elit. Cras vel ligula ex. Phasellus posuere.
@@ -0,0 +1 @@
-- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean sed interdum velit, tristique iaculis justo. Pellentesque ut porttitor dolor. Donec sit amet pharetra elit. Cras vel ligula ex. Phasellus posuere.
@@ -0,0 +1 @@
-- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean sed interdum velit, tristique iaculis justo. Pellentesque ut porttitor dolor. Donec sit amet pharetra elit. Cras vel ligula ex. Phasellus posuere.
@@ -0,0 +1 @@
-- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean sed interdum velit, tristique iaculis justo. Pellentesque ut porttitor dolor. Donec sit amet pharetra elit. Cras vel ligula ex. Phasellus posuere.
@@ -0,0 +1,405 @@
package sqlserver
import (
"context"
"database/sql"
"fmt"
"io"
nurl "net/url"
"strconv"
"strings"
"go.uber.org/atomic"
"github.com/Azure/go-autorest/autorest/adal"
"github.com/golang-migrate/migrate/v4"
"github.com/golang-migrate/migrate/v4/database"
"github.com/hashicorp/go-multierror"
mssql "github.com/microsoft/go-mssqldb" // mssql support
)
func init() {
database.Register("sqlserver", &SQLServer{})
}
// DefaultMigrationsTable is the name of the migrations table in the database
var DefaultMigrationsTable = "schema_migrations"
var (
ErrNilConfig = fmt.Errorf("no config")
ErrNoDatabaseName = fmt.Errorf("no database name")
ErrNoSchema = fmt.Errorf("no schema")
ErrDatabaseDirty = fmt.Errorf("database is dirty")
ErrMultipleAuthOptionsPassed = fmt.Errorf("both password and useMsi=true were passed.")
)
var lockErrorMap = map[mssql.ReturnStatus]string{
-1: "The lock request timed out.",
-2: "The lock request was canceled.",
-3: "The lock request was chosen as a deadlock victim.",
-999: "Parameter validation or other call error.",
}
// Config for database
type Config struct {
MigrationsTable string
DatabaseName string
SchemaName string
}
// SQL Server connection
type SQLServer struct {
// Locking and unlocking need to use the same connection
conn *sql.Conn
db *sql.DB
isLocked atomic.Bool
// Open and WithInstance need to garantuee that config is never nil
config *Config
}
// WithInstance returns a database instance from an already created database connection.
//
// Note that the deprecated `mssql` driver is not supported. Please use the newer `sqlserver` driver.
func WithInstance(instance *sql.DB, config *Config) (database.Driver, error) {
if config == nil {
return nil, ErrNilConfig
}
if err := instance.Ping(); err != nil {
return nil, err
}
if config.DatabaseName == "" {
query := `SELECT DB_NAME()`
var databaseName string
if err := instance.QueryRow(query).Scan(&databaseName); err != nil {
return nil, &database.Error{OrigErr: err, Query: []byte(query)}
}
if len(databaseName) == 0 {
return nil, ErrNoDatabaseName
}
config.DatabaseName = databaseName
}
if config.SchemaName == "" {
query := `SELECT SCHEMA_NAME()`
var schemaName string
if err := instance.QueryRow(query).Scan(&schemaName); err != nil {
return nil, &database.Error{OrigErr: err, Query: []byte(query)}
}
if len(schemaName) == 0 {
return nil, ErrNoSchema
}
config.SchemaName = schemaName
}
if len(config.MigrationsTable) == 0 {
config.MigrationsTable = DefaultMigrationsTable
}
conn, err := instance.Conn(context.Background())
if err != nil {
return nil, err
}
ss := &SQLServer{
conn: conn,
db: instance,
config: config,
}
if err := ss.ensureVersionTable(); err != nil {
return nil, err
}
return ss, nil
}
// Open a connection to the database.
func (ss *SQLServer) Open(url string) (database.Driver, error) {
purl, err := nurl.Parse(url)
if err != nil {
return nil, err
}
useMsiParam := purl.Query().Get("useMsi")
useMsi := false
if len(useMsiParam) > 0 {
useMsi, err = strconv.ParseBool(useMsiParam)
if err != nil {
return nil, err
}
}
if _, isPasswordSet := purl.User.Password(); useMsi && isPasswordSet {
return nil, ErrMultipleAuthOptionsPassed
}
filteredURL := migrate.FilterCustomQuery(purl).String()
var db *sql.DB
if useMsi {
resource := getAADResourceFromServerUri(purl)
tokenProvider, err := getMSITokenProvider(resource)
if err != nil {
return nil, err
}
connector, err := mssql.NewAccessTokenConnector(
filteredURL, tokenProvider)
if err != nil {
return nil, err
}
db = sql.OpenDB(connector)
} else {
db, err = sql.Open("sqlserver", filteredURL)
if err != nil {
return nil, err
}
}
migrationsTable := purl.Query().Get("x-migrations-table")
px, err := WithInstance(db, &Config{
DatabaseName: purl.Path,
MigrationsTable: migrationsTable,
})
if err != nil {
return nil, err
}
return px, nil
}
// Close the database connection
func (ss *SQLServer) Close() error {
connErr := ss.conn.Close()
dbErr := ss.db.Close()
if connErr != nil || dbErr != nil {
return fmt.Errorf("conn: %v, db: %v", connErr, dbErr)
}
return nil
}
// Lock creates an advisory local on the database to prevent multiple migrations from running at the same time.
func (ss *SQLServer) Lock() error {
return database.CasRestoreOnErr(&ss.isLocked, false, true, database.ErrLocked, func() error {
aid, err := database.GenerateAdvisoryLockId(ss.config.DatabaseName, ss.config.SchemaName)
if err != nil {
return err
}
// This will either obtain the lock immediately and return true,
// or return false if the lock cannot be acquired immediately.
// MS Docs: sp_getapplock: https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-getapplock-transact-sql?view=sql-server-2017
query := `EXEC sp_getapplock @Resource = @p1, @LockMode = 'Update', @LockOwner = 'Session', @LockTimeout = 0`
var status mssql.ReturnStatus
if _, err = ss.conn.ExecContext(context.Background(), query, aid, &status); err == nil && status > -1 {
return nil
} else if err != nil {
return &database.Error{OrigErr: err, Err: "try lock failed", Query: []byte(query)}
} else {
return &database.Error{Err: fmt.Sprintf("try lock failed with error %v: %v", status, lockErrorMap[status]), Query: []byte(query)}
}
})
}
// Unlock froms the migration lock from the database
func (ss *SQLServer) Unlock() error {
return database.CasRestoreOnErr(&ss.isLocked, true, false, database.ErrNotLocked, func() error {
aid, err := database.GenerateAdvisoryLockId(ss.config.DatabaseName, ss.config.SchemaName)
if err != nil {
return err
}
// MS Docs: sp_releaseapplock: https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-releaseapplock-transact-sql?view=sql-server-2017
query := `EXEC sp_releaseapplock @Resource = @p1, @LockOwner = 'Session'`
if _, err := ss.conn.ExecContext(context.Background(), query, aid); err != nil {
return &database.Error{OrigErr: err, Query: []byte(query)}
}
return nil
})
}
// Run the migrations for the database
func (ss *SQLServer) Run(migration io.Reader) error {
migr, err := io.ReadAll(migration)
if err != nil {
return err
}
// run migration
query := string(migr[:])
if _, err := ss.conn.ExecContext(context.Background(), query); err != nil {
if msErr, ok := err.(mssql.Error); ok {
message := fmt.Sprintf("migration failed: %s", msErr.Message)
if msErr.ProcName != "" {
message = fmt.Sprintf("%s (proc name %s)", msErr.Message, msErr.ProcName)
}
return database.Error{OrigErr: err, Err: message, Query: migr, Line: uint(msErr.LineNo)}
}
return database.Error{OrigErr: err, Err: "migration failed", Query: migr}
}
return nil
}
// SetVersion for the current database
func (ss *SQLServer) SetVersion(version int, dirty bool) error {
tx, err := ss.conn.BeginTx(context.Background(), &sql.TxOptions{})
if err != nil {
return &database.Error{OrigErr: err, Err: "transaction start failed"}
}
query := `TRUNCATE TABLE ` + ss.getMigrationTable()
if _, err := tx.Exec(query); err != nil {
if errRollback := tx.Rollback(); errRollback != nil {
err = multierror.Append(err, errRollback)
}
return &database.Error{OrigErr: err, Query: []byte(query)}
}
// Also re-write the schema version for nil dirty versions to prevent
// empty schema version for failed down migration on the first migration
// See: https://github.com/golang-migrate/migrate/issues/330
if version >= 0 || (version == database.NilVersion && dirty) {
var dirtyBit int
if dirty {
dirtyBit = 1
}
query = `INSERT INTO ` + ss.getMigrationTable() + ` (version, dirty) VALUES (@p1, @p2)`
if _, err := tx.Exec(query, version, dirtyBit); err != nil {
if errRollback := tx.Rollback(); errRollback != nil {
err = multierror.Append(err, errRollback)
}
return &database.Error{OrigErr: err, Query: []byte(query)}
}
}
if err := tx.Commit(); err != nil {
return &database.Error{OrigErr: err, Err: "transaction commit failed"}
}
return nil
}
// Version of the current database state
func (ss *SQLServer) Version() (version int, dirty bool, err error) {
query := `SELECT TOP 1 version, dirty FROM ` + ss.getMigrationTable()
err = ss.conn.QueryRowContext(context.Background(), query).Scan(&version, &dirty)
switch {
case err == sql.ErrNoRows:
return database.NilVersion, false, nil
case err != nil:
// FIXME: convert to MSSQL error
return 0, false, &database.Error{OrigErr: err, Query: []byte(query)}
default:
return version, dirty, nil
}
}
// Drop all tables from the database.
func (ss *SQLServer) Drop() error {
// drop all referential integrity constraints
query := `
DECLARE @Sql NVARCHAR(500) DECLARE @Cursor CURSOR
SET @Cursor = CURSOR FAST_FORWARD FOR
SELECT DISTINCT sql = 'ALTER TABLE [' + tc2.TABLE_NAME + '] DROP [' + rc1.CONSTRAINT_NAME + ']'
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc1
LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc2 ON tc2.CONSTRAINT_NAME =rc1.CONSTRAINT_NAME
OPEN @Cursor FETCH NEXT FROM @Cursor INTO @Sql
WHILE (@@FETCH_STATUS = 0)
BEGIN
Exec sp_executesql @Sql
FETCH NEXT FROM @Cursor INTO @Sql
END
CLOSE @Cursor DEALLOCATE @Cursor`
if _, err := ss.conn.ExecContext(context.Background(), query); err != nil {
return &database.Error{OrigErr: err, Query: []byte(query)}
}
// drop the tables
query = `EXEC sp_MSforeachtable 'DROP TABLE ?'`
if _, err := ss.conn.ExecContext(context.Background(), query); err != nil {
return &database.Error{OrigErr: err, Query: []byte(query)}
}
return nil
}
func (ss *SQLServer) ensureVersionTable() (err error) {
if err = ss.Lock(); err != nil {
return err
}
defer func() {
if e := ss.Unlock(); e != nil {
if err == nil {
err = e
} else {
err = multierror.Append(err, e)
}
}
}()
query := `IF NOT EXISTS
(SELECT *
FROM sysobjects
WHERE id = object_id(N'` + ss.getMigrationTable() + `')
AND OBJECTPROPERTY(id, N'IsUserTable') = 1
)
CREATE TABLE ` + ss.getMigrationTable() + ` ( version BIGINT PRIMARY KEY NOT NULL, dirty BIT NOT NULL );`
if _, err = ss.conn.ExecContext(context.Background(), query); err != nil {
return &database.Error{OrigErr: err, Query: []byte(query)}
}
return nil
}
func (ss *SQLServer) getMigrationTable() string {
return fmt.Sprintf("[%s].[%s]", ss.config.SchemaName, ss.config.MigrationsTable)
}
func getMSITokenProvider(resource string) (func() (string, error), error) {
msi, err := adal.NewServicePrincipalTokenFromManagedIdentity(resource, nil)
if err != nil {
return nil, err
}
return func() (string, error) {
err := msi.EnsureFresh()
if err != nil {
return "", err
}
token := msi.OAuthToken()
return token, nil
}, nil
}
// The sql server resource can change across clouds so get it
// dynamically based on the server uri.
// ex. <server name>.database.windows.net -> https://database.windows.net
func getAADResourceFromServerUri(purl *nurl.URL) string {
return fmt.Sprintf("%s%s", "https://", strings.Join(strings.Split(purl.Hostname(), ".")[1:], "."))
}
@@ -0,0 +1,341 @@
package sqlserver
import (
"context"
"database/sql"
sqldriver "database/sql/driver"
"fmt"
"log"
"runtime"
"strings"
"testing"
"time"
"github.com/dhui/dktest"
"github.com/docker/go-connections/nat"
"github.com/golang-migrate/migrate/v4"
dt "github.com/golang-migrate/migrate/v4/database/testing"
"github.com/golang-migrate/migrate/v4/dktesting"
_ "github.com/golang-migrate/migrate/v4/source/file"
)
const defaultPort = 1433
const saPassword = "Root1234"
var (
sqlEdgeOpts = dktest.Options{
Env: map[string]string{"ACCEPT_EULA": "Y", "MSSQL_SA_PASSWORD": saPassword},
PortBindings: map[nat.Port][]nat.PortBinding{
nat.Port(fmt.Sprintf("%d/tcp", defaultPort)): {
nat.PortBinding{
HostIP: "0.0.0.0",
HostPort: "0/tcp",
},
},
},
PortRequired: true, ReadyFunc: isReady, PullTimeout: 2 * time.Minute,
}
sqlServerOpts = dktest.Options{
Env: map[string]string{"ACCEPT_EULA": "Y", "MSSQL_SA_PASSWORD": saPassword, "MSSQL_PID": "Express"},
PortRequired: true, ReadyFunc: isReady, PullTimeout: 2 * time.Minute,
}
// Container versions: https://mcr.microsoft.com/v2/mssql/server/tags/list
specs = []dktesting.ContainerSpec{
{ImageName: "mcr.microsoft.com/azure-sql-edge:latest", Options: sqlEdgeOpts},
{ImageName: "mcr.microsoft.com/mssql/server:2017-latest", Options: sqlServerOpts},
{ImageName: "mcr.microsoft.com/mssql/server:2019-latest", Options: sqlServerOpts},
}
)
func msConnectionString(host, port string) string {
return fmt.Sprintf("sqlserver://sa:%v@%v:%v?database=master", saPassword, host, port)
}
func msConnectionStringMsiWithPassword(host, port string, useMsi bool) string {
return fmt.Sprintf("sqlserver://sa:%v@%v:%v?database=master&useMsi=%t", saPassword, host, port, useMsi)
}
func msConnectionStringMsi(host, port string, useMsi bool) string {
return fmt.Sprintf("sqlserver://sa@%v:%v?database=master&useMsi=%t", host, port, useMsi)
}
func isReady(ctx context.Context, c dktest.ContainerInfo) bool {
ip, port, err := c.Port(defaultPort)
if err != nil {
return false
}
uri := msConnectionString(ip, port)
db, err := sql.Open("sqlserver", uri)
if err != nil {
return false
}
defer func() {
if err := db.Close(); err != nil {
log.Println("close error:", err)
}
}()
if err = db.PingContext(ctx); err != nil {
switch err {
case sqldriver.ErrBadConn:
return false
default:
fmt.Println(err)
}
return false
}
return true
}
func SkipIfUnsupportedArch(t *testing.T, c dktest.ContainerInfo) {
if strings.Contains(c.ImageName, "mssql") && !strings.HasPrefix(runtime.GOARCH, "amd") {
t.Skipf("Image %s is not supported on arch %s", c.ImageName, runtime.GOARCH)
}
}
func Test(t *testing.T) {
t.Run("test", test)
t.Run("testMigrate", testMigrate)
t.Run("testMultiStatement", testMultiStatement)
t.Run("testErrorParsing", testErrorParsing)
t.Run("testLockWorks", testLockWorks)
t.Run("testMsiTrue", testMsiTrue)
t.Run("testOpenWithPasswordAndMSI", testOpenWithPasswordAndMSI)
t.Run("testMsiFalse", testMsiFalse)
t.Cleanup(func() {
for _, spec := range specs {
t.Log("Cleaning up ", spec.ImageName)
if err := spec.Cleanup(); err != nil {
t.Error("Error removing ", spec.ImageName, "error:", err)
}
}
})
}
func test(t *testing.T) {
dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) {
SkipIfUnsupportedArch(t, c)
ip, port, err := c.Port(defaultPort)
if err != nil {
t.Fatal(err)
}
addr := msConnectionString(ip, port)
p := &SQLServer{}
d, err := p.Open(addr)
if err != nil {
t.Fatalf("%v", err)
}
defer func() {
if err := d.Close(); err != nil {
t.Error(err)
}
}()
dt.Test(t, d, []byte("SELECT 1"))
})
}
func testMigrate(t *testing.T) {
dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) {
SkipIfUnsupportedArch(t, c)
ip, port, err := c.Port(defaultPort)
if err != nil {
t.Fatal(err)
}
addr := msConnectionString(ip, port)
p := &SQLServer{}
d, err := p.Open(addr)
if err != nil {
t.Fatalf("%v", err)
}
defer func() {
if err := d.Close(); err != nil {
t.Error(err)
}
}()
m, err := migrate.NewWithDatabaseInstance("file://./examples/migrations", "master", d)
if err != nil {
t.Fatal(err)
}
dt.TestMigrate(t, m)
})
}
func testMultiStatement(t *testing.T) {
dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) {
SkipIfUnsupportedArch(t, c)
ip, port, err := c.Port(defaultPort)
if err != nil {
t.Fatal(err)
}
addr := msConnectionString(ip, port)
ms := &SQLServer{}
d, err := ms.Open(addr)
if err != nil {
t.Fatal(err)
}
defer func() {
if err := d.Close(); err != nil {
t.Error(err)
}
}()
if err := d.Run(strings.NewReader("CREATE TABLE foo (foo text); CREATE TABLE bar (bar text);")); err != nil {
t.Fatalf("expected err to be nil, got %v", err)
}
// make sure second table exists
var exists int
if err := d.(*SQLServer).conn.QueryRowContext(context.Background(), "SELECT COUNT(1) FROM information_schema.tables WHERE table_name = 'bar' AND table_schema = (SELECT schema_name()) AND table_catalog = (SELECT db_name())").Scan(&exists); err != nil {
t.Fatal(err)
}
if exists != 1 {
t.Fatalf("expected table bar to exist")
}
})
}
func testErrorParsing(t *testing.T) {
dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) {
SkipIfUnsupportedArch(t, c)
ip, port, err := c.Port(defaultPort)
if err != nil {
t.Fatal(err)
}
addr := msConnectionString(ip, port)
p := &SQLServer{}
d, err := p.Open(addr)
if err != nil {
t.Fatal(err)
}
defer func() {
if err := d.Close(); err != nil {
t.Error(err)
}
}()
wantErr := `migration failed: Unknown object type 'TABLEE' used in a CREATE, DROP, or ALTER statement. in line 1:` +
` CREATE TABLE foo (foo text); CREATE TABLEE bar (bar text); (details: mssql: Unknown object type ` +
`'TABLEE' used in a CREATE, DROP, or ALTER statement.)`
if err := d.Run(strings.NewReader("CREATE TABLE foo (foo text); CREATE TABLEE bar (bar text);")); err == nil {
t.Fatal("expected err but got nil")
} else if err.Error() != wantErr {
t.Fatalf("expected '%s' but got '%s'", wantErr, err.Error())
}
})
}
func testLockWorks(t *testing.T) {
dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) {
SkipIfUnsupportedArch(t, c)
ip, port, err := c.Port(defaultPort)
if err != nil {
t.Fatal(err)
}
addr := fmt.Sprintf("sqlserver://sa:%v@%v:%v?master", saPassword, ip, port)
p := &SQLServer{}
d, err := p.Open(addr)
if err != nil {
t.Fatalf("%v", err)
}
dt.Test(t, d, []byte("SELECT 1"))
ms := d.(*SQLServer)
err = ms.Lock()
if err != nil {
t.Fatal(err)
}
err = ms.Unlock()
if err != nil {
t.Fatal(err)
}
// make sure the 2nd lock works (RELEASE_LOCK is very finicky)
err = ms.Lock()
if err != nil {
t.Fatal(err)
}
err = ms.Unlock()
if err != nil {
t.Fatal(err)
}
})
}
func testMsiTrue(t *testing.T) {
dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) {
SkipIfUnsupportedArch(t, c)
ip, port, err := c.Port(defaultPort)
if err != nil {
t.Fatal(err)
}
addr := msConnectionStringMsi(ip, port, true)
p := &SQLServer{}
_, err = p.Open(addr)
if err == nil {
t.Fatal("MSI should fail when not running in an Azure context.")
}
})
}
func testOpenWithPasswordAndMSI(t *testing.T) {
dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) {
SkipIfUnsupportedArch(t, c)
ip, port, err := c.Port(defaultPort)
if err != nil {
t.Fatal(err)
}
addr := msConnectionStringMsiWithPassword(ip, port, true)
p := &SQLServer{}
_, err = p.Open(addr)
if err == nil {
t.Fatal("Open should fail when both password and useMsi=true are passed.")
}
addr = msConnectionStringMsiWithPassword(ip, port, false)
p = &SQLServer{}
d, err := p.Open(addr)
if err != nil {
t.Fatal(err)
}
defer func() {
if err := d.Close(); err != nil {
t.Error(err)
}
}()
dt.Test(t, d, []byte("SELECT 1"))
})
}
func testMsiFalse(t *testing.T) {
dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) {
SkipIfUnsupportedArch(t, c)
ip, port, err := c.Port(defaultPort)
if err != nil {
t.Fatal(err)
}
addr := msConnectionStringMsi(ip, port, false)
p := &SQLServer{}
_, err = p.Open(addr)
if err == nil {
t.Fatal("Open should fail since no password was passed and useMsi is false.")
}
})
}