Skip to content

Commit

Permalink
Merge pull request #1551 from ydb-platform/static-credentials
Browse files Browse the repository at this point in the history
`sugar.DSN` refactoring + added test for static credentials
  • Loading branch information
asmyasnikov authored Nov 13, 2024
2 parents 00ad9c6 + 6a7e550 commit 7c3eb3c
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 34 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
* Added `sugar.WithUserPassword(user,password)` option for `sugar.DSN()` helper
* Added `sugar.WithSecure(bool)` option for `sugar.DSN()` helper
* Small breaking change: `sugar.DSN` have only two required parameters (endpoint and database) from now on.
Third parameter `secure` must be passed as option `sugar.WithSecure(bool)`

## v3.92.0
* Added experimental ydb.ParamsFromMap and ydb.MustParamsFromMap for build query parameters
* Refactored coordination traces
Expand Down
2 changes: 2 additions & 0 deletions internal/xerrors/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ func (e *operationError) Type() Type {
Ydb.StatusIds_UNDETERMINED,
Ydb.StatusIds_SESSION_EXPIRED:
return TypeConditionallyRetryable
case Ydb.StatusIds_UNAUTHORIZED:
return TypeNonRetryable
default:
return TypeUndefined
}
Expand Down
24 changes: 21 additions & 3 deletions sugar/dsn.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ package sugar

import "net/url"

type dsnOption func(dsn *url.URL)

// Usage of this package
//
// db, err := ydb.Open(ctx,
// sugar.DSN("endpoint", "database", false),
// )

// DSN makes connection string (data source name) by endpoint, database and secure
func DSN(endpoint, database string, secure bool) (s string) {
func DSN(endpoint, database string, opts ...dsnOption) (s string) {
qp := url.Values{}

dsn := url.URL{
Expand All @@ -19,9 +21,25 @@ func DSN(endpoint, database string, secure bool) (s string) {
RawQuery: qp.Encode(),
}

if secure {
dsn.Scheme = "grpcs"
for _, opt := range opts {
opt(&dsn)
}

return dsn.String()
}

func WithSecure(secure bool) dsnOption {
return func(dsn *url.URL) {
if secure {
dsn.Scheme = "grpcs"
} else {
dsn.Scheme = "grpc"
}
}
}

func WithUserPassword(user string, password string) dsnOption {
return func(dsn *url.URL) {
dsn.User = url.UserPassword(user, password)
}
}
53 changes: 23 additions & 30 deletions sugar/dsn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,53 +11,46 @@ import (

func TestDSN(t *testing.T) {
for _, tt := range []struct {
endpoint string
database string
secure bool
dsn string
act string
exp string
}{
{
"localhost:2135",
"/local",
false,
DSN("localhost:2135", "/local"),
"grpc://localhost:2135/local",
},
{
"ydb-ru.yandex.net:2135",
"/ru/home/gvit/mydb",
false,
DSN("localhost:2135", "/local", WithUserPassword("user", "")),
"grpc://user@localhost:2135/local",
},
{
DSN("localhost:2135", "/local", WithUserPassword("user", "password")),
"grpc://user:password@localhost:2135/local",
},
{
DSN("ydb-ru.yandex.net:2135", "/ru/home/gvit/mydb"),
"grpc://ydb-ru.yandex.net:2135/ru/home/gvit/mydb",
},
{
"ydb.serverless.yandexcloud.net:2135",
"/ru-central1/b1g8skpblkos03malf3s/etn02qso4v3isjb00te1",
true,
DSN("ydb.serverless.yandexcloud.net:2135", "/ru-central1/b1g8skpblkos03malf3s/etn02qso4v3isjb00te1", WithSecure(true)), //nolint:lll
"grpcs://ydb.serverless.yandexcloud.net:2135/ru-central1/b1g8skpblkos03malf3s/etn02qso4v3isjb00te1",
},
{
"lb.etn03r9df42nb631unbv.ydb.mdb.yandexcloud.net:2135",
"/ru-central1/b1g8skpblkos03malf3s/etn03r9df42nb631unbv",
true,
DSN("lb.etn03r9df42nb631unbv.ydb.mdb.yandexcloud.net:2135", "/ru-central1/b1g8skpblkos03malf3s/etn03r9df42nb631unbv", WithSecure(true)), //nolint:lll
"grpcs://lb.etn03r9df42nb631unbv.ydb.mdb.yandexcloud.net:2135/ru-central1/b1g8skpblkos03malf3s/etn03r9df42nb631unbv",
},
} {
t.Run(tt.dsn, func(t *testing.T) {
s := DSN(tt.endpoint, tt.database, tt.secure)
if s != tt.dsn {
t.Fatalf("Unexpected result: %s, exp: %s", s, tt.dsn)
}
info, err := dsn.Parse(s)
if err != nil {
t.Fatalf("")
}
lhs, rhs := config.New(info.Options...), config.New(
config.WithSecure(tt.secure),
config.WithEndpoint(tt.endpoint),
config.WithDatabase(tt.database),
)
t.Run(tt.exp, func(t *testing.T) {
act, err := dsn.Parse(tt.act)
require.NoError(t, err)
exp, err := dsn.Parse(tt.act)
require.NoError(t, err)
require.Equal(t, exp.Params, act.Params)
require.Equal(t, exp.UserInfo, act.UserInfo)
lhs, rhs := config.New(act.Options...), config.New(exp.Options...)
require.Equal(t, lhs.Endpoint(), rhs.Endpoint())
require.Equal(t, lhs.Database(), rhs.Database())
require.Equal(t, lhs.Secure(), rhs.Secure())
require.Equal(t, lhs.Credentials(), rhs.Credentials())
})
}
}
160 changes: 159 additions & 1 deletion tests/integration/driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import (
"context"
"crypto/tls"
"fmt"
"net/url"
"os"
"path"
"testing"
"time"

Expand All @@ -28,10 +30,14 @@ import (

"github.com/ydb-platform/ydb-go-sdk/v3"
"github.com/ydb-platform/ydb-go-sdk/v3/config"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/credentials"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/meta"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/version"
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
"github.com/ydb-platform/ydb-go-sdk/v3/log"
"github.com/ydb-platform/ydb-go-sdk/v3/retry"
"github.com/ydb-platform/ydb-go-sdk/v3/table"
"github.com/ydb-platform/ydb-go-sdk/v3/table/options"
"github.com/ydb-platform/ydb-go-sdk/v3/table/result/indexed"
"github.com/ydb-platform/ydb-go-sdk/v3/trace"
)
Expand Down Expand Up @@ -165,7 +171,159 @@ func TestDriver(sourceTest *testing.T) {
t.Fatalf("close failed: %+v", e)
}
}()
t.Run("With", func(t *testing.T) {
t.RunSynced("WithStaticCredentials", func(t *xtest.SyncedTest) {
if version.Lt(os.Getenv("YDB_VERSION"), "24.1") {
t.Skip("read rows not allowed in YDB version '" + os.Getenv("YDB_VERSION") + "'")
}
db, err := ydb.Open(ctx,
os.Getenv("YDB_CONNECTION_STRING"),
ydb.WithAccessTokenCredentials(
os.Getenv("YDB_ACCESS_TOKEN_CREDENTIALS"),
),
)
require.NoError(t, err)
defer func() {
_ = db.Close(ctx)
}()
err = db.Query().Exec(ctx, `DROP USER IF EXISTS test`)
require.NoError(t, err)
err = db.Query().Exec(ctx, "CREATE USER test PASSWORD 'password'; ALTER GROUP `ADMINS` ADD USER test;")
require.NoError(t, err)
defer func() {
err = db.Query().Exec(ctx, `DROP USER test`)
require.NoError(t, err)
}()
t.RunSynced("UsingConnectionString", func(t *xtest.SyncedTest) {
t.RunSynced("HappyWay", func(t *xtest.SyncedTest) {
u, err := url.Parse(os.Getenv("YDB_CONNECTION_STRING"))
require.NoError(t, err)
u.User = url.UserPassword("test", "password")
t.Log(u.String())
db, err := ydb.Open(ctx, u.String())
require.NoError(t, err)
defer func() {
_ = db.Close(ctx)
}()
row, err := db.Query().QueryRow(ctx, `SELECT 1`)
require.NoError(t, err)
var v int
err = row.Scan(&v)
require.NoError(t, err)
tableName := path.Join(db.Name(), t.Name(), "test")
t.RunSynced("CreateTable", func(t *xtest.SyncedTest) {
err := db.Query().Exec(ctx, fmt.Sprintf(`
CREATE TABLE IF NOT EXISTS %s (
id Uint64,
value Utf8,
PRIMARY KEY (id)
)`, "`"+tableName+"`"),
)
require.NoError(t, err)
})
t.RunSynced("DescribeTable", func(t *xtest.SyncedTest) {
var d options.Description
err := db.Table().Do(ctx, func(ctx context.Context, s table.Session) error {
d, err = s.DescribeTable(ctx, tableName)
if err != nil {
return err
}

return nil
})
require.NoError(t, err)
require.Equal(t, "test", d.Name)
require.Equal(t, 2, len(d.Columns))
require.Equal(t, "id", d.Columns[0].Name)
require.Equal(t, "value", d.Columns[1].Name)
require.Equal(t, []string{"id"}, d.PrimaryKey)
})
})
t.RunSynced("WrongLogin", func(t *xtest.SyncedTest) {
u, err := url.Parse(os.Getenv("YDB_CONNECTION_STRING"))
require.NoError(t, err)
u.User = url.UserPassword("wrong_login", "password")
db, err := ydb.Open(ctx, u.String())
require.Error(t, err)
require.Nil(t, db)
require.True(t, credentials.IsAccessError(err))
})
t.RunSynced("WrongPassword", func(t *xtest.SyncedTest) {
u, err := url.Parse(os.Getenv("YDB_CONNECTION_STRING"))
require.NoError(t, err)
u.User = url.UserPassword("test", "wrong_password")
db, err := ydb.Open(ctx, u.String())
require.Error(t, err)
require.Nil(t, db)
require.True(t, credentials.IsAccessError(err))
})
})
t.RunSynced("UsingExplicitStaticCredentials", func(t *xtest.SyncedTest) {
t.RunSynced("HappyWay", func(t *xtest.SyncedTest) {
db, err := ydb.Open(ctx,
os.Getenv("YDB_CONNECTION_STRING"),
ydb.WithStaticCredentials("test", "password"),
)
require.NoError(t, err)
defer func() {
_ = db.Close(ctx)
}()
tableName := path.Join(db.Name(), t.Name(), "test")
t.RunSynced("CreateTable", func(t *xtest.SyncedTest) {
err := db.Query().Exec(ctx, fmt.Sprintf(`
CREATE TABLE IF NOT EXISTS %s (
id Uint64,
value Utf8,
PRIMARY KEY (id)
)`, "`"+tableName+"`"),
)
require.NoError(t, err)
})
t.RunSynced("Query", func(t *xtest.SyncedTest) {
row, err := db.Query().QueryRow(ctx, `SELECT 1`)
require.NoError(t, err)
var v int
err = row.Scan(&v)
require.NoError(t, err)
})
t.RunSynced("DescribeTable", func(t *xtest.SyncedTest) {
var d options.Description
err := db.Table().Do(ctx, func(ctx context.Context, s table.Session) error {
d, err = s.DescribeTable(ctx, tableName)
if err != nil {
return err
}

return nil
})
require.NoError(t, err)
require.Equal(t, "test", d.Name)
require.Equal(t, 2, len(d.Columns))
require.Equal(t, "id", d.Columns[0].Name)
require.Equal(t, "value", d.Columns[1].Name)
require.Equal(t, []string{"id"}, d.PrimaryKey)
})
})
t.RunSynced("WrongLogin", func(t *xtest.SyncedTest) {
db, err := ydb.Open(ctx,
os.Getenv("YDB_CONNECTION_STRING"),
ydb.WithStaticCredentials("wrong_user", "password"),
)
require.Error(t, err)
require.Nil(t, db)
require.True(t, credentials.IsAccessError(err))
})
t.RunSynced("WrongPassword", func(t *xtest.SyncedTest) {
db, err := ydb.Open(ctx,
os.Getenv("YDB_CONNECTION_STRING"),
ydb.WithStaticCredentials("test", "wrong_password"),
)
require.Error(t, err)
require.Nil(t, db)
require.True(t, credentials.IsAccessError(err))
})
})
})
t.RunSynced("With", func(t *xtest.SyncedTest) {
t.Run("WithSharedBalancer", func(t *testing.T) {
child, err := db.With(ctx, ydb.WithSharedBalancer(db))
require.NoError(t, err)
Expand Down

0 comments on commit 7c3eb3c

Please sign in to comment.