github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/go.mongodb.org/mongo-driver/mongo/client.go (about)

     1  // Copyright (C) MongoDB, Inc. 2017-present.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"); you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
     6  
     7  package mongo
     8  
     9  import (
    10  	"context"
    11  	"errors"
    12  	"fmt"
    13  	"net/http"
    14  	"time"
    15  
    16  	"go.mongodb.org/mongo-driver/bson"
    17  	"go.mongodb.org/mongo-driver/bson/bsoncodec"
    18  	"go.mongodb.org/mongo-driver/event"
    19  	"go.mongodb.org/mongo-driver/internal"
    20  	"go.mongodb.org/mongo-driver/internal/logger"
    21  	"go.mongodb.org/mongo-driver/internal/uuid"
    22  	"go.mongodb.org/mongo-driver/mongo/description"
    23  	"go.mongodb.org/mongo-driver/mongo/options"
    24  	"go.mongodb.org/mongo-driver/mongo/readconcern"
    25  	"go.mongodb.org/mongo-driver/mongo/readpref"
    26  	"go.mongodb.org/mongo-driver/mongo/writeconcern"
    27  	"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
    28  	"go.mongodb.org/mongo-driver/x/mongo/driver"
    29  	"go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt"
    30  	mcopts "go.mongodb.org/mongo-driver/x/mongo/driver/mongocrypt/options"
    31  	"go.mongodb.org/mongo-driver/x/mongo/driver/operation"
    32  	"go.mongodb.org/mongo-driver/x/mongo/driver/session"
    33  	"go.mongodb.org/mongo-driver/x/mongo/driver/topology"
    34  )
    35  
    36  const (
    37  	defaultLocalThreshold = 15 * time.Millisecond
    38  	defaultMaxPoolSize    = 100
    39  )
    40  
    41  var (
    42  	// keyVaultCollOpts specifies options used to communicate with the key vault collection
    43  	keyVaultCollOpts = options.Collection().SetReadConcern(readconcern.Majority()).
    44  				SetWriteConcern(writeconcern.New(writeconcern.WMajority()))
    45  
    46  	endSessionsBatchSize = 10000
    47  )
    48  
    49  // Client is a handle representing a pool of connections to a MongoDB deployment. It is safe for concurrent use by
    50  // multiple goroutines.
    51  //
    52  // The Client type opens and closes connections automatically and maintains a pool of idle connections. For
    53  // connection pool configuration options, see documentation for the ClientOptions type in the mongo/options package.
    54  type Client struct {
    55  	id             uuid.UUID
    56  	deployment     driver.Deployment
    57  	localThreshold time.Duration
    58  	retryWrites    bool
    59  	retryReads     bool
    60  	clock          *session.ClusterClock
    61  	readPreference *readpref.ReadPref
    62  	readConcern    *readconcern.ReadConcern
    63  	writeConcern   *writeconcern.WriteConcern
    64  	bsonOpts       *options.BSONOptions
    65  	registry       *bsoncodec.Registry
    66  	monitor        *event.CommandMonitor
    67  	serverAPI      *driver.ServerAPIOptions
    68  	serverMonitor  *event.ServerMonitor
    69  	sessionPool    *session.Pool
    70  	timeout        *time.Duration
    71  	httpClient     *http.Client
    72  	logger         *logger.Logger
    73  
    74  	// client-side encryption fields
    75  	keyVaultClientFLE  *Client
    76  	keyVaultCollFLE    *Collection
    77  	mongocryptdFLE     *mongocryptdClient
    78  	cryptFLE           driver.Crypt
    79  	metadataClientFLE  *Client
    80  	internalClientFLE  *Client
    81  	encryptedFieldsMap map[string]interface{}
    82  }
    83  
    84  // Connect creates a new Client and then initializes it using the Connect method. This is equivalent to calling
    85  // NewClient followed by Client.Connect.
    86  //
    87  // When creating an options.ClientOptions, the order the methods are called matters. Later Set*
    88  // methods will overwrite the values from previous Set* method invocations. This includes the
    89  // ApplyURI method. This allows callers to determine the order of precedence for option
    90  // application. For instance, if ApplyURI is called before SetAuth, the Credential from
    91  // SetAuth will overwrite the values from the connection string. If ApplyURI is called
    92  // after SetAuth, then its values will overwrite those from SetAuth.
    93  //
    94  // The opts parameter is processed using options.MergeClientOptions, which will overwrite entire
    95  // option fields of previous options, there is no partial overwriting. For example, if Username is
    96  // set in the Auth field for the first option, and Password is set for the second but with no
    97  // Username, after the merge the Username field will be empty.
    98  //
    99  // The NewClient function does not do any I/O and returns an error if the given options are invalid.
   100  // The Client.Connect method starts background goroutines to monitor the state of the deployment and does not do
   101  // any I/O in the main goroutine to prevent the main goroutine from blocking. Therefore, it will not error if the
   102  // deployment is down.
   103  //
   104  // The Client.Ping method can be used to verify that the deployment is successfully connected and the
   105  // Client was correctly configured.
   106  func Connect(ctx context.Context, opts ...*options.ClientOptions) (*Client, error) {
   107  	c, err := NewClient(opts...)
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  	err = c.Connect(ctx)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	return c, nil
   116  }
   117  
   118  // NewClient creates a new client to connect to a deployment specified by the uri.
   119  //
   120  // When creating an options.ClientOptions, the order the methods are called matters. Later Set*
   121  // methods will overwrite the values from previous Set* method invocations. This includes the
   122  // ApplyURI method. This allows callers to determine the order of precedence for option
   123  // application. For instance, if ApplyURI is called before SetAuth, the Credential from
   124  // SetAuth will overwrite the values from the connection string. If ApplyURI is called
   125  // after SetAuth, then its values will overwrite those from SetAuth.
   126  //
   127  // The opts parameter is processed using options.MergeClientOptions, which will overwrite entire
   128  // option fields of previous options, there is no partial overwriting. For example, if Username is
   129  // set in the Auth field for the first option, and Password is set for the second but with no
   130  // Username, after the merge the Username field will be empty.
   131  //
   132  // Deprecated: Use [Connect] instead.
   133  func NewClient(opts ...*options.ClientOptions) (*Client, error) {
   134  	clientOpt := options.MergeClientOptions(opts...)
   135  
   136  	id, err := uuid.New()
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  	client := &Client{id: id}
   141  
   142  	// ClusterClock
   143  	client.clock = new(session.ClusterClock)
   144  
   145  	// LocalThreshold
   146  	client.localThreshold = defaultLocalThreshold
   147  	if clientOpt.LocalThreshold != nil {
   148  		client.localThreshold = *clientOpt.LocalThreshold
   149  	}
   150  	// Monitor
   151  	if clientOpt.Monitor != nil {
   152  		client.monitor = clientOpt.Monitor
   153  	}
   154  	// ServerMonitor
   155  	if clientOpt.ServerMonitor != nil {
   156  		client.serverMonitor = clientOpt.ServerMonitor
   157  	}
   158  	// ReadConcern
   159  	client.readConcern = readconcern.New()
   160  	if clientOpt.ReadConcern != nil {
   161  		client.readConcern = clientOpt.ReadConcern
   162  	}
   163  	// ReadPreference
   164  	client.readPreference = readpref.Primary()
   165  	if clientOpt.ReadPreference != nil {
   166  		client.readPreference = clientOpt.ReadPreference
   167  	}
   168  	// BSONOptions
   169  	if clientOpt.BSONOptions != nil {
   170  		client.bsonOpts = clientOpt.BSONOptions
   171  	}
   172  	// Registry
   173  	client.registry = bson.DefaultRegistry
   174  	if clientOpt.Registry != nil {
   175  		client.registry = clientOpt.Registry
   176  	}
   177  	// RetryWrites
   178  	client.retryWrites = true // retry writes on by default
   179  	if clientOpt.RetryWrites != nil {
   180  		client.retryWrites = *clientOpt.RetryWrites
   181  	}
   182  	client.retryReads = true
   183  	if clientOpt.RetryReads != nil {
   184  		client.retryReads = *clientOpt.RetryReads
   185  	}
   186  	// Timeout
   187  	client.timeout = clientOpt.Timeout
   188  	client.httpClient = clientOpt.HTTPClient
   189  	// WriteConcern
   190  	if clientOpt.WriteConcern != nil {
   191  		client.writeConcern = clientOpt.WriteConcern
   192  	}
   193  	// AutoEncryptionOptions
   194  	if clientOpt.AutoEncryptionOptions != nil {
   195  		if err := client.configureAutoEncryption(clientOpt); err != nil {
   196  			return nil, err
   197  		}
   198  	} else {
   199  		client.cryptFLE = clientOpt.Crypt
   200  	}
   201  
   202  	// Deployment
   203  	if clientOpt.Deployment != nil {
   204  		client.deployment = clientOpt.Deployment
   205  	}
   206  
   207  	// Set default options
   208  	if clientOpt.MaxPoolSize == nil {
   209  		clientOpt.SetMaxPoolSize(defaultMaxPoolSize)
   210  	}
   211  
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  
   216  	cfg, err := topology.NewConfig(clientOpt, client.clock)
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  	client.serverAPI = topology.ServerAPIFromServerOptions(cfg.ServerOpts)
   221  
   222  	if client.deployment == nil {
   223  		client.deployment, err = topology.New(cfg)
   224  		if err != nil {
   225  			return nil, replaceErrors(err)
   226  		}
   227  	}
   228  
   229  	// Create a logger for the client.
   230  	client.logger, err = newLogger(clientOpt.LoggerOptions)
   231  	if err != nil {
   232  		return nil, fmt.Errorf("invalid logger options: %w", err)
   233  	}
   234  
   235  	return client, nil
   236  }
   237  
   238  // Connect initializes the Client by starting background monitoring goroutines.
   239  // If the Client was created using the NewClient function, this method must be called before a Client can be used.
   240  //
   241  // Connect starts background goroutines to monitor the state of the deployment and does not do any I/O in the main
   242  // goroutine. The Client.Ping method can be used to verify that the connection was created successfully.
   243  //
   244  // Deprecated: Use [mongo.Connect] instead.
   245  func (c *Client) Connect(ctx context.Context) error {
   246  	if connector, ok := c.deployment.(driver.Connector); ok {
   247  		err := connector.Connect()
   248  		if err != nil {
   249  			return replaceErrors(err)
   250  		}
   251  	}
   252  
   253  	if c.mongocryptdFLE != nil {
   254  		if err := c.mongocryptdFLE.connect(ctx); err != nil {
   255  			return err
   256  		}
   257  	}
   258  
   259  	if c.internalClientFLE != nil {
   260  		if err := c.internalClientFLE.Connect(ctx); err != nil {
   261  			return err
   262  		}
   263  	}
   264  
   265  	if c.keyVaultClientFLE != nil && c.keyVaultClientFLE != c.internalClientFLE && c.keyVaultClientFLE != c {
   266  		if err := c.keyVaultClientFLE.Connect(ctx); err != nil {
   267  			return err
   268  		}
   269  	}
   270  
   271  	if c.metadataClientFLE != nil && c.metadataClientFLE != c.internalClientFLE && c.metadataClientFLE != c {
   272  		if err := c.metadataClientFLE.Connect(ctx); err != nil {
   273  			return err
   274  		}
   275  	}
   276  
   277  	var updateChan <-chan description.Topology
   278  	if subscriber, ok := c.deployment.(driver.Subscriber); ok {
   279  		sub, err := subscriber.Subscribe()
   280  		if err != nil {
   281  			return replaceErrors(err)
   282  		}
   283  		updateChan = sub.Updates
   284  	}
   285  	c.sessionPool = session.NewPool(updateChan)
   286  	return nil
   287  }
   288  
   289  // Disconnect closes sockets to the topology referenced by this Client. It will
   290  // shut down any monitoring goroutines, close the idle connection pool, and will
   291  // wait until all the in use connections have been returned to the connection
   292  // pool and closed before returning. If the context expires via cancellation,
   293  // deadline, or timeout before the in use connections have returned, the in use
   294  // connections will be closed, resulting in the failure of any in flight read
   295  // or write operations. If this method returns with no errors, all connections
   296  // associated with this Client have been closed.
   297  func (c *Client) Disconnect(ctx context.Context) error {
   298  	if c.logger != nil {
   299  		defer c.logger.Close()
   300  	}
   301  
   302  	if ctx == nil {
   303  		ctx = context.Background()
   304  	}
   305  
   306  	if c.httpClient == internal.DefaultHTTPClient {
   307  		defer internal.CloseIdleHTTPConnections(c.httpClient)
   308  	}
   309  
   310  	c.endSessions(ctx)
   311  	if c.mongocryptdFLE != nil {
   312  		if err := c.mongocryptdFLE.disconnect(ctx); err != nil {
   313  			return err
   314  		}
   315  	}
   316  
   317  	if c.internalClientFLE != nil {
   318  		if err := c.internalClientFLE.Disconnect(ctx); err != nil {
   319  			return err
   320  		}
   321  	}
   322  
   323  	if c.keyVaultClientFLE != nil && c.keyVaultClientFLE != c.internalClientFLE && c.keyVaultClientFLE != c {
   324  		if err := c.keyVaultClientFLE.Disconnect(ctx); err != nil {
   325  			return err
   326  		}
   327  	}
   328  	if c.metadataClientFLE != nil && c.metadataClientFLE != c.internalClientFLE && c.metadataClientFLE != c {
   329  		if err := c.metadataClientFLE.Disconnect(ctx); err != nil {
   330  			return err
   331  		}
   332  	}
   333  	if c.cryptFLE != nil {
   334  		c.cryptFLE.Close()
   335  	}
   336  
   337  	if disconnector, ok := c.deployment.(driver.Disconnector); ok {
   338  		return replaceErrors(disconnector.Disconnect(ctx))
   339  	}
   340  
   341  	return nil
   342  }
   343  
   344  // Ping sends a ping command to verify that the client can connect to the deployment.
   345  //
   346  // The rp parameter is used to determine which server is selected for the operation.
   347  // If it is nil, the client's read preference is used.
   348  //
   349  // If the server is down, Ping will try to select a server until the client's server selection timeout expires.
   350  // This can be configured through the ClientOptions.SetServerSelectionTimeout option when creating a new Client.
   351  // After the timeout expires, a server selection error is returned.
   352  //
   353  // Using Ping reduces application resilience because applications starting up will error if the server is temporarily
   354  // unavailable or is failing over (e.g. during autoscaling due to a load spike).
   355  func (c *Client) Ping(ctx context.Context, rp *readpref.ReadPref) error {
   356  	if ctx == nil {
   357  		ctx = context.Background()
   358  	}
   359  
   360  	if rp == nil {
   361  		rp = c.readPreference
   362  	}
   363  
   364  	db := c.Database("admin")
   365  	res := db.RunCommand(ctx, bson.D{
   366  		{"ping", 1},
   367  	}, options.RunCmd().SetReadPreference(rp))
   368  
   369  	return replaceErrors(res.Err())
   370  }
   371  
   372  // StartSession starts a new session configured with the given options.
   373  //
   374  // StartSession does not actually communicate with the server and will not error if the client is
   375  // disconnected.
   376  //
   377  // StartSession is safe to call from multiple goroutines concurrently. However, Sessions returned by StartSession are
   378  // not safe for concurrent use by multiple goroutines.
   379  //
   380  // If the DefaultReadConcern, DefaultWriteConcern, or DefaultReadPreference options are not set, the client's read
   381  // concern, write concern, or read preference will be used, respectively.
   382  func (c *Client) StartSession(opts ...*options.SessionOptions) (Session, error) {
   383  	if c.sessionPool == nil {
   384  		return nil, ErrClientDisconnected
   385  	}
   386  
   387  	sopts := options.MergeSessionOptions(opts...)
   388  	coreOpts := &session.ClientOptions{
   389  		DefaultReadConcern:    c.readConcern,
   390  		DefaultReadPreference: c.readPreference,
   391  		DefaultWriteConcern:   c.writeConcern,
   392  	}
   393  	if sopts.CausalConsistency != nil {
   394  		coreOpts.CausalConsistency = sopts.CausalConsistency
   395  	}
   396  	if sopts.DefaultReadConcern != nil {
   397  		coreOpts.DefaultReadConcern = sopts.DefaultReadConcern
   398  	}
   399  	if sopts.DefaultWriteConcern != nil {
   400  		coreOpts.DefaultWriteConcern = sopts.DefaultWriteConcern
   401  	}
   402  	if sopts.DefaultReadPreference != nil {
   403  		coreOpts.DefaultReadPreference = sopts.DefaultReadPreference
   404  	}
   405  	if sopts.DefaultMaxCommitTime != nil {
   406  		coreOpts.DefaultMaxCommitTime = sopts.DefaultMaxCommitTime
   407  	}
   408  	if sopts.Snapshot != nil {
   409  		coreOpts.Snapshot = sopts.Snapshot
   410  	}
   411  
   412  	sess, err := session.NewClientSession(c.sessionPool, c.id, coreOpts)
   413  	if err != nil {
   414  		return nil, replaceErrors(err)
   415  	}
   416  
   417  	// Writes are not retryable on standalones, so let operation determine whether to retry
   418  	sess.RetryWrite = false
   419  	sess.RetryRead = c.retryReads
   420  
   421  	return &sessionImpl{
   422  		clientSession: sess,
   423  		client:        c,
   424  		deployment:    c.deployment,
   425  	}, nil
   426  }
   427  
   428  func (c *Client) endSessions(ctx context.Context) {
   429  	if c.sessionPool == nil {
   430  		return
   431  	}
   432  
   433  	sessionIDs := c.sessionPool.IDSlice()
   434  	op := operation.NewEndSessions(nil).ClusterClock(c.clock).Deployment(c.deployment).
   435  		ServerSelector(description.ReadPrefSelector(readpref.PrimaryPreferred())).CommandMonitor(c.monitor).
   436  		Database("admin").Crypt(c.cryptFLE).ServerAPI(c.serverAPI)
   437  
   438  	totalNumIDs := len(sessionIDs)
   439  	var currentBatch []bsoncore.Document
   440  	for i := 0; i < totalNumIDs; i++ {
   441  		currentBatch = append(currentBatch, sessionIDs[i])
   442  
   443  		// If we are at the end of a batch or the end of the overall IDs array, execute the operation.
   444  		if ((i+1)%endSessionsBatchSize) == 0 || i == totalNumIDs-1 {
   445  			// Ignore all errors when ending sessions.
   446  			_, marshalVal, err := bson.MarshalValue(currentBatch)
   447  			if err == nil {
   448  				_ = op.SessionIDs(marshalVal).Execute(ctx)
   449  			}
   450  
   451  			currentBatch = currentBatch[:0]
   452  		}
   453  	}
   454  }
   455  
   456  func (c *Client) configureAutoEncryption(clientOpts *options.ClientOptions) error {
   457  	c.encryptedFieldsMap = clientOpts.AutoEncryptionOptions.EncryptedFieldsMap
   458  	if err := c.configureKeyVaultClientFLE(clientOpts); err != nil {
   459  		return err
   460  	}
   461  	if err := c.configureMetadataClientFLE(clientOpts); err != nil {
   462  		return err
   463  	}
   464  
   465  	mc, err := c.newMongoCrypt(clientOpts.AutoEncryptionOptions)
   466  	if err != nil {
   467  		return err
   468  	}
   469  
   470  	// If the crypt_shared library was not loaded, try to spawn and connect to mongocryptd.
   471  	if mc.CryptSharedLibVersionString() == "" {
   472  		mongocryptdFLE, err := newMongocryptdClient(clientOpts.AutoEncryptionOptions)
   473  		if err != nil {
   474  			return err
   475  		}
   476  		c.mongocryptdFLE = mongocryptdFLE
   477  	}
   478  
   479  	c.configureCryptFLE(mc, clientOpts.AutoEncryptionOptions)
   480  	return nil
   481  }
   482  
   483  func (c *Client) getOrCreateInternalClient(clientOpts *options.ClientOptions) (*Client, error) {
   484  	if c.internalClientFLE != nil {
   485  		return c.internalClientFLE, nil
   486  	}
   487  
   488  	internalClientOpts := options.MergeClientOptions(clientOpts)
   489  	internalClientOpts.AutoEncryptionOptions = nil
   490  	internalClientOpts.SetMinPoolSize(0)
   491  	var err error
   492  	c.internalClientFLE, err = NewClient(internalClientOpts)
   493  	return c.internalClientFLE, err
   494  }
   495  
   496  func (c *Client) configureKeyVaultClientFLE(clientOpts *options.ClientOptions) error {
   497  	// parse key vault options and create new key vault client
   498  	var err error
   499  	aeOpts := clientOpts.AutoEncryptionOptions
   500  	switch {
   501  	case aeOpts.KeyVaultClientOptions != nil:
   502  		c.keyVaultClientFLE, err = NewClient(aeOpts.KeyVaultClientOptions)
   503  	case clientOpts.MaxPoolSize != nil && *clientOpts.MaxPoolSize == 0:
   504  		c.keyVaultClientFLE = c
   505  	default:
   506  		c.keyVaultClientFLE, err = c.getOrCreateInternalClient(clientOpts)
   507  	}
   508  
   509  	if err != nil {
   510  		return err
   511  	}
   512  
   513  	dbName, collName := splitNamespace(aeOpts.KeyVaultNamespace)
   514  	c.keyVaultCollFLE = c.keyVaultClientFLE.Database(dbName).Collection(collName, keyVaultCollOpts)
   515  	return nil
   516  }
   517  
   518  func (c *Client) configureMetadataClientFLE(clientOpts *options.ClientOptions) error {
   519  	// parse key vault options and create new key vault client
   520  	aeOpts := clientOpts.AutoEncryptionOptions
   521  	if aeOpts.BypassAutoEncryption != nil && *aeOpts.BypassAutoEncryption {
   522  		// no need for a metadata client.
   523  		return nil
   524  	}
   525  	if clientOpts.MaxPoolSize != nil && *clientOpts.MaxPoolSize == 0 {
   526  		c.metadataClientFLE = c
   527  		return nil
   528  	}
   529  
   530  	var err error
   531  	c.metadataClientFLE, err = c.getOrCreateInternalClient(clientOpts)
   532  	return err
   533  }
   534  
   535  func (c *Client) newMongoCrypt(opts *options.AutoEncryptionOptions) (*mongocrypt.MongoCrypt, error) {
   536  	// convert schemas in SchemaMap to bsoncore documents
   537  	cryptSchemaMap := make(map[string]bsoncore.Document)
   538  	for k, v := range opts.SchemaMap {
   539  		schema, err := marshal(v, c.bsonOpts, c.registry)
   540  		if err != nil {
   541  			return nil, err
   542  		}
   543  		cryptSchemaMap[k] = schema
   544  	}
   545  
   546  	// convert schemas in EncryptedFieldsMap to bsoncore documents
   547  	cryptEncryptedFieldsMap := make(map[string]bsoncore.Document)
   548  	for k, v := range opts.EncryptedFieldsMap {
   549  		encryptedFields, err := marshal(v, c.bsonOpts, c.registry)
   550  		if err != nil {
   551  			return nil, err
   552  		}
   553  		cryptEncryptedFieldsMap[k] = encryptedFields
   554  	}
   555  
   556  	kmsProviders, err := marshal(opts.KmsProviders, c.bsonOpts, c.registry)
   557  	if err != nil {
   558  		return nil, fmt.Errorf("error creating KMS providers document: %v", err)
   559  	}
   560  
   561  	// Set the crypt_shared library override path from the "cryptSharedLibPath" extra option if one
   562  	// was set.
   563  	cryptSharedLibPath := ""
   564  	if val, ok := opts.ExtraOptions["cryptSharedLibPath"]; ok {
   565  		str, ok := val.(string)
   566  		if !ok {
   567  			return nil, fmt.Errorf(
   568  				`expected AutoEncryption extra option "cryptSharedLibPath" to be a string, but is a %T`, val)
   569  		}
   570  		cryptSharedLibPath = str
   571  	}
   572  
   573  	// Explicitly disable loading the crypt_shared library if requested. Note that this is ONLY
   574  	// intended for use from tests; there is no supported public API for explicitly disabling
   575  	// loading the crypt_shared library.
   576  	cryptSharedLibDisabled := false
   577  	if v, ok := opts.ExtraOptions["__cryptSharedLibDisabledForTestOnly"]; ok {
   578  		cryptSharedLibDisabled = v.(bool)
   579  	}
   580  
   581  	bypassAutoEncryption := opts.BypassAutoEncryption != nil && *opts.BypassAutoEncryption
   582  	bypassQueryAnalysis := opts.BypassQueryAnalysis != nil && *opts.BypassQueryAnalysis
   583  
   584  	mc, err := mongocrypt.NewMongoCrypt(mcopts.MongoCrypt().
   585  		SetKmsProviders(kmsProviders).
   586  		SetLocalSchemaMap(cryptSchemaMap).
   587  		SetBypassQueryAnalysis(bypassQueryAnalysis).
   588  		SetEncryptedFieldsMap(cryptEncryptedFieldsMap).
   589  		SetCryptSharedLibDisabled(cryptSharedLibDisabled || bypassAutoEncryption).
   590  		SetCryptSharedLibOverridePath(cryptSharedLibPath).
   591  		SetHTTPClient(opts.HTTPClient))
   592  	if err != nil {
   593  		return nil, err
   594  	}
   595  
   596  	var cryptSharedLibRequired bool
   597  	if val, ok := opts.ExtraOptions["cryptSharedLibRequired"]; ok {
   598  		b, ok := val.(bool)
   599  		if !ok {
   600  			return nil, fmt.Errorf(
   601  				`expected AutoEncryption extra option "cryptSharedLibRequired" to be a bool, but is a %T`, val)
   602  		}
   603  		cryptSharedLibRequired = b
   604  	}
   605  
   606  	// If the "cryptSharedLibRequired" extra option is set to true, check the MongoCrypt version
   607  	// string to confirm that the library was successfully loaded. If the version string is empty,
   608  	// return an error indicating that we couldn't load the crypt_shared library.
   609  	if cryptSharedLibRequired && mc.CryptSharedLibVersionString() == "" {
   610  		return nil, errors.New(
   611  			`AutoEncryption extra option "cryptSharedLibRequired" is true, but we failed to load the crypt_shared library`)
   612  	}
   613  
   614  	return mc, nil
   615  }
   616  
   617  //nolint:unused // the unused linter thinks that this function is unreachable because "c.newMongoCrypt" always panics without the "cse" build tag set.
   618  func (c *Client) configureCryptFLE(mc *mongocrypt.MongoCrypt, opts *options.AutoEncryptionOptions) {
   619  	bypass := opts.BypassAutoEncryption != nil && *opts.BypassAutoEncryption
   620  	kr := keyRetriever{coll: c.keyVaultCollFLE}
   621  	var cir collInfoRetriever
   622  	// If bypass is true, c.metadataClientFLE is nil and the collInfoRetriever
   623  	// will not be used. If bypass is false, to the parent client or the
   624  	// internal client.
   625  	if !bypass {
   626  		cir = collInfoRetriever{client: c.metadataClientFLE}
   627  	}
   628  
   629  	c.cryptFLE = driver.NewCrypt(&driver.CryptOptions{
   630  		MongoCrypt:           mc,
   631  		CollInfoFn:           cir.cryptCollInfo,
   632  		KeyFn:                kr.cryptKeys,
   633  		MarkFn:               c.mongocryptdFLE.markCommand,
   634  		TLSConfig:            opts.TLSConfig,
   635  		BypassAutoEncryption: bypass,
   636  	})
   637  }
   638  
   639  // validSession returns an error if the session doesn't belong to the client
   640  func (c *Client) validSession(sess *session.Client) error {
   641  	if sess != nil && sess.ClientID != c.id {
   642  		return ErrWrongClient
   643  	}
   644  	return nil
   645  }
   646  
   647  // Database returns a handle for a database with the given name configured with the given DatabaseOptions.
   648  func (c *Client) Database(name string, opts ...*options.DatabaseOptions) *Database {
   649  	return newDatabase(c, name, opts...)
   650  }
   651  
   652  // ListDatabases executes a listDatabases command and returns the result.
   653  //
   654  // The filter parameter must be a document containing query operators and can be used to select which
   655  // databases are included in the result. It cannot be nil. An empty document (e.g. bson.D{}) should be used to include
   656  // all databases.
   657  //
   658  // The opts parameter can be used to specify options for this operation (see the options.ListDatabasesOptions documentation).
   659  //
   660  // For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/listDatabases/.
   661  func (c *Client) ListDatabases(ctx context.Context, filter interface{}, opts ...*options.ListDatabasesOptions) (ListDatabasesResult, error) {
   662  	if ctx == nil {
   663  		ctx = context.Background()
   664  	}
   665  
   666  	sess := sessionFromContext(ctx)
   667  
   668  	err := c.validSession(sess)
   669  	if err != nil {
   670  		return ListDatabasesResult{}, err
   671  	}
   672  	if sess == nil && c.sessionPool != nil {
   673  		sess = session.NewImplicitClientSession(c.sessionPool, c.id)
   674  		defer sess.EndSession()
   675  	}
   676  
   677  	err = c.validSession(sess)
   678  	if err != nil {
   679  		return ListDatabasesResult{}, err
   680  	}
   681  
   682  	filterDoc, err := marshal(filter, c.bsonOpts, c.registry)
   683  	if err != nil {
   684  		return ListDatabasesResult{}, err
   685  	}
   686  
   687  	selector := description.CompositeSelector([]description.ServerSelector{
   688  		description.ReadPrefSelector(readpref.Primary()),
   689  		description.LatencySelector(c.localThreshold),
   690  	})
   691  	selector = makeReadPrefSelector(sess, selector, c.localThreshold)
   692  
   693  	ldo := options.MergeListDatabasesOptions(opts...)
   694  	op := operation.NewListDatabases(filterDoc).
   695  		Session(sess).ReadPreference(c.readPreference).CommandMonitor(c.monitor).
   696  		ServerSelector(selector).ClusterClock(c.clock).Database("admin").Deployment(c.deployment).Crypt(c.cryptFLE).
   697  		ServerAPI(c.serverAPI).Timeout(c.timeout)
   698  
   699  	if ldo.NameOnly != nil {
   700  		op = op.NameOnly(*ldo.NameOnly)
   701  	}
   702  	if ldo.AuthorizedDatabases != nil {
   703  		op = op.AuthorizedDatabases(*ldo.AuthorizedDatabases)
   704  	}
   705  
   706  	retry := driver.RetryNone
   707  	if c.retryReads {
   708  		retry = driver.RetryOncePerCommand
   709  	}
   710  	op.Retry(retry)
   711  
   712  	err = op.Execute(ctx)
   713  	if err != nil {
   714  		return ListDatabasesResult{}, replaceErrors(err)
   715  	}
   716  
   717  	return newListDatabasesResultFromOperation(op.Result()), nil
   718  }
   719  
   720  // ListDatabaseNames executes a listDatabases command and returns a slice containing the names of all of the databases
   721  // on the server.
   722  //
   723  // The filter parameter must be a document containing query operators and can be used to select which databases
   724  // are included in the result. It cannot be nil. An empty document (e.g. bson.D{}) should be used to include all
   725  // databases.
   726  //
   727  // The opts parameter can be used to specify options for this operation (see the options.ListDatabasesOptions
   728  // documentation.)
   729  //
   730  // For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/listDatabases/.
   731  func (c *Client) ListDatabaseNames(ctx context.Context, filter interface{}, opts ...*options.ListDatabasesOptions) ([]string, error) {
   732  	opts = append(opts, options.ListDatabases().SetNameOnly(true))
   733  
   734  	res, err := c.ListDatabases(ctx, filter, opts...)
   735  	if err != nil {
   736  		return nil, err
   737  	}
   738  
   739  	names := make([]string, 0)
   740  	for _, spec := range res.Databases {
   741  		names = append(names, spec.Name)
   742  	}
   743  
   744  	return names, nil
   745  }
   746  
   747  // WithSession creates a new SessionContext from the ctx and sess parameters and uses it to call the fn callback. The
   748  // SessionContext must be used as the Context parameter for any operations in the fn callback that should be executed
   749  // under the session.
   750  //
   751  // WithSession is safe to call from multiple goroutines concurrently. However, the SessionContext passed to the
   752  // WithSession callback function is not safe for concurrent use by multiple goroutines.
   753  //
   754  // If the ctx parameter already contains a Session, that Session will be replaced with the one provided.
   755  //
   756  // Any error returned by the fn callback will be returned without any modifications.
   757  func WithSession(ctx context.Context, sess Session, fn func(SessionContext) error) error {
   758  	return fn(NewSessionContext(ctx, sess))
   759  }
   760  
   761  // UseSession creates a new Session and uses it to create a new SessionContext, which is used to call the fn callback.
   762  // The SessionContext parameter must be used as the Context parameter for any operations in the fn callback that should
   763  // be executed under a session. After the callback returns, the created Session is ended, meaning that any in-progress
   764  // transactions started by fn will be aborted even if fn returns an error.
   765  //
   766  // UseSession is safe to call from multiple goroutines concurrently. However, the SessionContext passed to the
   767  // UseSession callback function is not safe for concurrent use by multiple goroutines.
   768  //
   769  // If the ctx parameter already contains a Session, that Session will be replaced with the newly created one.
   770  //
   771  // Any error returned by the fn callback will be returned without any modifications.
   772  func (c *Client) UseSession(ctx context.Context, fn func(SessionContext) error) error {
   773  	return c.UseSessionWithOptions(ctx, options.Session(), fn)
   774  }
   775  
   776  // UseSessionWithOptions operates like UseSession but uses the given SessionOptions to create the Session.
   777  //
   778  // UseSessionWithOptions is safe to call from multiple goroutines concurrently. However, the SessionContext passed to
   779  // the UseSessionWithOptions callback function is not safe for concurrent use by multiple goroutines.
   780  func (c *Client) UseSessionWithOptions(ctx context.Context, opts *options.SessionOptions, fn func(SessionContext) error) error {
   781  	defaultSess, err := c.StartSession(opts)
   782  	if err != nil {
   783  		return err
   784  	}
   785  
   786  	defer defaultSess.EndSession(ctx)
   787  	return fn(NewSessionContext(ctx, defaultSess))
   788  }
   789  
   790  // Watch returns a change stream for all changes on the deployment. See
   791  // https://www.mongodb.com/docs/manual/changeStreams/ for more information about change streams.
   792  //
   793  // The client must be configured with read concern majority or no read concern for a change stream to be created
   794  // successfully.
   795  //
   796  // The pipeline parameter must be an array of documents, each representing a pipeline stage. The pipeline cannot be
   797  // nil or empty. The stage documents must all be non-nil. See https://www.mongodb.com/docs/manual/changeStreams/ for a list
   798  // of pipeline stages that can be used with change streams. For a pipeline of bson.D documents, the mongo.Pipeline{}
   799  // type can be used.
   800  //
   801  // The opts parameter can be used to specify options for change stream creation (see the options.ChangeStreamOptions
   802  // documentation).
   803  func (c *Client) Watch(ctx context.Context, pipeline interface{},
   804  	opts ...*options.ChangeStreamOptions) (*ChangeStream, error) {
   805  	if c.sessionPool == nil {
   806  		return nil, ErrClientDisconnected
   807  	}
   808  
   809  	csConfig := changeStreamConfig{
   810  		readConcern:    c.readConcern,
   811  		readPreference: c.readPreference,
   812  		client:         c,
   813  		bsonOpts:       c.bsonOpts,
   814  		registry:       c.registry,
   815  		streamType:     ClientStream,
   816  		crypt:          c.cryptFLE,
   817  	}
   818  
   819  	return newChangeStream(ctx, csConfig, pipeline, opts...)
   820  }
   821  
   822  // NumberSessionsInProgress returns the number of sessions that have been started for this client but have not been
   823  // closed (i.e. EndSession has not been called).
   824  func (c *Client) NumberSessionsInProgress() int {
   825  	// The underlying session pool uses an int64 for checkedOut to allow atomic
   826  	// access. We convert to an int here to maintain backward compatibility with
   827  	// older versions of the driver that did not atomically access checkedOut.
   828  	return int(c.sessionPool.CheckedOut())
   829  }
   830  
   831  // Timeout returns the timeout set for this client.
   832  func (c *Client) Timeout() *time.Duration {
   833  	return c.timeout
   834  }
   835  
   836  func (c *Client) createBaseCursorOptions() driver.CursorOptions {
   837  	return driver.CursorOptions{
   838  		CommandMonitor: c.monitor,
   839  		Crypt:          c.cryptFLE,
   840  		ServerAPI:      c.serverAPI,
   841  	}
   842  }
   843  
   844  // newLogger will use the LoggerOptions to create an internal logger and publish
   845  // messages using a LogSink.
   846  func newLogger(opts *options.LoggerOptions) (*logger.Logger, error) {
   847  	// If there are no logger options, then create a default logger.
   848  	if opts == nil {
   849  		opts = options.Logger()
   850  	}
   851  
   852  	// If there are no component-level options and the environment does not
   853  	// contain component variables, then do nothing.
   854  	if (opts.ComponentLevels == nil || len(opts.ComponentLevels) == 0) &&
   855  		!logger.EnvHasComponentVariables() {
   856  
   857  		return nil, nil
   858  	}
   859  
   860  	// Otherwise, collect the component-level options and create a logger.
   861  	componentLevels := make(map[logger.Component]logger.Level)
   862  	for component, level := range opts.ComponentLevels {
   863  		componentLevels[logger.Component(component)] = logger.Level(level)
   864  	}
   865  
   866  	return logger.New(opts.Sink, opts.MaxDocumentLength, componentLevels)
   867  }