diff --git a/Makefile b/Makefile index 8e23a43c7..7c9535327 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ SOURCE ?= file go_bindata github github_ee bitbucket aws_s3 google_cloud_storage godoc_vfs gitlab -DATABASE ?= postgres mysql redshift cassandra spanner cockroachdb yugabytedb clickhouse mongodb sqlserver firebird neo4j pgx pgx5 rqlite +DATABASE ?= postgres mysql redshift cassandra spanner cockroachdb yugabytedb clickhouse mongodb sqlserver firebird neo4j pgx pgx5 rqlite ydb DATABASE_TEST ?= $(DATABASE) sqlite sqlite3 sqlcipher VERSION ?= $(shell git describe --tags 2>/dev/null | cut -c 2-) TEST_FLAGS ?= diff --git a/README.md b/README.md index a79cc7b76..16c2e1565 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ Database drivers run migrations. [Add a new database?](database/driver.go) * [Firebird](database/firebird) * [MS SQL Server](database/sqlserver) * [rqlite](database/rqlite) +* [YDB](database/ydb) ### Database URLs diff --git a/database/ydb/README.md b/database/ydb/README.md new file mode 100644 index 000000000..3d66356fd --- /dev/null +++ b/database/ydb/README.md @@ -0,0 +1,40 @@ +# [YDB](https://ydb.tech/docs/) + +`ydb://[user:password@]host:port/database?QUERY_PARAMS` + +| URL Query | Description | +|:----------:|:---------------------------------------:| +| `user` | The user to sign in as. | +| `password` | The user's password. | +| `host` | The host to connect to. | +| `port` | The port to bind to. | +| `database` | The name of the database to connect to. | + +| URL Query Params | Description | +|:----------------------------:|:--------------------------------------------------------------------------------------------:| +| `x-auth-token` | Authentication token. | +| `x-migrations-table` | Name of the migrations table (default `schema_migrations`). | +| `x-use-grpcs` | Enables gRPCS protocol for YDB connections (default grpc). | +| `x-tls-ca` | The location of the CA (certificate authority) file. | +| `x-tls-insecure-skip-verify` | Controls whether a client verifies the server's certificate chain and host name. | +| `x-tls-min-version` | Controls the minimum TLS version that is acceptable, use 1.0, 1.1, 1.2 or 1.3 (default 1.2). | + +### Secure connection + +Query param `x-use-grpcs` enables secure TLS connection that requires certificates. +You can declare root certificate using ENV +variable: `export YDB_SSL_ROOT_CERTIFICATES_FILE=/path/to/ydb/certs/CA.pem` or +by using `x-tls-ca` query param: `?x-tls-ca=/path/to/ydb/certs/CA.pem`. + +### Authentication + +By default, golang-migrate connects to YDB +using [anonymous credentials](https://ydb.tech/docs/en/recipes/ydb-sdk/auth-anonymous). \ +Through the url query, you can change the default behavior: + +- To connect to YDB using [static credentials](https://ydb.tech/docs/en/recipes/ydb-sdk/auth-static) you need to specify + username and password: + `ydb://user:password@host:port/database` +- To connect to YDB using [token](https://ydb.tech/docs/en/recipes/ydb-sdk/auth-access-token) you need to specify token + as query parameter: + `ydb://host:port/database?x-auth-token=` \ No newline at end of file diff --git a/database/ydb/examples/migrations/001_create_users.down.yql b/database/ydb/examples/migrations/001_create_users.down.yql new file mode 100644 index 000000000..724e24df1 --- /dev/null +++ b/database/ydb/examples/migrations/001_create_users.down.yql @@ -0,0 +1 @@ +DROP TABLE `test/users`; diff --git a/database/ydb/examples/migrations/001_create_users.up.yql b/database/ydb/examples/migrations/001_create_users.up.yql new file mode 100644 index 000000000..32b13ed6e --- /dev/null +++ b/database/ydb/examples/migrations/001_create_users.up.yql @@ -0,0 +1,6 @@ +CREATE TABLE `test/users` ( + id Uint64, + name String, + email String, + PRIMARY KEY (id) +); diff --git a/database/ydb/examples/migrations/002_add_city_to_users.down.yql b/database/ydb/examples/migrations/002_add_city_to_users.down.yql new file mode 100644 index 000000000..7ff5b3ceb --- /dev/null +++ b/database/ydb/examples/migrations/002_add_city_to_users.down.yql @@ -0,0 +1,4 @@ +DROP TABLE `test/cities`; + +ALTER TABLE `test/users` + DROP COLUMN city; diff --git a/database/ydb/examples/migrations/002_add_city_to_users.up.yql b/database/ydb/examples/migrations/002_add_city_to_users.up.yql new file mode 100644 index 000000000..83d322967 --- /dev/null +++ b/database/ydb/examples/migrations/002_add_city_to_users.up.yql @@ -0,0 +1,8 @@ +CREATE TABLE `test/cities` ( + id Uint64, + name String, + PRIMARY KEY (id) +); + +ALTER TABLE `test/users` + ADD COLUMN city Uint64; diff --git a/database/ydb/examples/migrations/003_create_topic.down.yql b/database/ydb/examples/migrations/003_create_topic.down.yql new file mode 100644 index 000000000..c73aaf379 --- /dev/null +++ b/database/ydb/examples/migrations/003_create_topic.down.yql @@ -0,0 +1 @@ +DROP TOPIC `test/topic`; diff --git a/database/ydb/examples/migrations/003_create_topic.up.yql b/database/ydb/examples/migrations/003_create_topic.up.yql new file mode 100644 index 000000000..70dddf256 --- /dev/null +++ b/database/ydb/examples/migrations/003_create_topic.up.yql @@ -0,0 +1 @@ +CREATE TOPIC `test/topic`; diff --git a/database/ydb/ydb.go b/database/ydb/ydb.go new file mode 100644 index 000000000..ec46839cd --- /dev/null +++ b/database/ydb/ydb.go @@ -0,0 +1,330 @@ +package ydb + +import ( + "context" + "crypto/tls" + "database/sql" + "fmt" + "io" + "net/url" + "sync/atomic" + + "github.com/hashicorp/go-multierror" + "github.com/ydb-platform/ydb-go-sdk/v3" + + "github.com/golang-migrate/migrate/v4" + "github.com/golang-migrate/migrate/v4/database" +) + +func init() { + database.Register("ydb", &YDB{}) +} + +const ( + defaultMigrationsTable = "schema_migrations" + + queryParamAuthToken = "x-auth-token" + queryParamMigrationsTable = "x-migrations-table" + queryParamUseGRPCS = "x-use-grpcs" + queryParamTLSCertificateAuthorities = "x-tls-ca" + queryParamTLSInsecureSkipVerify = "x-tls-insecure-skip-verify" + queryParamTLSMinVersion = "x-tls-min-version" +) + +var ( + ErrNilConfig = fmt.Errorf("no config") + ErrNoDatabaseName = fmt.Errorf("no database name") + ErrUnsupportedTLSVersion = fmt.Errorf("unsupported tls version: use 1.0, 1.1, 1,2 or 1.3") +) + +type Config struct { + MigrationsTable string +} + +type YDB struct { + // locking and unlocking need to use the same connection + conn *sql.Conn + db *sql.DB + isLocked atomic.Bool + + config *Config +} + +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 len(config.MigrationsTable) == 0 { + config.MigrationsTable = defaultMigrationsTable + } + + conn, err := instance.Conn(context.TODO()) + if err != nil { + return nil, err + } + + db := &YDB{ + conn: conn, + db: instance, + config: config, + } + if err = db.ensureVersionTable(); err != nil { + return nil, err + } + return db, nil +} + +func (y *YDB) Open(dsn string) (database.Driver, error) { + purl, err := url.Parse(dsn) + if err != nil { + return nil, err + } + + if len(purl.Path) == 0 { + return nil, ErrNoDatabaseName + } + + pquery, err := url.ParseQuery(purl.RawQuery) + if err != nil { + return nil, err + } + + switch { + case pquery.Has(queryParamUseGRPCS): + purl.Scheme = "grpcs" + default: + purl.Scheme = "grpc" + } + + purl = migrate.FilterCustomQuery(purl) + + credentials := y.parseCredentialsOptions(purl, pquery) + tlsOptions, err := y.parseTLSOptions(purl, pquery) + if err != nil { + return nil, err + } + + nativeDriver, err := ydb.Open(context.TODO(), purl.String(), append(tlsOptions, credentials)...) + if err != nil { + return nil, err + } + + connector, err := ydb.Connector(nativeDriver, + ydb.WithQueryService(true), + ) + if err != nil { + return nil, err + } + + db, err := WithInstance(sql.OpenDB(connector), &Config{ + MigrationsTable: pquery.Get(queryParamMigrationsTable), + }) + if err != nil { + return nil, err + } + + return db, nil +} + +func (y *YDB) parseCredentialsOptions(url *url.URL, query url.Values) (credentials ydb.Option) { + switch { + case query.Has(queryParamAuthToken): + credentials = ydb.WithAccessTokenCredentials(query.Get(queryParamAuthToken)) + case url.User != nil: + user := url.User.Username() + password, _ := url.User.Password() + credentials = ydb.WithStaticCredentials(user, password) + default: + credentials = ydb.WithAnonymousCredentials() + } + url.User = nil + return credentials +} + +func (y *YDB) parseTLSOptions(_ *url.URL, query url.Values) (options []ydb.Option, err error) { + if query.Has(queryParamTLSCertificateAuthorities) { + options = append(options, ydb.WithCertificatesFromFile(query.Get(queryParamTLSCertificateAuthorities))) + } + if query.Has(queryParamTLSInsecureSkipVerify) { + options = append(options, ydb.WithTLSSInsecureSkipVerify()) + } + if query.Has(queryParamTLSMinVersion) { + switch query.Get(queryParamTLSMinVersion) { + case "1.0": + options = append(options, ydb.WithMinTLSVersion(tls.VersionTLS10)) + case "1.1": + options = append(options, ydb.WithMinTLSVersion(tls.VersionTLS11)) + case "1.2": + options = append(options, ydb.WithMinTLSVersion(tls.VersionTLS12)) + case "1.3": + options = append(options, ydb.WithMinTLSVersion(tls.VersionTLS13)) + default: + return nil, ErrUnsupportedTLSVersion + } + } + return options, nil +} + +func (y *YDB) Close() error { + connErr := y.conn.Close() + var dbErr error + if y.db != nil { + dbErr = y.db.Close() + } + if connErr != nil || dbErr != nil { + return fmt.Errorf("conn: %v, db: %v", connErr, dbErr) + } + return nil +} + +func (y *YDB) Run(migration io.Reader) error { + rawMigrations, err := io.ReadAll(migration) + if err != nil { + return err + } + + if _, err = y.conn.ExecContext(ydb.WithQueryMode(context.TODO(), ydb.SchemeQueryMode), string(rawMigrations)); err != nil { + return database.Error{OrigErr: err, Err: "migration failed", Query: rawMigrations} + } + return nil +} + +func (y *YDB) SetVersion(version int, dirty bool) error { + deleteVersionQuery := fmt.Sprintf(` + DELETE FROM %s + `, y.config.MigrationsTable) + + insertVersionQuery := fmt.Sprintf(` + INSERT INTO %s (version, dirty, created) VALUES (%d, %t, CurrentUtcTimestamp()) + `, y.config.MigrationsTable, version, dirty) + + tx, err := y.conn.BeginTx(context.TODO(), &sql.TxOptions{}) + if err != nil { + return &database.Error{OrigErr: err, Err: "transaction start failed"} + } + + if _, err := tx.Exec(deleteVersionQuery); err != nil { + if errRollback := tx.Rollback(); errRollback != nil { + err = multierror.Append(err, errRollback) + } + return &database.Error{OrigErr: err, Query: []byte(deleteVersionQuery)} + } + + // 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) { + if _, err := tx.Exec(insertVersionQuery, version, dirty); err != nil { + if errRollback := tx.Rollback(); errRollback != nil { + err = multierror.Append(err, errRollback) + } + return &database.Error{OrigErr: err, Query: []byte(insertVersionQuery)} + } + } + + if err := tx.Commit(); err != nil { + return &database.Error{OrigErr: err, Err: "transaction commit failed"} + } + return err +} + +func (y *YDB) Version() (version int, dirty bool, err error) { + getVersionQuery := fmt.Sprintf(` + SELECT version, dirty FROM %s LIMIT 1 + `, y.config.MigrationsTable) + + var v uint64 + err = y.conn.QueryRowContext(context.TODO(), getVersionQuery).Scan(&v, &dirty) + switch { + case err == sql.ErrNoRows: + return database.NilVersion, false, nil + case err != nil: + return 0, false, &database.Error{OrigErr: err, Query: []byte(getVersionQuery)} + default: + return int(v), dirty, err + } +} + +func (y *YDB) Drop() (err error) { + listQuery := "SELECT DISTINCT Path FROM `.sys/partition_stats` WHERE Path NOT LIKE '%/.sys%'" + rs, err := y.conn.QueryContext(context.TODO(), listQuery) + if err != nil { + return &database.Error{OrigErr: err, Query: []byte(listQuery)} + } + defer func() { + if closeErr := rs.Close(); closeErr != nil { + err = multierror.Append(err, closeErr) + } + }() + + paths := make([]string, 0) + for rs.Next() { + var path string + if err = rs.Scan(&path); err != nil { + return err + } + if len(path) != 0 { + paths = append(paths, path) + } + } + if err = rs.Err(); err != nil { + return &database.Error{OrigErr: err, Query: []byte(listQuery)} + } + + for _, path := range paths { + dropQuery := fmt.Sprintf("DROP TABLE IF EXISTS `%s`", path) + if _, err = y.conn.ExecContext(ydb.WithQueryMode(context.TODO(), ydb.SchemeQueryMode), dropQuery); err != nil { + return &database.Error{OrigErr: err, Query: []byte(dropQuery)} + } + } + return nil +} + +func (y *YDB) Lock() error { + if !y.isLocked.CompareAndSwap(false, true) { + return database.ErrLocked + } + return nil +} + +func (y *YDB) Unlock() error { + if !y.isLocked.CompareAndSwap(true, false) { + return database.ErrNotLocked + } + return nil +} + +// ensureVersionTable checks if versions table exists and, if not, creates it. +func (y *YDB) ensureVersionTable() (err error) { + if err = y.Lock(); err != nil { + return err + } + + defer func() { + if unlockErr := y.Unlock(); unlockErr != nil { + if err == nil { + err = unlockErr + } else { + err = multierror.Append(err, unlockErr) + } + } + }() + + createVersionTableQuery := fmt.Sprintf(` + CREATE TABLE IF NOT EXISTS %s ( + version Uint64 NOT NULL, + dirty Bool NOT NULL, + created Timestamp NOT NULL, + PRIMARY KEY(version) + ) + `, y.config.MigrationsTable) + if _, err = y.conn.ExecContext(ydb.WithQueryMode(context.TODO(), ydb.SchemeQueryMode), createVersionTableQuery); err != nil { + return &database.Error{OrigErr: err, Query: []byte(createVersionTableQuery)} + } + return nil +} diff --git a/database/ydb/ydb_test.go b/database/ydb/ydb_test.go new file mode 100644 index 000000000..e9b6d2099 --- /dev/null +++ b/database/ydb/ydb_test.go @@ -0,0 +1,110 @@ +package ydb + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + "github.com/dhui/dktest" + "github.com/docker/go-connections/nat" + "github.com/ydb-platform/ydb-go-sdk/v3" + + "github.com/golang-migrate/migrate/v4" + dt "github.com/golang-migrate/migrate/v4/database/testing" + _ "github.com/golang-migrate/migrate/v4/source/file" +) + +const ( + image = "ydbplatform/local-ydb:latest" + host = "localhost" + port = "2136" + databaseName = "local" +) + +var ( + opts = dktest.Options{ + Env: map[string]string{ + "GRPC_TLS_PORT": "2135", + "GRPC_PORT": "2136", + "MON_PORT": "8765", + }, + + PortBindings: nat.PortMap{ + nat.Port("2136/tcp"): []nat.PortBinding{ + { + HostIP: "0.0.0.0", + HostPort: port, + }, + }, + }, + + Hostname: host, + ReadyTimeout: 15 * time.Second, + ReadyFunc: isReady, + } +) + +func connectionString(options ...string) string { + return fmt.Sprintf("ydb://%s:%s/%s?%s", host, port, databaseName, strings.Join(options, "&")) +} + +func isReady(ctx context.Context, c dktest.ContainerInfo) bool { + d, err := ydb.Open(ctx, fmt.Sprintf("grpc://%s:%s/%s", host, port, databaseName)) + if err != nil { + return false + } + defer func() { _ = d.Close(ctx) }() + + err = d.Query().Exec(ctx, ` + CREATE TABLE test ( + id Int, + PRIMARY KEY(id) + ); + DROP TABLE test;`, nil) + return err == nil +} + +func Test(t *testing.T) { + dktest.Run(t, image, opts, func(t *testing.T, c dktest.ContainerInfo) { + db := &YDB{} + d, err := db.Open(connectionString()) + if err != nil { + t.Fatal(err) + } + + defer func() { + err := d.Close() + if err != nil { + t.Fatal(err) + } + }() + + dt.Test(t, d, []byte("CREATE TABLE `a/b/c/d/table` (x Uint64 NOT NULL, PRIMARY KEY (x))")) + }) +} + +func TestMigrate(t *testing.T) { + dktest.Run(t, image, opts, func(t *testing.T, c dktest.ContainerInfo) { + db := &YDB{} + d, err := db.Open(connectionString()) + if err != nil { + t.Fatal(err) + } + + defer func() { + err := d.Close() + if err != nil { + t.Fatal(err) + } + }() + + m, err := migrate.NewWithDatabaseInstance("file://./examples/migrations", "ydb", d) + if err != nil { + t.Fatal(err) + } + + dt.TestMigrate(t, m) + }) +} diff --git a/go.mod b/go.mod index 851054ffd..56d243a18 100644 --- a/go.mod +++ b/go.mod @@ -36,6 +36,7 @@ require ( github.com/snowflakedb/gosnowflake v1.6.19 github.com/stretchr/testify v1.9.0 github.com/xanzy/go-gitlab v0.15.0 + github.com/ydb-platform/ydb-go-sdk/v3 v3.95.5 go.mongodb.org/mongo-driver v1.7.5 go.uber.org/atomic v1.7.0 golang.org/x/oauth2 v0.18.0 diff --git a/go.sum b/go.sum index 846e435b8..6552b9415 100644 --- a/go.sum +++ b/go.sum @@ -71,6 +71,7 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/arrow/go/v10 v10.0.1 h1:n9dERvixoC/1JjDmBcs9FPaEryoANa2sCgVFo6ez9cI= github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= github.com/apache/thrift v0.16.0 h1:qEy6UW60iVOlUy+b9ZR0d5WzUWYGOo4HfopoyBaNmoY= @@ -126,12 +127,18 @@ github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInq github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg= github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50 h1:DBmgJDC9dTfkVyGgipamEh2BpGYxScCH1TOF1LL1cXc= github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido69INQaVO6d87Qn543Xr6nooe9Kz7oBFM= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= @@ -175,6 +182,8 @@ github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI= github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= @@ -191,6 +200,7 @@ github.com/fsouza/fake-gcs-server v1.17.0 h1:OeH75kBZcZa3ZE+zz/mFdJ2btt9FgqfjI7g github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= github.com/gabriel-vasile/mimetype v1.4.1 h1:TRWk7se+TOjCYgRth7+1/OYLNiRNIotknkFtf/dnN7Q= github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= @@ -241,6 +251,7 @@ github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -306,6 +317,8 @@ github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= @@ -403,6 +416,8 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= +github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg= +github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -526,9 +541,12 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rekby/fixenv v0.6.1 h1:jUFiSPpajT4WY2cYuc++7Y1zWrnCxnovGCIX72PZniM= +github.com/rekby/fixenv v0.6.1/go.mod h1:/b5LRc06BYJtslRtHKxsPWFT/ySpHV+rWvzTg+XWk4c= github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= @@ -578,6 +596,10 @@ github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23n github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/ydb-platform/ydb-go-genproto v0.0.0-20241112172322-ea1f63298f77 h1:LY6cI8cP4B9rrpTleZk95+08kl2gF4rixG7+V/dwL6Q= +github.com/ydb-platform/ydb-go-genproto v0.0.0-20241112172322-ea1f63298f77/go.mod h1:Er+FePu1dNUieD+XTMDduGpQuCPssK5Q4BjF+IIXJ3I= +github.com/ydb-platform/ydb-go-sdk/v3 v3.95.5 h1:wqaTZW458sbyloRENtULUlGhVTqv/1NTObBSGCgtPNc= +github.com/ydb-platform/ydb-go-sdk/v3 v3.95.5/go.mod h1:WiezFS4YCi2vHqbYGQkeu/2MDBYFLix6dIs/pd87Yck= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -613,6 +635,7 @@ go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHy go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -621,6 +644,8 @@ go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -699,6 +724,7 @@ golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -716,6 +742,7 @@ golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAG golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -756,6 +783,7 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -867,6 +895,7 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= @@ -881,7 +910,10 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -895,6 +927,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -913,6 +947,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/cli/build_ydb.go b/internal/cli/build_ydb.go new file mode 100644 index 000000000..baf58a6de --- /dev/null +++ b/internal/cli/build_ydb.go @@ -0,0 +1,8 @@ +//go:build ydb +// +build ydb + +package cli + +import ( + _ "github.com/golang-migrate/migrate/v4/database/ydb" +)