diff --git a/Makefile b/Makefile index 8e23a43c7..6e0a43222 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 couchbase DATABASE_TEST ?= $(DATABASE) sqlite sqlite3 sqlcipher VERSION ?= $(shell git describe --tags 2>/dev/null | cut -c 2-) TEST_FLAGS ?= diff --git a/database/couchbase/README.md b/database/couchbase/README.md new file mode 100644 index 000000000..5fd70c62b --- /dev/null +++ b/database/couchbase/README.md @@ -0,0 +1,103 @@ +# couchbase + +`couchbase://user:password@host:port/bucket?query` (`couchbases://` for TLS) + +Migrations are JSON files containing arrays of N1QL statements: + +```json +[ + {"query": "CREATE PRIMARY INDEX IF NOT EXISTS ON `mybucket`.`_default`.`_default`"}, + {"query": "UPSERT INTO `mybucket` (KEY, VALUE) VALUES ($1, $2)", "params": ["doc::1", {"name": "Alice"}]} +] +``` + +| URL Query | WithInstance Config | Description | +|------------|---------------------|-------------| +| `x-scope` | `ScopeName` | Couchbase scope for migration metadata (default: `_default`) | +| `x-migrations-collection` | `MigrationsCollection` | Collection to store migration version (default: `migrations`) | +| `x-advisory-locking` | `Locking.Enabled` | Enable advisory locking (default: `true`) | +| `x-advisory-lock-collection` | `Locking.CollectionName` | Collection for lock document (default: `migrate_advisory_lock`) | +| `x-advisory-lock-timeout` | `Locking.Timeout` | Seconds to wait for lock acquisition (default: `15`) | +| `x-advisory-lock-timeout-interval` | `Locking.Interval` | Max backoff interval in seconds between lock retries (default: `10`) | + +## Usage + +### CLI + +```bash +# Apply all up migrations +migrate -source file://migrations -database "couchbase://Administrator:password@localhost:11210/mybucket?x-scope=_default" up + +# Roll back one migration +migrate -source file://migrations -database "couchbase://Administrator:password@localhost:11210/mybucket" down 1 + +# Force a specific version (useful for fixing dirty state) +migrate -source file://migrations -database "couchbase://Administrator:password@localhost:11210/mybucket" force 3 + +# TLS connection +migrate -source file://migrations -database "couchbases://Administrator:password@localhost:11207/mybucket" up +``` + +### Go + +```go +import ( + "github.com/couchbase/gocb/v2" + "github.com/golang-migrate/migrate/v4" + "github.com/golang-migrate/migrate/v4/database/couchbase" + _ "github.com/golang-migrate/migrate/v4/source/file" +) + +// Using a URL +m, err := migrate.New("file://migrations", "couchbase://user:pass@localhost:11210/mybucket") + +// Using an existing cluster instance +cluster, _ := gocb.Connect("couchbase://localhost", gocb.ClusterOptions{ + Authenticator: gocb.PasswordAuthenticator{ + Username: "Administrator", + Password: "password", + }, +}) + +driver, err := couchbase.WithInstance(cluster, &couchbase.Config{ + BucketName: "mybucket", + ScopeName: "_default", + Locking: couchbase.Locking{ + Enabled: true, + Timeout: 15, + }, +}) + +m, err := migrate.NewWithDatabaseInstance("file://migrations", "mybucket", driver) +m.Up() +``` + +## Migration file format + +Each migration file is a JSON array of objects. Each object has a `query` field (required) and an optional `params` field for positional parameters: + +```json +[ + {"query": "CREATE COLLECTION `mybucket`.`myscope`.`users` IF NOT EXISTS"}, + {"query": "CREATE INDEX idx_email IF NOT EXISTS ON `mybucket`.`myscope`.`users`(email)"}, + {"query": "UPSERT INTO `mybucket` (KEY, VALUE) VALUES ($1, $2)", "params": ["key1", {"field": "value"}]} +] +``` + +See the [examples/migrations](examples/migrations) directory for a complete set of migrations covering collections, indexes, and data operations. + +## Advisory locking + +Advisory locking is enabled by default and uses a document-based lock in a dedicated collection. This prevents concurrent migration runs from corrupting state. The lock uses exponential backoff when waiting for acquisition. + +To disable locking (e.g., for single-instance deployments): + +``` +couchbase://user:pass@host/bucket?x-advisory-locking=false +``` + +## Couchbase version support + +Tested against: +- Couchbase Community Edition 7.6.2 +- Couchbase Community Edition 8.0.0 diff --git a/database/couchbase/couchbase.go b/database/couchbase/couchbase.go new file mode 100644 index 000000000..1c0102b5b --- /dev/null +++ b/database/couchbase/couchbase.go @@ -0,0 +1,456 @@ +package couchbase + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "net/url" + "strconv" + "sync/atomic" + "time" + + "github.com/couchbase/gocb/v2" + "github.com/golang-migrate/migrate/v4/database" +) + +func init() { + db := Couchbase{} + database.Register("couchbase", &db) + database.Register("couchbases", &db) // TLS variant +} + +const ( + DefaultScopeName = "_default" + DefaultMigrationsCollection = "migrations" + DefaultLockingCollection = "migrate_advisory_lock" + DefaultLockTimeout = 15 + DefaultLockTimeoutInterval = 10 + DefaultAdvisoryLockingFlag = true + lockDocID = "migrate_lock" + versionDocID = "migrate_version" + contextWaitTimeout = 5 * time.Second + startupWaitTimeout = 30 * time.Second +) + +var ( + ErrNilConfig = fmt.Errorf("no config") + ErrNoBucketName = fmt.Errorf("no bucket name") + ErrNoScopeName = fmt.Errorf("no scope name") +) + +type Locking struct { + CollectionName string + Timeout int + Enabled bool + Interval int +} + +type Config struct { + BucketName string + ScopeName string + MigrationsCollection string + Locking Locking +} + +type Couchbase struct { + cluster *gocb.Cluster + bucket *gocb.Bucket + scope *gocb.Scope + config *Config + isLocked atomic.Bool +} + +type versionInfo struct { + Version int `json:"version"` + Dirty bool `json:"dirty"` +} + +// N1QLMigration represents a single N1QL statement to execute. +type N1QLMigration struct { + Query string `json:"query"` + Params []any `json:"params,omitempty"` +} + +func WithInstance(cluster *gocb.Cluster, config *Config) (database.Driver, error) { + if config == nil { + return nil, ErrNilConfig + } + if len(config.BucketName) == 0 { + return nil, ErrNoBucketName + } + if len(config.ScopeName) == 0 { + config.ScopeName = DefaultScopeName + } + if len(config.MigrationsCollection) == 0 { + config.MigrationsCollection = DefaultMigrationsCollection + } + if len(config.Locking.CollectionName) == 0 { + config.Locking.CollectionName = DefaultLockingCollection + } + if config.Locking.Timeout <= 0 { + config.Locking.Timeout = DefaultLockTimeout + } + if config.Locking.Interval <= 0 { + config.Locking.Interval = DefaultLockTimeoutInterval + } + + bucket := cluster.Bucket(config.BucketName) + if err := bucket.WaitUntilReady(startupWaitTimeout, nil); err != nil { + return nil, fmt.Errorf("bucket not ready: %w", err) + } + + cb := &Couchbase{ + cluster: cluster, + bucket: bucket, + scope: bucket.Scope(config.ScopeName), + config: config, + } + + if err := cb.ensureCollections(); err != nil { + return nil, err + } + if err := cb.ensureVersionDoc(); err != nil { + return nil, err + } + + return cb, nil +} + +func (cb *Couchbase) Open(dsn string) (database.Driver, error) { + u, err := url.Parse(dsn) + if err != nil { + return nil, err + } + + // Connection string: couchbase://host/bucket?x-scope=scopeName&... + bucketName := u.Path + if len(bucketName) > 0 && bucketName[0] == '/' { + bucketName = bucketName[1:] + } + if len(bucketName) == 0 { + return nil, ErrNoBucketName + } + + q := u.Query() + + // Extract custom parameters before passing to gocb + scopeName := q.Get("x-scope") + migrationsCollection := q.Get("x-migrations-collection") + lockCollection := q.Get("x-advisory-lock-collection") + + advisoryLockingFlag, err := parseBoolean(q.Get("x-advisory-locking"), DefaultAdvisoryLockingFlag) + if err != nil { + return nil, fmt.Errorf("could not parse x-advisory-locking: %w", err) + } + lockTimeout, err := parseInt(q.Get("x-advisory-lock-timeout"), DefaultLockTimeout) + if err != nil { + return nil, fmt.Errorf("could not parse x-advisory-lock-timeout: %w", err) + } + lockInterval, err := parseInt(q.Get("x-advisory-lock-timeout-interval"), DefaultLockTimeoutInterval) + if err != nil { + return nil, fmt.Errorf("could not parse x-advisory-lock-timeout-interval: %w", err) + } + + // Extract credentials from URL + username := u.User.Username() + password, _ := u.User.Password() + + // Build the couchbase connection string without custom params + for _, key := range []string{ + "x-scope", "x-migrations-collection", "x-advisory-lock-collection", + "x-advisory-locking", "x-advisory-lock-timeout", "x-advisory-lock-timeout-interval", + } { + q.Del(key) + } + + connStr := fmt.Sprintf("%s://%s", u.Scheme, u.Host) + if len(q) > 0 { + connStr += "?" + q.Encode() + } + + opts := gocb.ClusterOptions{} + if username != "" || password != "" { + opts.Authenticator = gocb.PasswordAuthenticator{ + Username: username, + Password: password, + } + } + + cluster, err := gocb.Connect(connStr, opts) + if err != nil { + return nil, err + } + + if err := cluster.WaitUntilReady(startupWaitTimeout, nil); err != nil { + return nil, fmt.Errorf("cluster not ready: %w", err) + } + + return WithInstance(cluster, &Config{ + BucketName: bucketName, + ScopeName: scopeName, + MigrationsCollection: migrationsCollection, + Locking: Locking{ + CollectionName: lockCollection, + Timeout: lockTimeout, + Enabled: advisoryLockingFlag, + Interval: lockInterval, + }, + }) +} + +func (cb *Couchbase) Close() error { + return cb.cluster.Close(nil) +} + +func (cb *Couchbase) Lock() error { + return database.CasRestoreOnErr(&cb.isLocked, false, true, database.ErrLocked, func() error { + if !cb.config.Locking.Enabled { + return nil + } + + col := cb.scope.Collection(cb.config.Locking.CollectionName) + + deadline := time.Now().Add(time.Duration(cb.config.Locking.Timeout) * time.Second) + interval := 100 * time.Millisecond + maxInterval := time.Duration(cb.config.Locking.Interval) * time.Second + + for { + // Try to insert lock document; fails if it already exists + _, err := col.Insert(lockDocID, map[string]any{ + "locked_at": time.Now().UTC().Format(time.RFC3339), + "pid": fmt.Sprintf("%d", time.Now().UnixNano()), + }, &gocb.InsertOptions{ + Timeout: contextWaitTimeout, + }) + if err == nil { + return nil + } + + // If the document already exists, the lock is held — retry until timeout + if errors.Is(err, gocb.ErrDocumentExists) { + if time.Now().After(deadline) { + return database.ErrLocked + } + + time.Sleep(interval) + // Exponential backoff + interval = min(interval*2, maxInterval) + continue + } + + // For non-lock-related errors, return the original error + return err + } + }) +} + +func (cb *Couchbase) Unlock() error { + return database.CasRestoreOnErr(&cb.isLocked, true, false, database.ErrNotLocked, func() error { + if !cb.config.Locking.Enabled { + return nil + } + + col := cb.scope.Collection(cb.config.Locking.CollectionName) + _, err := col.Remove(lockDocID, &gocb.RemoveOptions{ + Timeout: contextWaitTimeout, + }) + if err != nil && !errors.Is(err, gocb.ErrDocumentNotFound) { + return err + } + return nil + }) +} + +// Run executes a migration. Migrations are JSON arrays of N1QL statements: +// +// [{"query": "CREATE INDEX ..."},{"query": "INSERT INTO ...","params": [1, "val"]}] +func (cb *Couchbase) Run(migration io.Reader) error { + data, err := io.ReadAll(migration) + if err != nil { + return err + } + + var stmts []N1QLMigration + if err := json.Unmarshal(data, &stmts); err != nil { + return fmt.Errorf("unmarshaling migration json: %w", err) + } + + for _, stmt := range stmts { + if len(stmt.Query) == 0 { + continue + } + + opts := &gocb.QueryOptions{ + Timeout: 60 * time.Second, + } + if len(stmt.Params) > 0 { + opts.PositionalParameters = stmt.Params + } + + _, err := cb.cluster.Query(stmt.Query, opts) + if err != nil { + return &database.Error{ + OrigErr: err, + Query: []byte(stmt.Query), + Err: "migration failed", + } + } + } + + return nil +} + +func (cb *Couchbase) SetVersion(version int, dirty bool) error { + col := cb.scope.Collection(cb.config.MigrationsCollection) + + doc := versionInfo{ + Version: version, + Dirty: dirty, + } + + _, err := col.Upsert(versionDocID, doc, &gocb.UpsertOptions{ + Timeout: contextWaitTimeout, + }) + if err != nil { + return &database.Error{OrigErr: err, Err: "save version failed"} + } + return nil +} + +func (cb *Couchbase) Version() (version int, dirty bool, err error) { + col := cb.scope.Collection(cb.config.MigrationsCollection) + + result, err := col.Get(versionDocID, &gocb.GetOptions{ + Timeout: contextWaitTimeout, + }) + if err != nil { + if errors.Is(err, gocb.ErrDocumentNotFound) { + return database.NilVersion, false, nil + } + return 0, false, &database.Error{OrigErr: err, Err: "failed to get migration version"} + } + + var info versionInfo + if err := result.Content(&info); err != nil { + return 0, false, &database.Error{OrigErr: err, Err: "failed to decode migration version"} + } + + return info.Version, info.Dirty, nil +} + +func (cb *Couchbase) Drop() error { + mgr := cb.bucket.Collections() + + // Drop all collections in the configured scope (except system collections) + scopes, err := mgr.GetAllScopes(nil) + if err != nil { + return &database.Error{OrigErr: err, Err: "failed to list scopes"} + } + + for _, scope := range scopes { + if scope.Name != cb.config.ScopeName { + continue + } + for _, col := range scope.Collections { + if col.Name == DefaultScopeName { + // Flush default collection documents via N1QL + _, qErr := cb.cluster.Query( + fmt.Sprintf("DELETE FROM `%s`.`%s`.`%s`", cb.config.BucketName, cb.config.ScopeName, col.Name), + nil, + ) + if qErr != nil { + return &database.Error{OrigErr: qErr, Err: "failed to flush default collection"} + } + continue + } + err := mgr.DropCollection(gocb.CollectionSpec{ + Name: col.Name, + ScopeName: cb.config.ScopeName, + }, nil) + if err != nil { + return &database.Error{OrigErr: err, Err: fmt.Sprintf("failed to drop collection %s", col.Name)} + } + } + } + + return nil +} + +// ensureCollections creates the migrations and locking collections if they don't exist. +func (cb *Couchbase) ensureCollections() error { + mgr := cb.bucket.Collections() + + // Ensure scope exists (skip _default) + if cb.config.ScopeName != DefaultScopeName { + if err := mgr.CreateScope(cb.config.ScopeName, nil); err != nil && !errors.Is(err, gocb.ErrScopeExists) { + return fmt.Errorf("create scope: %w", err) + } + } + + // Ensure migrations collection + err := mgr.CreateCollection(gocb.CollectionSpec{ + Name: cb.config.MigrationsCollection, + ScopeName: cb.config.ScopeName, + }, nil) + if err != nil && !errors.Is(err, gocb.ErrCollectionExists) { + return fmt.Errorf("create migrations collection: %w", err) + } + + // Ensure locking collection + if cb.config.Locking.Enabled { + err = mgr.CreateCollection(gocb.CollectionSpec{ + Name: cb.config.Locking.CollectionName, + ScopeName: cb.config.ScopeName, + }, nil) + if err != nil && !errors.Is(err, gocb.ErrCollectionExists) { + return fmt.Errorf("create locking collection: %w", err) + } + } + + // Wait for collections to be ready + time.Sleep(500 * time.Millisecond) + + return nil +} + +// ensureVersionDoc verifies that the version document is accessible. +// Note that this function locks the database, which deviates from the usual +// convention of "caller locks" in the Couchbase type. +func (cb *Couchbase) ensureVersionDoc() (err error) { + if err = cb.Lock(); err != nil { + return err + } + + defer func() { + if e := cb.Unlock(); e != nil { + err = errors.Join(err, e) + } + }() + + if _, _, err = cb.Version(); err != nil { + return err + } + return nil +} + +func parseBoolean(urlParam string, defaultValue bool) (bool, error) { + if urlParam != "" { + return strconv.ParseBool(urlParam) + } + return defaultValue, nil +} + +func parseInt(urlParam string, defaultValue int) (int, error) { + if urlParam != "" { + result, err := strconv.Atoi(urlParam) + if err != nil { + return -1, err + } + return result, nil + } + return defaultValue, nil +} + +// Ensure we satisfy the database.Driver interface at compile time. +var _ database.Driver = (*Couchbase)(nil) diff --git a/database/couchbase/couchbase_test.go b/database/couchbase/couchbase_test.go new file mode 100644 index 000000000..d02394456 --- /dev/null +++ b/database/couchbase/couchbase_test.go @@ -0,0 +1,398 @@ +package couchbase + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "net/url" + "strings" + "testing" + "time" + + "github.com/couchbase/gocb/v2" + "github.com/dhui/dktest" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/client" + "github.com/docker/go-connections/nat" + + 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 ( + testBucket = "testmigrate" + testUsername = "Administrator" + testPassword = "password" +) + +var ( + opts = dktest.Options{ + PortRequired: true, + ReadyFunc: isReady, + PullTimeout: 5 * time.Minute, + Timeout: 120 * time.Second, + ReadyTimeout: 60 * time.Second, + LogStderr: true, + // Expose required ports: 8091 (mgmt), 8092 (views/capi), 8093 (query), 11210 (KV) + PortBindings: nat.PortMap{ + "8091/tcp": []nat.PortBinding{{}}, + "8092/tcp": []nat.PortBinding{{}}, + "8093/tcp": []nat.PortBinding{{}}, + "11210/tcp": []nat.PortBinding{{}}, + }, + } + specs = []dktesting.ContainerSpec{ + {ImageName: "couchbase:community-7.6.2", Options: opts}, + {ImageName: "couchbase:community-8.0.0", Options: opts}, + } +) + +func couchbaseConnectionString(host, port string) string { + return fmt.Sprintf("couchbase://%s:%s@%s:%s/%s?x-scope=_default&network=external", testUsername, testPassword, host, port, testBucket) +} + +// restReq sends an HTTP request to the Couchbase REST API. +func restReq(method, fullURL string, form url.Values, useAuth bool) ([]byte, error) { + var bodyReader io.Reader + if form != nil { + bodyReader = strings.NewReader(form.Encode()) + } + req, err := http.NewRequest(method, fullURL, bodyReader) + if err != nil { + return nil, err + } + if form != nil { + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + } + if useAuth { + req.SetBasicAuth(testUsername, testPassword) + } + httpClient := &http.Client{Timeout: 5 * time.Second} + resp, err := httpClient.Do(req) + if err != nil { + return nil, err + } + defer func() { _ = resp.Body.Close() }() + body, _ := io.ReadAll(resp.Body) + if resp.StatusCode >= 400 { + return body, fmt.Errorf("REST %s %s returned %d: %s", method, fullURL, resp.StatusCode, string(body)) + } + return body, nil +} + +// dockerExec runs a command inside a container and returns the output. +func dockerExec(ctx context.Context, containerID string, cmd []string) (string, error) { + dc, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + if err != nil { + return "", err + } + defer func() { _ = dc.Close() }() + + exec, err := dc.ContainerExecCreate(ctx, containerID, container.ExecOptions{ + Cmd: cmd, + AttachStdout: true, + AttachStderr: true, + }) + if err != nil { + return "", fmt.Errorf("exec create: %w", err) + } + + resp, err := dc.ContainerExecAttach(ctx, exec.ID, container.ExecAttachOptions{}) + if err != nil { + return "", fmt.Errorf("exec attach: %w", err) + } + defer resp.Close() + + out, _ := io.ReadAll(resp.Reader) + return string(out), nil +} + +func isReady(ctx context.Context, c dktest.ContainerInfo) bool { + ip, mgmtPort, err := c.Port(8091) + if err != nil { + return false + } + _, kvPort, err := c.Port(11210) + if err != nil { + return false + } + _, n1qlPort, err := c.Port(8093) + if err != nil { + return false + } + _, capiPort, err := c.Port(8092) + if err != nil { + return false + } + + baseURL := fmt.Sprintf("http://%s:%s", ip, mgmtPort) + + // 1. Check if REST API is up (try without auth first, then with auth) + _, err = restReq(http.MethodGet, baseURL+"/pools", nil, false) + if err != nil { + _, err = restReq(http.MethodGet, baseURL+"/pools", nil, true) + if err != nil { + log.Printf("REST not ready: %v", err) + return false + } + } + + // 2. Provision the cluster via REST API + if err := initCluster(ctx, c.ID, baseURL, ip, mgmtPort, kvPort, n1qlPort, capiPort); err != nil { + log.Printf("init cluster error: %v", err) + return false + } + + // 3. Check bucket health via REST + body, err := restReq(http.MethodGet, baseURL+"/pools/default/buckets/"+testBucket, nil, true) + if err != nil { + log.Printf("bucket not ready: %v", err) + return false + } + + var bucketInfo struct { + Nodes []struct { + Status string `json:"status"` + } `json:"nodes"` + } + if err := json.Unmarshal(body, &bucketInfo); err != nil { + log.Printf("parse bucket info: %v", err) + return false + } + for _, node := range bucketInfo.Nodes { + if node.Status != "healthy" { + log.Printf("bucket node not healthy: %s", node.Status) + return false + } + } + + // 4. Verify N1QL via REST + n1qlURL := fmt.Sprintf("http://%s:%s/query/service", ip, n1qlPort) + _, err = restReq(http.MethodPost, n1qlURL, url.Values{"statement": {"SELECT 1"}}, true) + if err != nil { + log.Printf("N1QL not ready: %v", err) + return false + } + + // 5. Verify gocb can connect and route queries via external network + connStr := fmt.Sprintf("couchbase://%s:%s?network=external", ip, kvPort) + cluster, err := gocb.Connect(connStr, gocb.ClusterOptions{ + Authenticator: gocb.PasswordAuthenticator{ + Username: testUsername, + Password: testPassword, + }, + }) + if err != nil { + log.Printf("gocb connect error: %v", err) + return false + } + defer func() { _ = cluster.Close(nil) }() + + if err := cluster.WaitUntilReady(5*time.Second, &gocb.WaitUntilReadyOptions{ + ServiceTypes: []gocb.ServiceType{gocb.ServiceTypeManagement, gocb.ServiceTypeQuery}, + }); err != nil { + log.Printf("gocb cluster not ready: %v", err) + return false + } + + bucket := cluster.Bucket(testBucket) + if err := bucket.WaitUntilReady(5*time.Second, nil); err != nil { + log.Printf("gocb bucket not ready: %v", err) + return false + } + + _, err = cluster.Query("SELECT 1", &gocb.QueryOptions{Timeout: 5 * time.Second}) + if err != nil { + log.Printf("gocb N1QL not ready: %v", err) + return false + } + + return true +} + +// initCluster provisions the Couchbase cluster via the REST API. +// Alternate addresses are set using couchbase-cli inside the container +// (the REST API doesn't reliably set N1QL alternate ports). +func initCluster(ctx context.Context, containerID, baseURL, host, mgmtPort, kvPort, n1qlPort, capiPort string) error { + // Check if cluster is already initialized + _, err := restReq(http.MethodGet, baseURL+"/pools/default", nil, true) + alreadyInitialized := err == nil + + if !alreadyInitialized { + // Fresh node — no auth required for initial setup + if _, err := restReq(http.MethodPost, baseURL+"/node/controller/setupServices", + url.Values{"services": {"kv,n1ql,index"}}, false); err != nil { + return fmt.Errorf("setup services: %w", err) + } + if _, err := restReq(http.MethodPost, baseURL+"/pools/default", + url.Values{"memoryQuota": {"256"}, "indexMemoryQuota": {"256"}}, false); err != nil { + return fmt.Errorf("set memory quota: %w", err) + } + if _, err := restReq(http.MethodPost, baseURL+"/settings/web", + url.Values{"username": {testUsername}, "password": {testPassword}, "port": {"8091"}}, false); err != nil { + return fmt.Errorf("set credentials: %w", err) + } + } + + // Set alternate addresses using couchbase-cli inside the container + // (this reliably sets all service ports including n1ql, unlike the REST API) + portsArg := fmt.Sprintf("mgmt=%s,kv=%s,capi=%s,n1ql=%s", mgmtPort, kvPort, capiPort, n1qlPort) + out, err := dockerExec(ctx, containerID, []string{ + "couchbase-cli", "setting-alternate-address", + "-c", "127.0.0.1:8091", + "--username", testUsername, + "--password", testPassword, + "--set", + "--node", "127.0.0.1", + "--hostname", host, + "--ports", portsArg, + }) + if err != nil { + return fmt.Errorf("set alternate addresses: %w (output: %s)", err, out) + } + + // Create the test bucket (idempotent) + _, err = restReq(http.MethodPost, baseURL+"/pools/default/buckets", + url.Values{"name": {testBucket}, "ramQuota": {"256"}, "bucketType": {"couchbase"}}, true) + if err != nil && !strings.Contains(err.Error(), "already exists") { + return fmt.Errorf("create bucket: %w", err) + } + + // Give the bucket time to initialize on first creation + if !alreadyInitialized { + time.Sleep(3 * time.Second) + } + + return nil +} + +func Test(t *testing.T) { + t.Run("test", test) + t.Run("testWithInstance", testWithInstance) + t.Run("testLockWorks", testLockWorks) + + // Note: we intentionally do not clean up images here. + // Containers are cleaned up by dktest automatically. + // Removing images would force slow re-pulls on every test run. +} + +func test(t *testing.T) { + dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) { + ip, kvPort, err := c.Port(11210) + if err != nil { + t.Fatal(err) + } + + addr := couchbaseConnectionString(ip, kvPort) + p := &Couchbase{} + d, err := p.Open(addr) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() + + migration := []byte(`[{"query":"SELECT 1"}]`) + dt.Test(t, d, migration) + }) +} + +func testWithInstance(t *testing.T) { + dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) { + ip, kvPort, err := c.Port(11210) + if err != nil { + t.Fatal(err) + } + + connStr := fmt.Sprintf("couchbase://%s:%s?network=external", ip, kvPort) + cluster, err := gocb.Connect(connStr, gocb.ClusterOptions{ + Authenticator: gocb.PasswordAuthenticator{ + Username: testUsername, + Password: testPassword, + }, + }) + if err != nil { + t.Fatal(err) + } + + d, err := WithInstance(cluster, &Config{ + BucketName: testBucket, + ScopeName: "_default", + }) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() + + dt.TestNilVersion(t, d) + dt.TestLockAndUnlock(t, d) + + migration := []byte(`[{"query":"SELECT 1"}]`) + dt.TestRun(t, d, bytes.NewReader(migration)) + dt.TestSetVersion(t, d) + dt.TestDrop(t, d) + }) +} + +func testLockWorks(t *testing.T) { + dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) { + ip, kvPort, err := c.Port(11210) + if err != nil { + t.Fatal(err) + } + + addr := couchbaseConnectionString(ip, kvPort) + p := &Couchbase{} + d, err := p.Open(addr) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := d.Close(); err != nil { + t.Error(err) + } + }() + + migration := []byte(`[{"query":"SELECT 1"}]`) + dt.TestRun(t, d, bytes.NewReader(migration)) + + cb := d.(*Couchbase) + + // Test lock/unlock cycle + if err := cb.Lock(); err != nil { + t.Fatal(err) + } + if err := cb.Unlock(); err != nil { + t.Fatal(err) + } + + if err := cb.Lock(); err != nil { + t.Fatal(err) + } + if err := cb.Unlock(); err != nil { + t.Fatal(err) + } + + // Enable locking with a short timeout and test lock conflict + cb.config.Locking.Enabled = true + cb.config.Locking.Timeout = 1 + if err := cb.Lock(); err != nil { + t.Fatal(err) + } + if err := cb.Lock(); err == nil { + t.Fatal("should have failed, couchbase should be locked already") + } + }) +} diff --git a/database/couchbase/examples/migrations/000001_create_index.down.json b/database/couchbase/examples/migrations/000001_create_index.down.json new file mode 100644 index 000000000..712865d00 --- /dev/null +++ b/database/couchbase/examples/migrations/000001_create_index.down.json @@ -0,0 +1 @@ +[{"query": "DROP PRIMARY INDEX IF EXISTS ON `testmigrate`._default._default"}] diff --git a/database/couchbase/examples/migrations/000001_create_index.up.json b/database/couchbase/examples/migrations/000001_create_index.up.json new file mode 100644 index 000000000..ecf5e8334 --- /dev/null +++ b/database/couchbase/examples/migrations/000001_create_index.up.json @@ -0,0 +1 @@ +[{"query": "CREATE PRIMARY INDEX IF NOT EXISTS ON `testmigrate`._default._default"}] diff --git a/database/couchbase/examples/migrations/000002_create_collections.down.json b/database/couchbase/examples/migrations/000002_create_collections.down.json new file mode 100644 index 000000000..77208cc79 --- /dev/null +++ b/database/couchbase/examples/migrations/000002_create_collections.down.json @@ -0,0 +1,4 @@ +[ + {"query": "DROP COLLECTION `testmigrate`.`_default`.`orders` IF EXISTS"}, + {"query": "DROP COLLECTION `testmigrate`.`_default`.`users` IF EXISTS"} +] diff --git a/database/couchbase/examples/migrations/000002_create_collections.up.json b/database/couchbase/examples/migrations/000002_create_collections.up.json new file mode 100644 index 000000000..67c90f2a5 --- /dev/null +++ b/database/couchbase/examples/migrations/000002_create_collections.up.json @@ -0,0 +1,4 @@ +[ + {"query": "CREATE COLLECTION `testmigrate`.`_default`.`users` IF NOT EXISTS"}, + {"query": "CREATE COLLECTION `testmigrate`.`_default`.`orders` IF NOT EXISTS"} +] diff --git a/database/couchbase/examples/migrations/000003_create_indexes.down.json b/database/couchbase/examples/migrations/000003_create_indexes.down.json new file mode 100644 index 000000000..c82c5ae72 --- /dev/null +++ b/database/couchbase/examples/migrations/000003_create_indexes.down.json @@ -0,0 +1,6 @@ +[ + {"query": "DROP INDEX `testmigrate`.`_default`.`orders`.idx_orders_created_at IF EXISTS"}, + {"query": "DROP INDEX `testmigrate`.`_default`.`orders`.idx_orders_user_id IF EXISTS"}, + {"query": "DROP INDEX `testmigrate`.`_default`.`users`.idx_users_email IF EXISTS"}, + {"query": "DROP PRIMARY INDEX IF EXISTS ON `testmigrate`.`_default`.`users`"} +] diff --git a/database/couchbase/examples/migrations/000003_create_indexes.up.json b/database/couchbase/examples/migrations/000003_create_indexes.up.json new file mode 100644 index 000000000..65aeeb360 --- /dev/null +++ b/database/couchbase/examples/migrations/000003_create_indexes.up.json @@ -0,0 +1,6 @@ +[ + {"query": "CREATE PRIMARY INDEX IF NOT EXISTS ON `testmigrate`.`_default`.`users`"}, + {"query": "CREATE INDEX idx_users_email IF NOT EXISTS ON `testmigrate`.`_default`.`users`(email)"}, + {"query": "CREATE INDEX idx_orders_user_id IF NOT EXISTS ON `testmigrate`.`_default`.`orders`(user_id)"}, + {"query": "CREATE INDEX idx_orders_created_at IF NOT EXISTS ON `testmigrate`.`_default`.`orders`(created_at)"} +] diff --git a/database/couchbase/examples/migrations/000004_seed_data.down.json b/database/couchbase/examples/migrations/000004_seed_data.down.json new file mode 100644 index 000000000..187a08b6a --- /dev/null +++ b/database/couchbase/examples/migrations/000004_seed_data.down.json @@ -0,0 +1,3 @@ +[ + {"query": "DELETE FROM `testmigrate`.`_default`.`users` WHERE META().id IN ['user::1', 'user::2']"} +] diff --git a/database/couchbase/examples/migrations/000004_seed_data.up.json b/database/couchbase/examples/migrations/000004_seed_data.up.json new file mode 100644 index 000000000..1c2254fcf --- /dev/null +++ b/database/couchbase/examples/migrations/000004_seed_data.up.json @@ -0,0 +1,4 @@ +[ + {"query": "UPSERT INTO `testmigrate`.`_default`.`users` (KEY, VALUE) VALUES ($1, {\"name\": $2, \"email\": $3, \"type\": \"user\", \"created_at\": NOW_STR()})", "params": ["user::1", "Alice Smith", "alice@example.com"]}, + {"query": "UPSERT INTO `testmigrate`.`_default`.`users` (KEY, VALUE) VALUES ($1, {\"name\": $2, \"email\": $3, \"type\": \"user\", \"created_at\": NOW_STR()})", "params": ["user::2", "Bob Jones", "bob@example.com"]} +] diff --git a/database/couchbase/examples/migrations/000005_add_composite_index.down.json b/database/couchbase/examples/migrations/000005_add_composite_index.down.json new file mode 100644 index 000000000..5b7975009 --- /dev/null +++ b/database/couchbase/examples/migrations/000005_add_composite_index.down.json @@ -0,0 +1,3 @@ +[ + {"query": "DROP INDEX `testmigrate`.`_default`.`users`.idx_users_name_email IF EXISTS"} +] diff --git a/database/couchbase/examples/migrations/000005_add_composite_index.up.json b/database/couchbase/examples/migrations/000005_add_composite_index.up.json new file mode 100644 index 000000000..b191f2d6c --- /dev/null +++ b/database/couchbase/examples/migrations/000005_add_composite_index.up.json @@ -0,0 +1,3 @@ +[ + {"query": "CREATE INDEX idx_users_name_email IF NOT EXISTS ON `testmigrate`.`_default`.`users`(name, email) WHERE type = 'user'"} +] diff --git a/go.mod b/go.mod index eacffe3e0..f65c1c7cd 100644 --- a/go.mod +++ b/go.mod @@ -10,8 +10,10 @@ require ( github.com/aws/aws-sdk-go v1.49.6 github.com/cenkalti/backoff/v4 v4.1.2 github.com/cockroachdb/cockroach-go/v2 v2.1.1 + github.com/couchbase/gocb/v2 v2.12.0 github.com/dhui/dktest v0.4.6 github.com/docker/docker v28.3.3+incompatible + github.com/docker/go-connections v0.5.0 github.com/fsouza/fake-gcs-server v1.17.0 github.com/go-sql-driver/mysql v1.5.0 github.com/gobuffalo/here v0.6.0 @@ -33,7 +35,7 @@ require ( github.com/stretchr/testify v1.11.1 github.com/xanzy/go-gitlab v0.15.0 go.mongodb.org/mongo-driver v1.7.5 - golang.org/x/oauth2 v0.30.0 + golang.org/x/oauth2 v0.34.0 golang.org/x/tools/godoc v0.1.0-deprecated google.golang.org/api v0.247.0 modernc.org/ql v1.0.0 @@ -41,32 +43,36 @@ require ( ) require ( - cel.dev/expr v0.24.0 // indirect + cel.dev/expr v0.25.1 // indirect cloud.google.com/go/auth v0.16.4 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/monitoring v1.24.2 // indirect github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.3 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/couchbase/gocbcore/v10 v10.9.0 // indirect + github.com/couchbase/gocbcoreps v0.1.5-0.20260107140814-1c3a03f888f8 // indirect + github.com/couchbase/goprotostellar v1.0.5 // indirect + github.com/couchbaselabs/gocbconnstr/v2 v2.0.0 // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/docker/go-connections v0.5.0 // indirect - github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect + github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-jose/go-jose/v4 v4.0.5 // indirect + github.com/go-jose/go-jose/v4 v4.1.3 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/sys/sequential v0.6.0 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect - github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect - github.com/zeebo/errs v1.4.0 // indirect + github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.36.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect go.opentelemetry.io/otel v1.40.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 // indirect @@ -74,14 +80,15 @@ require ( go.opentelemetry.io/otel/sdk v1.40.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect go.opentelemetry.io/otel/trace v1.40.0 // indirect - go.opentelemetry.io/proto/otlp v1.3.1 // indirect - golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 // indirect - golang.org/x/tools v0.38.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.1 // indirect + golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2 // indirect + golang.org/x/tools v0.41.0 // indirect ) require ( cloud.google.com/go v0.121.6 // indirect - cloud.google.com/go/compute/metadata v0.8.0 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect cloud.google.com/go/iam v1.5.2 // indirect cloud.google.com/go/longrunning v0.6.7 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect @@ -113,14 +120,14 @@ require ( github.com/aws/smithy-go v1.13.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 // indirect - github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect + github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dvsekhvalnov/jose2go v1.7.0 // indirect github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 // indirect - github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect + github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect github.com/form3tech-oss/jwt-go v3.2.5+incompatible // indirect github.com/gabriel-vasile/mimetype v1.4.1 // indirect github.com/go-stack/stack v1.8.0 // indirect @@ -131,7 +138,7 @@ require ( github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/snappy v0.0.4 // indirect + github.com/golang/snappy v1.0.0 // indirect github.com/google/flatbuffers v2.0.8+incompatible // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/s2a-go v0.1.9 // indirect @@ -182,21 +189,21 @@ require ( github.com/zeebo/xxh3 v1.0.2 // indirect gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b // indirect go.opencensus.io v0.24.0 // indirect - golang.org/x/crypto v0.45.0 // indirect + golang.org/x/crypto v0.48.0 // indirect golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 // indirect - golang.org/x/mod v0.29.0 // indirect - golang.org/x/net v0.47.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.40.0 // indirect - golang.org/x/term v0.37.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/mod v0.32.0 // indirect + golang.org/x/net v0.50.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.41.0 // indirect + golang.org/x/term v0.40.0 // indirect + golang.org/x/text v0.34.0 // indirect golang.org/x/time v0.12.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect - google.golang.org/grpc v1.74.2 // indirect - google.golang.org/protobuf v1.36.7 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect + google.golang.org/grpc v1.79.1 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/uint128 v1.2.0 // indirect diff --git a/go.sum b/go.sum index 039672531..0f48b557a 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= -cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= +cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -184,8 +184,8 @@ cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZ cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA= -cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= @@ -656,8 +656,8 @@ github.com/ClickHouse/clickhouse-go v1.4.3 h1:iAFMa2UrQdR5bHJ2/yaSLffZkxpcOYQMCU github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.3 h1:2afWGsMzkIcN8Qm4mgPJKZWyroE5QBszMiDMYEBrnfw= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.3/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0 h1:4LP6hvB4I5ouTbGgWtixJhgED6xdf67twf9PoY96Tbg= @@ -760,8 +760,8 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls= -github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/cockroach-go/v2 v2.1.1 h1:3XzfSMuUT0wBe1a3o5C0eOTcArhmmFAg2Jzh/7hhKqo= @@ -774,6 +774,18 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/couchbase/gocb/v2 v2.12.0 h1:IIIhOLJJHXHJ5Y876tgmhG9osmOaDPuepycJyJKj/14= +github.com/couchbase/gocb/v2 v2.12.0/go.mod h1:MVrScUfHQI+/wIg5BJZd2LefgW+0sn9FfK2x89mW10Y= +github.com/couchbase/gocbcore/v10 v10.9.0 h1:+O1ZF9/BZN2wE8qrPUwatR4BsXcffdIOZ8Lj/0tY3s4= +github.com/couchbase/gocbcore/v10 v10.9.0/go.mod h1:OWKfU9R5Nm5V3QZBtfdZl5qCfgxtxTqOgXiNr4pn9/c= +github.com/couchbase/gocbcoreps v0.1.5-0.20260107140814-1c3a03f888f8 h1:WwGhY3TYn2INQo88yzEhUMYFlgjRInA1dgfEa3UhAxw= +github.com/couchbase/gocbcoreps v0.1.5-0.20260107140814-1c3a03f888f8/go.mod h1:AUR8DPPmvM+uMkb+Q01Y0mMXINdEY/jUL/qE+kPJ67s= +github.com/couchbase/goprotostellar v1.0.5 h1:pmR4H87zbYymIdTR1owyUZsfQ7NupkfCuNLW4FIPBhE= +github.com/couchbase/goprotostellar v1.0.5/go.mod h1:X58ot5FRqlBTBkwG/oI4klunpu4MApjGktheqeRWQw0= +github.com/couchbaselabs/gocaves/client v0.0.0-20250107114554-f96479220ae8 h1:MQfvw4BiLTuyR69FuA5Kex+tXUeLkH+/ucJfVL1/hkM= +github.com/couchbaselabs/gocaves/client v0.0.0-20250107114554-f96479220ae8/go.mod h1:AVekAZwIY2stsJOMWLAS/0uA/+qdp7pjO8EHnl61QkY= +github.com/couchbaselabs/gocbconnstr/v2 v2.0.0 h1:HU9DlAYYWR69jQnLN6cpg0fh0hxW/8d5hnglCXXjW78= +github.com/couchbaselabs/gocbconnstr/v2 v2.0.0/go.mod h1:o7T431UOfFVHDNvMBUmUxpHnhivwv7BziUao/nMl81E= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= @@ -817,18 +829,18 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= -github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= -github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA= -github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= -github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= +github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= +github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU= +github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= +github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= -github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= +github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4= +github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= @@ -851,8 +863,8 @@ github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmn 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-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= -github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= +github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= +github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= @@ -935,8 +947,9 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6 github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM= @@ -1023,12 +1036,13 @@ 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/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= -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/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= @@ -1296,8 +1310,8 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE= -github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= +github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= +github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -1340,8 +1354,6 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= -github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM= -github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= @@ -1360,10 +1372,10 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/detectors/gcp v1.36.0 h1:F7q2tNlCaHY9nMKHR6XH9/qkp8FktLnIcy6jJNyOCQw= -go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPxAxnjc2pQTxWNkwfstZ+6H2k= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= +go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE= +go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0 h1:XmiuHzgJt067+a6kwyAzkhXooYVv3/TOw9cM2VfJgUM= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0/go.mod h1:KDgtbWKTQs4bM+VPUr6WlL9m/WXcmkCcBlIzqxPGzmI= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= @@ -1385,19 +1397,25 @@ go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTq go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -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.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= +go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 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/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 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= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1420,8 +1438,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= +golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1481,8 +1499,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= +golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1549,8 +1567,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= +golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1582,8 +1600,8 @@ golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= -golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1600,8 +1618,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1697,10 +1715,10 @@ golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 h1:LvzTn0GQhWuvKH/kVRS3R3bVAsdQWI7hvfLHGgh9+lU= -golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2 h1:O1cMQHRfwNpDfDJerqRoE2oD+AFlyid87D40L/OkkJo= +golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2/go.mod h1:b7fPSJ0pKZ3ccUh8gnTONJxhn3c/PS6tyzQvyqw4iA8= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1710,8 +1728,8 @@ golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= +golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1728,8 +1746,8 @@ golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1808,8 +1826,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= +golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= golang.org/x/tools/godoc v0.1.0-deprecated h1:o+aZ1BOj6Hsx/GBdJO/s815sqftjSnrZZwyYTHODvtk= golang.org/x/tools/godoc v0.1.0-deprecated/go.mod h1:qM63CriJ961IHWmnWa9CjZnBndniPt4a3CK0PVB9bIg= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1827,8 +1845,9 @@ golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6f gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= @@ -2035,10 +2054,10 @@ google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOl google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= -google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c h1:AtEkQdl5b6zsybXcbz00j1LwNodDuH6hVifIaNqk7NQ= -google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo= +google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls= +google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -2080,8 +2099,8 @@ google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5v google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4= -google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM= +google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -2100,8 +2119,8 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= -google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/cli/build_couchbase.go b/internal/cli/build_couchbase.go new file mode 100644 index 000000000..ea9c4359b --- /dev/null +++ b/internal/cli/build_couchbase.go @@ -0,0 +1,7 @@ +//go:build couchbase + +package cli + +import ( + _ "github.com/golang-migrate/migrate/v4/database/couchbase" +)