Skip to content

Commit 7c3eb3c

Browse files
authored
Merge pull request #1551 from ydb-platform/static-credentials
`sugar.DSN` refactoring + added test for static credentials
2 parents 00ad9c6 + 6a7e550 commit 7c3eb3c

File tree

5 files changed

+210
-34
lines changed

5 files changed

+210
-34
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
* Added `sugar.WithUserPassword(user,password)` option for `sugar.DSN()` helper
2+
* Added `sugar.WithSecure(bool)` option for `sugar.DSN()` helper
3+
* Small breaking change: `sugar.DSN` have only two required parameters (endpoint and database) from now on.
4+
Third parameter `secure` must be passed as option `sugar.WithSecure(bool)`
5+
16
## v3.92.0
27
* Added experimental ydb.ParamsFromMap and ydb.MustParamsFromMap for build query parameters
38
* Refactored coordination traces

internal/xerrors/operation.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ func (e *operationError) Type() Type {
181181
Ydb.StatusIds_UNDETERMINED,
182182
Ydb.StatusIds_SESSION_EXPIRED:
183183
return TypeConditionallyRetryable
184+
case Ydb.StatusIds_UNAUTHORIZED:
185+
return TypeNonRetryable
184186
default:
185187
return TypeUndefined
186188
}

sugar/dsn.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ package sugar
22

33
import "net/url"
44

5+
type dsnOption func(dsn *url.URL)
6+
57
// Usage of this package
68
//
79
// db, err := ydb.Open(ctx,
810
// sugar.DSN("endpoint", "database", false),
911
// )
1012

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

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

22-
if secure {
23-
dsn.Scheme = "grpcs"
24+
for _, opt := range opts {
25+
opt(&dsn)
2426
}
2527

2628
return dsn.String()
2729
}
30+
31+
func WithSecure(secure bool) dsnOption {
32+
return func(dsn *url.URL) {
33+
if secure {
34+
dsn.Scheme = "grpcs"
35+
} else {
36+
dsn.Scheme = "grpc"
37+
}
38+
}
39+
}
40+
41+
func WithUserPassword(user string, password string) dsnOption {
42+
return func(dsn *url.URL) {
43+
dsn.User = url.UserPassword(user, password)
44+
}
45+
}

sugar/dsn_test.go

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,53 +11,46 @@ import (
1111

1212
func TestDSN(t *testing.T) {
1313
for _, tt := range []struct {
14-
endpoint string
15-
database string
16-
secure bool
17-
dsn string
14+
act string
15+
exp string
1816
}{
1917
{
20-
"localhost:2135",
21-
"/local",
22-
false,
18+
DSN("localhost:2135", "/local"),
2319
"grpc://localhost:2135/local",
2420
},
2521
{
26-
"ydb-ru.yandex.net:2135",
27-
"/ru/home/gvit/mydb",
28-
false,
22+
DSN("localhost:2135", "/local", WithUserPassword("user", "")),
23+
"grpc://user@localhost:2135/local",
24+
},
25+
{
26+
DSN("localhost:2135", "/local", WithUserPassword("user", "password")),
27+
"grpc://user:password@localhost:2135/local",
28+
},
29+
{
30+
DSN("ydb-ru.yandex.net:2135", "/ru/home/gvit/mydb"),
2931
"grpc://ydb-ru.yandex.net:2135/ru/home/gvit/mydb",
3032
},
3133
{
32-
"ydb.serverless.yandexcloud.net:2135",
33-
"/ru-central1/b1g8skpblkos03malf3s/etn02qso4v3isjb00te1",
34-
true,
34+
DSN("ydb.serverless.yandexcloud.net:2135", "/ru-central1/b1g8skpblkos03malf3s/etn02qso4v3isjb00te1", WithSecure(true)), //nolint:lll
3535
"grpcs://ydb.serverless.yandexcloud.net:2135/ru-central1/b1g8skpblkos03malf3s/etn02qso4v3isjb00te1",
3636
},
3737
{
38-
"lb.etn03r9df42nb631unbv.ydb.mdb.yandexcloud.net:2135",
39-
"/ru-central1/b1g8skpblkos03malf3s/etn03r9df42nb631unbv",
40-
true,
38+
DSN("lb.etn03r9df42nb631unbv.ydb.mdb.yandexcloud.net:2135", "/ru-central1/b1g8skpblkos03malf3s/etn03r9df42nb631unbv", WithSecure(true)), //nolint:lll
4139
"grpcs://lb.etn03r9df42nb631unbv.ydb.mdb.yandexcloud.net:2135/ru-central1/b1g8skpblkos03malf3s/etn03r9df42nb631unbv",
4240
},
4341
} {
44-
t.Run(tt.dsn, func(t *testing.T) {
45-
s := DSN(tt.endpoint, tt.database, tt.secure)
46-
if s != tt.dsn {
47-
t.Fatalf("Unexpected result: %s, exp: %s", s, tt.dsn)
48-
}
49-
info, err := dsn.Parse(s)
50-
if err != nil {
51-
t.Fatalf("")
52-
}
53-
lhs, rhs := config.New(info.Options...), config.New(
54-
config.WithSecure(tt.secure),
55-
config.WithEndpoint(tt.endpoint),
56-
config.WithDatabase(tt.database),
57-
)
42+
t.Run(tt.exp, func(t *testing.T) {
43+
act, err := dsn.Parse(tt.act)
44+
require.NoError(t, err)
45+
exp, err := dsn.Parse(tt.act)
46+
require.NoError(t, err)
47+
require.Equal(t, exp.Params, act.Params)
48+
require.Equal(t, exp.UserInfo, act.UserInfo)
49+
lhs, rhs := config.New(act.Options...), config.New(exp.Options...)
5850
require.Equal(t, lhs.Endpoint(), rhs.Endpoint())
5951
require.Equal(t, lhs.Database(), rhs.Database())
6052
require.Equal(t, lhs.Secure(), rhs.Secure())
53+
require.Equal(t, lhs.Credentials(), rhs.Credentials())
6154
})
6255
}
6356
}

tests/integration/driver_test.go

Lines changed: 159 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import (
77
"context"
88
"crypto/tls"
99
"fmt"
10+
"net/url"
1011
"os"
12+
"path"
1113
"testing"
1214
"time"
1315

@@ -28,10 +30,14 @@ import (
2830

2931
"github.com/ydb-platform/ydb-go-sdk/v3"
3032
"github.com/ydb-platform/ydb-go-sdk/v3/config"
33+
"github.com/ydb-platform/ydb-go-sdk/v3/internal/credentials"
3134
"github.com/ydb-platform/ydb-go-sdk/v3/internal/meta"
35+
"github.com/ydb-platform/ydb-go-sdk/v3/internal/version"
3236
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xtest"
3337
"github.com/ydb-platform/ydb-go-sdk/v3/log"
3438
"github.com/ydb-platform/ydb-go-sdk/v3/retry"
39+
"github.com/ydb-platform/ydb-go-sdk/v3/table"
40+
"github.com/ydb-platform/ydb-go-sdk/v3/table/options"
3541
"github.com/ydb-platform/ydb-go-sdk/v3/table/result/indexed"
3642
"github.com/ydb-platform/ydb-go-sdk/v3/trace"
3743
)
@@ -165,7 +171,159 @@ func TestDriver(sourceTest *testing.T) {
165171
t.Fatalf("close failed: %+v", e)
166172
}
167173
}()
168-
t.Run("With", func(t *testing.T) {
174+
t.RunSynced("WithStaticCredentials", func(t *xtest.SyncedTest) {
175+
if version.Lt(os.Getenv("YDB_VERSION"), "24.1") {
176+
t.Skip("read rows not allowed in YDB version '" + os.Getenv("YDB_VERSION") + "'")
177+
}
178+
db, err := ydb.Open(ctx,
179+
os.Getenv("YDB_CONNECTION_STRING"),
180+
ydb.WithAccessTokenCredentials(
181+
os.Getenv("YDB_ACCESS_TOKEN_CREDENTIALS"),
182+
),
183+
)
184+
require.NoError(t, err)
185+
defer func() {
186+
_ = db.Close(ctx)
187+
}()
188+
err = db.Query().Exec(ctx, `DROP USER IF EXISTS test`)
189+
require.NoError(t, err)
190+
err = db.Query().Exec(ctx, "CREATE USER test PASSWORD 'password'; ALTER GROUP `ADMINS` ADD USER test;")
191+
require.NoError(t, err)
192+
defer func() {
193+
err = db.Query().Exec(ctx, `DROP USER test`)
194+
require.NoError(t, err)
195+
}()
196+
t.RunSynced("UsingConnectionString", func(t *xtest.SyncedTest) {
197+
t.RunSynced("HappyWay", func(t *xtest.SyncedTest) {
198+
u, err := url.Parse(os.Getenv("YDB_CONNECTION_STRING"))
199+
require.NoError(t, err)
200+
u.User = url.UserPassword("test", "password")
201+
t.Log(u.String())
202+
db, err := ydb.Open(ctx, u.String())
203+
require.NoError(t, err)
204+
defer func() {
205+
_ = db.Close(ctx)
206+
}()
207+
row, err := db.Query().QueryRow(ctx, `SELECT 1`)
208+
require.NoError(t, err)
209+
var v int
210+
err = row.Scan(&v)
211+
require.NoError(t, err)
212+
tableName := path.Join(db.Name(), t.Name(), "test")
213+
t.RunSynced("CreateTable", func(t *xtest.SyncedTest) {
214+
err := db.Query().Exec(ctx, fmt.Sprintf(`
215+
CREATE TABLE IF NOT EXISTS %s (
216+
id Uint64,
217+
value Utf8,
218+
PRIMARY KEY (id)
219+
)`, "`"+tableName+"`"),
220+
)
221+
require.NoError(t, err)
222+
})
223+
t.RunSynced("DescribeTable", func(t *xtest.SyncedTest) {
224+
var d options.Description
225+
err := db.Table().Do(ctx, func(ctx context.Context, s table.Session) error {
226+
d, err = s.DescribeTable(ctx, tableName)
227+
if err != nil {
228+
return err
229+
}
230+
231+
return nil
232+
})
233+
require.NoError(t, err)
234+
require.Equal(t, "test", d.Name)
235+
require.Equal(t, 2, len(d.Columns))
236+
require.Equal(t, "id", d.Columns[0].Name)
237+
require.Equal(t, "value", d.Columns[1].Name)
238+
require.Equal(t, []string{"id"}, d.PrimaryKey)
239+
})
240+
})
241+
t.RunSynced("WrongLogin", func(t *xtest.SyncedTest) {
242+
u, err := url.Parse(os.Getenv("YDB_CONNECTION_STRING"))
243+
require.NoError(t, err)
244+
u.User = url.UserPassword("wrong_login", "password")
245+
db, err := ydb.Open(ctx, u.String())
246+
require.Error(t, err)
247+
require.Nil(t, db)
248+
require.True(t, credentials.IsAccessError(err))
249+
})
250+
t.RunSynced("WrongPassword", func(t *xtest.SyncedTest) {
251+
u, err := url.Parse(os.Getenv("YDB_CONNECTION_STRING"))
252+
require.NoError(t, err)
253+
u.User = url.UserPassword("test", "wrong_password")
254+
db, err := ydb.Open(ctx, u.String())
255+
require.Error(t, err)
256+
require.Nil(t, db)
257+
require.True(t, credentials.IsAccessError(err))
258+
})
259+
})
260+
t.RunSynced("UsingExplicitStaticCredentials", func(t *xtest.SyncedTest) {
261+
t.RunSynced("HappyWay", func(t *xtest.SyncedTest) {
262+
db, err := ydb.Open(ctx,
263+
os.Getenv("YDB_CONNECTION_STRING"),
264+
ydb.WithStaticCredentials("test", "password"),
265+
)
266+
require.NoError(t, err)
267+
defer func() {
268+
_ = db.Close(ctx)
269+
}()
270+
tableName := path.Join(db.Name(), t.Name(), "test")
271+
t.RunSynced("CreateTable", func(t *xtest.SyncedTest) {
272+
err := db.Query().Exec(ctx, fmt.Sprintf(`
273+
CREATE TABLE IF NOT EXISTS %s (
274+
id Uint64,
275+
value Utf8,
276+
PRIMARY KEY (id)
277+
)`, "`"+tableName+"`"),
278+
)
279+
require.NoError(t, err)
280+
})
281+
t.RunSynced("Query", func(t *xtest.SyncedTest) {
282+
row, err := db.Query().QueryRow(ctx, `SELECT 1`)
283+
require.NoError(t, err)
284+
var v int
285+
err = row.Scan(&v)
286+
require.NoError(t, err)
287+
})
288+
t.RunSynced("DescribeTable", func(t *xtest.SyncedTest) {
289+
var d options.Description
290+
err := db.Table().Do(ctx, func(ctx context.Context, s table.Session) error {
291+
d, err = s.DescribeTable(ctx, tableName)
292+
if err != nil {
293+
return err
294+
}
295+
296+
return nil
297+
})
298+
require.NoError(t, err)
299+
require.Equal(t, "test", d.Name)
300+
require.Equal(t, 2, len(d.Columns))
301+
require.Equal(t, "id", d.Columns[0].Name)
302+
require.Equal(t, "value", d.Columns[1].Name)
303+
require.Equal(t, []string{"id"}, d.PrimaryKey)
304+
})
305+
})
306+
t.RunSynced("WrongLogin", func(t *xtest.SyncedTest) {
307+
db, err := ydb.Open(ctx,
308+
os.Getenv("YDB_CONNECTION_STRING"),
309+
ydb.WithStaticCredentials("wrong_user", "password"),
310+
)
311+
require.Error(t, err)
312+
require.Nil(t, db)
313+
require.True(t, credentials.IsAccessError(err))
314+
})
315+
t.RunSynced("WrongPassword", func(t *xtest.SyncedTest) {
316+
db, err := ydb.Open(ctx,
317+
os.Getenv("YDB_CONNECTION_STRING"),
318+
ydb.WithStaticCredentials("test", "wrong_password"),
319+
)
320+
require.Error(t, err)
321+
require.Nil(t, db)
322+
require.True(t, credentials.IsAccessError(err))
323+
})
324+
})
325+
})
326+
t.RunSynced("With", func(t *xtest.SyncedTest) {
169327
t.Run("WithSharedBalancer", func(t *testing.T) {
170328
child, err := db.With(ctx, ydb.WithSharedBalancer(db))
171329
require.NoError(t, err)

0 commit comments

Comments
 (0)