diff options
author | David Golden <xdg@xdg.me> | 2018-10-04 10:08:32 -0400 |
---|---|---|
committer | David Golden <xdg@xdg.me> | 2018-10-04 10:37:03 -0400 |
commit | f9436146f64ad7caa2ce525c1d75a85f918d68c0 (patch) | |
tree | 40c0ff2d4ab32b2fabde9f1c5c61d37d0bb8e919 /src/mongo/gotools | |
parent | 6d71a539ce484770c9830f59e3984130a7100dbd (diff) | |
download | mongo-f9436146f64ad7caa2ce525c1d75a85f918d68c0.tar.gz |
Import tools: d6c691f3ed621e087db9f064c5ca872fbb7096ca from branch v4.1
ref: e914484acd..d6c691f3ed
for: 4.1.4
TOOLS-2069 mongoreplay does not support SCRAM-SHA-256
TOOLS-2102 Mongorestore does not check for errors decoding the oplog.bson file
TOOLS-2118 Fix typos in code comments
TOOLS-2131 mongorestore hang in replaying oplog with --archive and --oplogReplay option
Diffstat (limited to 'src/mongo/gotools')
48 files changed, 188 insertions, 84 deletions
diff --git a/src/mongo/gotools/binaryurl.py b/src/mongo/gotools/binaryurl.py index ce68cf1e431..df18a8146f1 100644 --- a/src/mongo/gotools/binaryurl.py +++ b/src/mongo/gotools/binaryurl.py @@ -48,7 +48,11 @@ if opts.version == "latest" or isVersionGreaterOrEqual(opts.version,"4.1.0"): opts.target = 'windows_x86_64-2012plus' def isCorrectVersion(version): - actual = version["version"].split(".") + parts = version["version"].split("-") + # always skip '-rcX' versions + if len(parts) > 1: + return False + actual = parts[0].split(".") desired = opts.version.split(".") for i in range(len(desired)): if desired[i] and not actual[i] == desired[i]: diff --git a/src/mongo/gotools/common/archive/demultiplexer.go b/src/mongo/gotools/common/archive/demultiplexer.go index 4a1e074dcf4..c1659708115 100644 --- a/src/mongo/gotools/common/archive/demultiplexer.go +++ b/src/mongo/gotools/common/archive/demultiplexer.go @@ -270,11 +270,11 @@ func (receiver *RegularCollectionReceiver) Read(r []byte) (int, error) { return 0, receiver.err } if wLen > db.MaxBSONSize { - return 0, fmt.Errorf("incomming buffer size is too big %v", wLen) + return 0, fmt.Errorf("incoming buffer size is too big %v", wLen) } rLen := len(r) if wLen > rLen { - // if the incomming write size is larger then the incomming read buffer then we need to accept + // if the incoming write size is larger then the incoming read buffer then we need to accept // the write in a larger buffer, fill the read buffer, then cache the remainder receiver.partialReadBuf = receiver.partialReadArray[:wLen] receiver.readBufChan <- receiver.partialReadBuf @@ -303,7 +303,7 @@ func (receiver *RegularCollectionReceiver) Pos() int64 { // Open is part of the intents.file interface. It creates the chan's in the // RegularCollectionReceiver and adds the RegularCollectionReceiver to the set of -// RegularCollectonReceivers in the demultiplexer +// RegularCollectionReceivers in the demultiplexer func (receiver *RegularCollectionReceiver) Open() error { // TODO move this implementation to some non intents.file method, to be called from prioritizer.Get // So that we don't have to enable this double open stuff. @@ -330,7 +330,7 @@ func (receiver *RegularCollectionReceiver) ReleaseIOBuffer() { func (receiver *RegularCollectionReceiver) Write(buf []byte) (int, error) { // As a writer, we need to write first, so that the reader can properly detect EOF // Additionally, the reader needs to know the write size, so that it can give us a - // properly sized buffer. Sending the incomming buffersize fills both of these needs. + // properly sized buffer. Sending the incoming buffersize fills both of these needs. receiver.readLenChan <- len(buf) // Receive from the reader a buffer to put the bytes into readBuf := <-receiver.readBufChan @@ -356,7 +356,7 @@ func (receiver *RegularCollectionReceiver) Close() error { return nil } -// SpecialCollectionCache implemnts both DemuxOut as well as intents.file +// SpecialCollectionCache implements both DemuxOut as well as intents.file type SpecialCollectionCache struct { pos int64 // updated atomically, aligned at the beginning of the struct Intent *intents.Intent @@ -438,7 +438,7 @@ func (*MutedCollection) Sum64() (uint64, bool) { //===== Archive Manager Prioritizer ===== -// NewPrioritizer careates a new Prioritizer and hooks up its Namespace channels to the ones in demux +// NewPrioritizer creates a new Prioritizer and hooks up its Namespace channels to the ones in demux func (demux *Demultiplexer) NewPrioritizer(mgr *intents.Manager) *Prioritizer { return &Prioritizer{ NamespaceChan: demux.NamespaceChan, diff --git a/src/mongo/gotools/common/archive/multiplexer.go b/src/mongo/gotools/common/archive/multiplexer.go index 1f94ad50292..fe52b3d520d 100644 --- a/src/mongo/gotools/common/archive/multiplexer.go +++ b/src/mongo/gotools/common/archive/multiplexer.go @@ -43,7 +43,7 @@ type notifier interface { } // NewMultiplexer creates a Multiplexer and populates its Control/Completed chans -// it takes a WriteCloser, which is where in imputs will get multiplexed on to, +// it takes a WriteCloser, which is where in inputs will get multiplexed on to, // and it takes a notifier, which should allow the multiplexer to ask for the shutdown // of the inputs. func NewMultiplexer(out io.WriteCloser, shutdownInputs notifier) *Multiplexer { diff --git a/src/mongo/gotools/common/archive/parser.go b/src/mongo/gotools/common/archive/parser.go index 227f93ab479..eb53d4f0774 100644 --- a/src/mongo/gotools/common/archive/parser.go +++ b/src/mongo/gotools/common/archive/parser.go @@ -64,7 +64,7 @@ func newParserWrappedError(msg string, err error) error { } // readBSONOrTerminator reads at least four bytes, determines -// if the first four bytes are a terminator, a bson lenght, or something else. +// if the first four bytes are a terminator, a bson length, or something else. // If they are a terminator, true,nil are returned. If they are a BSON length, // then the remainder of the BSON document are read in to the parser, otherwise // an error is returned. diff --git a/src/mongo/gotools/common/connstring/connstring.go b/src/mongo/gotools/common/connstring/connstring.go index 503bf74bafd..2437ff32406 100644 --- a/src/mongo/gotools/common/connstring/connstring.go +++ b/src/mongo/gotools/common/connstring/connstring.go @@ -528,7 +528,7 @@ type extractedDatabase struct { db string } -// extractDatabaseFromURI is a helper function to retreive information about +// extractDatabaseFromURI is a helper function to retrieve information about // the database from the passed in URI. It accepts as an argument the currently // parsed URI and returns the remainder of the uri, the database it found, // and any error it encounters while parsing. diff --git a/src/mongo/gotools/common/db/bson_stream.go b/src/mongo/gotools/common/db/bson_stream.go index a8a9551b932..4679b9bdde4 100644 --- a/src/mongo/gotools/common/db/bson_stream.go +++ b/src/mongo/gotools/common/db/bson_stream.go @@ -80,7 +80,7 @@ func (dbs *DecodedBSONSource) Next(result interface{}) bool { // LoadNext reads and returns the next BSON document in the stream. If the // BSONSource was created with NewBSONSource then each returned []byte will be // a slice of a single reused I/O buffer. If the BSONSource was created with -// NewBufferlessBSONSource then each returend []byte will be individually +// NewBufferlessBSONSource then each returned []byte will be individually // allocated func (bs *BSONSource) LoadNext() []byte { var into []byte diff --git a/src/mongo/gotools/common/db/command.go b/src/mongo/gotools/common/db/command.go index d427b3b431b..24df88aa5ef 100644 --- a/src/mongo/gotools/common/db/command.go +++ b/src/mongo/gotools/common/db/command.go @@ -210,7 +210,7 @@ func (sp *SessionProvider) SupportsWriteCommands() (bool, error) { return (masterDoc.Ok == 1 && masterDoc.MaxWire >= 2), nil } -// FindOne retuns the first document in the collection and database that matches +// FindOne returns the first document in the collection and database that matches // the query after skip, sort and query flags are applied. func (sp *SessionProvider) FindOne(db, collection string, skip int, query interface{}, sort []string, into interface{}, flags int) error { session, err := sp.GetSession() diff --git a/src/mongo/gotools/common/db/namespaces.go b/src/mongo/gotools/common/db/namespaces.go index 2e7171bdc51..b2d5c7e8dda 100644 --- a/src/mongo/gotools/common/db/namespaces.go +++ b/src/mongo/gotools/common/db/namespaces.go @@ -48,7 +48,7 @@ func (ci *CollectionInfo) GetUUID() string { return "" } -// IsNoCmd reeturns true if err indicates a query command is not supported, +// IsNoCmd returns true if err indicates a query command is not supported, // otherwise, returns false. func IsNoCmd(err error) bool { e, ok := err.(*mgo.QueryError) @@ -79,7 +79,7 @@ func buildBsonArray(iter *mgo.Iter) ([]bson.D, error) { } -// GetIndexes returns an iterator to thethe raw index info for a collection by +// GetIndexes returns an iterator to the raw index info for a collection by // using the listIndexes command if available, or by falling back to querying // against system.indexes (pre-3.0 systems). nil is returned if the collection // does not exist. diff --git a/src/mongo/gotools/common/db/write_concern.go b/src/mongo/gotools/common/db/write_concern.go index 7eee9b1ad0e..b7b0377bd70 100644 --- a/src/mongo/gotools/common/db/write_concern.go +++ b/src/mongo/gotools/common/db/write_concern.go @@ -43,9 +43,9 @@ func constructWCObject(writeConcern string) (sessionSafety *mgo.Safe, err error) jsonWriteConcern := map[string]interface{}{} if err = json.Unmarshal([]byte(writeConcern), &jsonWriteConcern); err != nil { - // if the writeConcern string can not be unmarshaled into JSON, this + // if the writeConcern string cannot be unmarshalled into JSON, this // allows a default to the old behavior wherein the entire argument - // passed in is assigned to the 'w' field - thus allowing users pass + // passed in is assigned to the 'w' field - thus allowing users to pass // a write concern that looks like: "majority", 0, "4", etc. wValue, err := strconv.Atoi(writeConcern) if err != nil { diff --git a/src/mongo/gotools/common/failpoint/failpoint.go b/src/mongo/gotools/common/failpoint/failpoint.go index c7140f6a8f1..e6cdd59b28f 100644 --- a/src/mongo/gotools/common/failpoint/failpoint.go +++ b/src/mongo/gotools/common/failpoint/failpoint.go @@ -6,7 +6,7 @@ // +build failpoints -// Package failpoint implements triggers for custom debugging behavoir +// Package failpoint implements triggers for custom debugging behavior package failpoint import ( diff --git a/src/mongo/gotools/common/intents/intent.go b/src/mongo/gotools/common/intents/intent.go index 47227950e72..4b82567e612 100644 --- a/src/mongo/gotools/common/intents/intent.go +++ b/src/mongo/gotools/common/intents/intent.go @@ -119,7 +119,8 @@ func (it *Intent) IsSystemProfile() bool { } func (intent *Intent) IsSpecialCollection() bool { - return intent.IsSystemIndexes() || intent.IsUsers() || intent.IsRoles() || intent.IsAuthVersion() || intent.IsSystemProfile() || intent.IsOplog() + // can't see oplog as special collection because when restore from archive it need to be a RegularCollectionReceiver + return intent.IsSystemIndexes() || intent.IsUsers() || intent.IsRoles() || intent.IsAuthVersion() || intent.IsSystemProfile() } func (it *Intent) IsView() bool { diff --git a/src/mongo/gotools/common/json/decode.go b/src/mongo/gotools/common/json/decode.go index 1cb540c32be..1e23aa69815 100644 --- a/src/mongo/gotools/common/json/decode.go +++ b/src/mongo/gotools/common/json/decode.go @@ -63,10 +63,10 @@ import ( // // The JSON null value unmarshals into an interface, map, pointer, or slice // by setting that Go value to nil. Because null is often used in JSON to mean -// ``not present,'' unmarshaling a JSON null into any other Go type has no effect +// ``not present,'' unmarshalling a JSON null into any other Go type has no effect // on the value and produces no error. // -// When unmarshaling quoted strings, invalid UTF-8 or +// When unmarshalling quoted strings, invalid UTF-8 or // invalid UTF-16 surrogate pairs are not treated as an error. // Instead, they are replaced by the Unicode replacement // character U+FFFD. diff --git a/src/mongo/gotools/common/json/decode_test.go b/src/mongo/gotools/common/json/decode_test.go index ba106700f38..107321d1f5e 100644 --- a/src/mongo/gotools/common/json/decode_test.go +++ b/src/mongo/gotools/common/json/decode_test.go @@ -36,7 +36,7 @@ type V struct { F3 Number } -// ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshaling with and +// ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshalling with and // without UseNumber var ifaceNumAsFloat64 = map[string]interface{}{ "k1": float64(1), @@ -100,14 +100,14 @@ type ustructText struct { } var ( - um0, um1 unmarshaler // target2 of unmarshaling + um0, um1 unmarshaler // target2 of unmarshalling ump = &um1 umtrue = unmarshaler{true} umslice = []unmarshaler{{true}} umslicep = new([]unmarshaler) umstruct = ustruct{unmarshaler{true}} - um0T, um1T unmarshalerText // target2 of unmarshaling + um0T, um1T unmarshalerText // target2 of unmarshalling umpT = &um1T umtrueT = unmarshalerText{true} umsliceT = []unmarshalerText{{true}} diff --git a/src/mongo/gotools/common/json/mongo_extjson.go b/src/mongo/gotools/common/json/mongo_extjson.go index 0196c712b80..355975bbdb9 100644 --- a/src/mongo/gotools/common/json/mongo_extjson.go +++ b/src/mongo/gotools/common/json/mongo_extjson.go @@ -341,7 +341,7 @@ func (d *decodeState) getExtendedLiteral(item []byte) (interface{}, bool) { return d.getDate(), true case 'b': // Dbref return d.getDBRef(), true - case 'B': // DBRef or DBPoiner + case 'B': // DBRef or DBPointer switch item[2] { case 'R': // DBRef return d.getDBRef(), true diff --git a/src/mongo/gotools/common/testutil/testutil.go b/src/mongo/gotools/common/testutil/testutil.go index bf6ce7f0d62..5fdadc499e8 100644 --- a/src/mongo/gotools/common/testutil/testutil.go +++ b/src/mongo/gotools/common/testutil/testutil.go @@ -90,7 +90,7 @@ func CompareFCV(x, y string) (int, error) { return 0, err } - // Ensure left is the shorter one, flip logic if necesary + // Ensure left is the shorter one, flip logic if necessary inverter := 1 if len(right) < len(left) { inverter = -1 diff --git a/src/mongo/gotools/common/util/file.go b/src/mongo/gotools/common/util/file.go index 29d4b09b693..a46365de4b6 100644 --- a/src/mongo/gotools/common/util/file.go +++ b/src/mongo/gotools/common/util/file.go @@ -34,7 +34,7 @@ func GetFieldsFromFile(path string) ([]string, error) { } // ToUniversalPath returns the result of replacing each slash ('/') character -// in "path" with an OS-sepcific separator character. Multiple slashes are +// in "path" with an OS-specific separator character. Multiple slashes are // replaced by multiple separators func ToUniversalPath(path string) string { return filepath.FromSlash(path) diff --git a/src/mongo/gotools/import.data b/src/mongo/gotools/import.data index 5aa006c9b7d..dbc728f5802 100644 --- a/src/mongo/gotools/import.data +++ b/src/mongo/gotools/import.data @@ -1,5 +1,5 @@ { - "commit": "e914484acd3a6e6f0c384de565a5d353d67aed52", + "commit": "d6c691f3ed621e087db9f064c5ca872fbb7096ca", "github": "mongodb/mongo-tools.git", "vendor": "tools", "branch": "v4.1" diff --git a/src/mongo/gotools/mongodump/mongodump.go b/src/mongo/gotools/mongodump/mongodump.go index 16c68c8ae25..a9122fa8440 100644 --- a/src/mongo/gotools/mongodump/mongodump.go +++ b/src/mongo/gotools/mongodump/mongodump.go @@ -541,7 +541,7 @@ func (dump *MongoDump) DumpIntent(intent *intents.Intent, buffer resettableOutpu case dump.OutputOptions.ViewsAsCollections: // views have an implied aggregation which does not support snapshot fallthrough - case dump.InputOptions.TableScan || intent.IsSpecialCollection(): + case dump.InputOptions.TableScan || intent.IsSpecialCollection() || intent.IsOplog(): // ---forceTablesScan runs the query without snapshot enabled // The system.profile collection has no index on _id so can't be hinted. findQuery = session.DB(intent.DB).C(intent.C).Find(nil) @@ -602,7 +602,7 @@ func copyDocumentFilter(in []byte) ([]byte, error) { // dumpQueryToIntent takes an mgo Query, its intent, and a writer, performs the query, // and writes the raw bson results to the writer. Returns a final count of documents -// dumped, and any errors that occured. +// dumped, and any errors that occurred. func (dump *MongoDump) dumpQueryToIntent( query *mgo.Query, intent *intents.Intent, buffer resettableOutputBuffer) (dumpCount int64, err error) { return dump.dumpFilteredQueryToIntent(query, intent, buffer, copyDocumentFilter) @@ -611,7 +611,7 @@ func (dump *MongoDump) dumpQueryToIntent( // dumpFilterQueryToIntent takes an mgo Query, its intent, a writer, and a document filter, performs the query, // passes the results through the filter // and writes the raw bson results to the writer. Returns a final count of documents -// dumped, and any errors that occured. +// dumped, and any errors that occurred. func (dump *MongoDump) dumpFilteredQueryToIntent( query *mgo.Query, intent *intents.Intent, buffer resettableOutputBuffer, filter documentFilter) (dumpCount int64, err error) { @@ -677,7 +677,7 @@ func (dump *MongoDump) dumpIterToWriter( } // dumpFilteredIterToWriter takes an mgo iterator, a writer, and a pointer to -// a counter, and fiters and dumps the iterator's contents to the writer. +// a counter, and filters and dumps the iterator's contents to the writer. func (dump *MongoDump) dumpFilteredIterToWriter( iter *mgo.Iter, writer io.Writer, progressCount progress.Updateable, filter documentFilter) error { var termErr error diff --git a/src/mongo/gotools/mongodump/mongodump_test.go b/src/mongo/gotools/mongodump/mongodump_test.go index ff41afbeaaf..6601e2a3342 100644 --- a/src/mongo/gotools/mongodump/mongodump_test.go +++ b/src/mongo/gotools/mongodump/mongodump_test.go @@ -165,7 +165,7 @@ func getMatchingFiles(dir, pattern string) ([]string, error) { } // read all the database bson documents from dir and put it into another DB -// ignore the inddexes for now +// ignore the indexes for now func readBSONIntoDatabase(dir, restoreDBName string) error { if ok := fileDirExists(dir); !ok { return fmt.Errorf("error finding '%v' on local FS", dir) diff --git a/src/mongo/gotools/mongoexport/mongoexport.go b/src/mongo/gotools/mongoexport/mongoexport.go index 34fba65d101..d94ada163a5 100644 --- a/src/mongo/gotools/mongoexport/mongoexport.go +++ b/src/mongo/gotools/mongoexport/mongoexport.go @@ -417,7 +417,7 @@ func (exp *MongoExport) getExportOutput(out io.Writer) (ExportOutput, error) { } // getObjectFromByteArg takes an object in extended JSON, and converts it to an object that -// can be passed straight to db.collection.find(...) as a query or sort critera. +// can be passed straight to db.collection.find(...) as a query or sort criteria. // Returns an error if the string is not valid JSON, or extended JSON. func getObjectFromByteArg(queryRaw []byte) (map[string]interface{}, error) { parsedJSON := map[string]interface{}{} diff --git a/src/mongo/gotools/mongoimport/csv/reader.go b/src/mongo/gotools/mongoimport/csv/reader.go index 34dca6a8b72..f1b73764cd5 100644 --- a/src/mongo/gotools/mongoimport/csv/reader.go +++ b/src/mongo/gotools/mongoimport/csv/reader.go @@ -301,7 +301,7 @@ func (r *Reader) parseField() (haveField bool, delim rune, err error) { } // we don't want '"foo" "bar",' to look like '"foo""bar"' // which evaluates to 'foo"bar' - // so we explicitly test for the case that the trimed whitespace isn't + // so we explicitly test for the case that the trimmed whitespace isn't // followed by a '"' if err == nil && r1 == '"' { r.column-- diff --git a/src/mongo/gotools/mongoimport/dateconv/dateconv.go b/src/mongo/gotools/mongoimport/dateconv/dateconv.go index 4df7d292eeb..093d0cde3fd 100644 --- a/src/mongo/gotools/mongoimport/dateconv/dateconv.go +++ b/src/mongo/gotools/mongoimport/dateconv/dateconv.go @@ -76,7 +76,7 @@ var ( oracleStringReplacer = strings.NewReplacer(oracleReplacers...) ) -// FromOrace reformats a datetime layout string from the Oracle Database +// FromOracle reformats a datetime layout string from the Oracle Database // TO_DATE function into go's parse format. func FromOracle(layout string) string { return oracleStringReplacer.Replace(strings.ToUpper(layout)) diff --git a/src/mongo/gotools/mongoimport/mongoimport.go b/src/mongo/gotools/mongoimport/mongoimport.go index 890b1bc29c7..9617146d9e4 100644 --- a/src/mongo/gotools/mongoimport/mongoimport.go +++ b/src/mongo/gotools/mongoimport/mongoimport.go @@ -463,7 +463,7 @@ type flushInserter interface { } // runInsertionWorker is a helper to InsertDocuments - it reads document off -// the read channel and prepares then in batches for insertion into the databas +// the read channel and prepares then in batches for insertion into the database func (imp *MongoImport) runInsertionWorker(readDocs chan bson.D) (err error) { session, err := imp.SessionProvider.GetSession() if err != nil { diff --git a/src/mongo/gotools/mongoimport/options.go b/src/mongo/gotools/mongoimport/options.go index 6c8cf0a0e2a..149b1473a0b 100644 --- a/src/mongo/gotools/mongoimport/options.go +++ b/src/mongo/gotools/mongoimport/options.go @@ -64,7 +64,7 @@ type IngestOptions struct { // Modify the import process. // Always insert the documents if they are new (do NOT match --upsertFields). // For existing documents (match --upsertFields) in the database: - // "insert": Insert only, skip exisiting documents. + // "insert": Insert only, skip existing documents. // "upsert": Insert new documents or replace existing ones. // "merge": Insert new documents or modify existing ones; Preserve values in the database that are not overwritten. Mode string `long:"mode" choice:"insert" choice:"upsert" choice:"merge" description:"insert: insert only. upsert: insert or replace existing documents. merge: insert or modify existing documents. defaults to insert"` diff --git a/src/mongo/gotools/mongoreplay/assembly.go b/src/mongo/gotools/mongoreplay/assembly.go index 04a1290a458..0b13d8f33d5 100644 --- a/src/mongo/gotools/mongoreplay/assembly.go +++ b/src/mongo/gotools/mongoreplay/assembly.go @@ -411,7 +411,7 @@ type AssemblerOptions struct { // NOTE: If you can guarantee that packets going to a set of Assemblers will // contain information on different connections per Assembler (for example, // they're already hashed by PF_RING hashing or some other hashing mechanism), -// then we recommend you use a seperate StreamPool per Assembler, thus avoiding +// then we recommend you use a separate StreamPool per Assembler, thus avoiding // all lock contention. Only when different Assemblers could receive packets // for the same Stream should a StreamPool be shared between them. // diff --git a/src/mongo/gotools/mongoreplay/command_op.go b/src/mongo/gotools/mongoreplay/command_op.go index 68cbf22411e..32c23b44030 100644 --- a/src/mongo/gotools/mongoreplay/command_op.go +++ b/src/mongo/gotools/mongoreplay/command_op.go @@ -49,10 +49,10 @@ func (gmCommand *CommandGetMore) getCursorIDs() ([]int64, error) { return []int64{*gmCommand.cachedCursor}, err } -// setCursorIDs is an implementation of the cusorsRewriteable interface method. -// It takes an array of of cursors that will function as the new cursors for -// this operation. If there are more than one cursorIDs in the array, it -// errors, as it only ever expects one. It may also error if unmarshalling the +// setCursorIDs is an implementation of the cursorsRewriteable interface method. +// It takes an array of cursors that will function as the new cursors for +// this operation. If there is more than one cursorID in the array, it +// errors, as it only ever expects one. It may also error if unmarshalling the // underlying bson fails. func (gmCommand *CommandGetMore) setCursorIDs(newCursorIDs []int64) error { newDoc, newCursorID, err := setCursorID(gmCommand.CommandArgs, newCursorIDs) diff --git a/src/mongo/gotools/mongoreplay/command_reply.go b/src/mongo/gotools/mongoreplay/command_reply.go index d376018c51c..807bcf299b5 100644 --- a/src/mongo/gotools/mongoreplay/command_reply.go +++ b/src/mongo/gotools/mongoreplay/command_reply.go @@ -18,7 +18,7 @@ import ( // CommandReplyOp is a struct for parsing OP_COMMANDREPLY as defined here: // https://github.com/mongodb/mongo/blob/master/src/mongo/rpc/command_reply.h. -// Although this file parses the wire protocol message into a more useable +// Although this file parses the wire protocol message into a more usable // struct, it does not currently provide functionality to execute the operation, // as it is not implemented fully in llmgo. type CommandReplyOp struct { diff --git a/src/mongo/gotools/mongoreplay/cursors.go b/src/mongo/gotools/mongoreplay/cursors.go index fcb661c8a55..d12798571da 100644 --- a/src/mongo/gotools/mongoreplay/cursors.go +++ b/src/mongo/gotools/mongoreplay/cursors.go @@ -200,7 +200,7 @@ func newPreprocessCursorManager(opChan <-chan *RecordedOp) (*preprocessCursorMan switch castOp := parsedOp.(type) { case cursorsRewriteable: // If the op makes use of a cursor, such as a getmore or a killcursors, - // track this op and attemp to match it with the reply that contains its + // track this op and attempt to match it with the reply that contains its // cursor cursorIDs, err := castOp.getCursorIDs() if err != nil { @@ -257,9 +257,9 @@ func newPreprocessCursorManager(opChan <-chan *RecordedOp) (*preprocessCursorMan // playback, but was in the original recording file, GetCursor will block until // it receives the cursorID. GetCursor also takes the connection number that the // waiting operation will be played on so that it will not block if the op is -// somehow waiting for a reply that has not yet occured and is on the same +// somehow waiting for a reply that has not yet occurred and is on the same // connection. It takes a lock to prevent prevent concurrent accesses to its -// data structues and so that it can unlock while waiting for its cursorID +// data structures and so that it can unlock while waiting for its cursorID // without deadlocking other attempts to access its data. func (p *preprocessCursorManager) GetCursor(fileCursorID int64, connectionNum int64) (int64, bool) { p.RLock() diff --git a/src/mongo/gotools/mongoreplay/cursors_test.go b/src/mongo/gotools/mongoreplay/cursors_test.go index 86667b7e98f..06a7153536d 100644 --- a/src/mongo/gotools/mongoreplay/cursors_test.go +++ b/src/mongo/gotools/mongoreplay/cursors_test.go @@ -18,7 +18,7 @@ import ( // playback. It then calls SetCursor to simulate setting this CursorID from live // traffic. Finally, it gets the cursor from the preprocessCursorManager to // ensure the cursorID has been remapped correctly. It uses a select statement -// to establish a timeout incase the goroutine running GetCursor has not +// to establish a timeout in case the goroutine running GetCursor has not // returned because the cursorID was not set properly. func TestFetchingCursorFromPreprocessManager(t *testing.T) { fileCursor := int64(1234) @@ -189,7 +189,7 @@ func TestBlockOnUnresolvedCursor(t *testing.T) { t.Log("Verifying that successChan not closed before cursor was set") // Verify that its successChan is not closed, which indicates that - // GetCuror would block + // GetCursor would block select { case <-cursorInfo.successChan: t.Error("successChan closed before mapping was complete") diff --git a/src/mongo/gotools/mongoreplay/filter_test.go b/src/mongo/gotools/mongoreplay/filter_test.go index 5155f1556ee..c2c022cb2a0 100644 --- a/src/mongo/gotools/mongoreplay/filter_test.go +++ b/src/mongo/gotools/mongoreplay/filter_test.go @@ -434,7 +434,7 @@ func TestRemoveOpsAfterDuration(t *testing.T) { } } -// convienence function for adding a close method to an io.Writer +// convenience function for adding a close method to an io.Writer func NopWriteCloser(w io.Writer) io.WriteCloser { return &nopWriteCloser{w} } diff --git a/src/mongo/gotools/mongoreplay/message.go b/src/mongo/gotools/mongoreplay/message.go index 55ad3fd1d0e..e8e6609c30a 100644 --- a/src/mongo/gotools/mongoreplay/message.go +++ b/src/mongo/gotools/mongoreplay/message.go @@ -21,7 +21,7 @@ const MaxMessageSize = 48 * 1000 * 1000 type MsgHeader struct { // MessageLength is the total message size, including this header MessageLength int32 - // RequestID is the identifier for this miessage + // RequestID is the identifier for this message RequestID int32 // ResponseTo is the RequestID of the message being responded to; // used in DB responses @@ -76,7 +76,7 @@ func (m *MsgHeader) WriteTo(w io.Writer) (int64, error) { return n, nil } -// LooksReal does a best efffort to detect if a MsgHeadr is not invalid +// LooksReal does a best effort to detect if a MsgHeader is valid func (m *MsgHeader) LooksReal() bool { // AFAIK, the smallest wire protocol message possible is a 24 byte // KILL_CURSORS_OP diff --git a/src/mongo/gotools/mongoreplay/monitor.go b/src/mongo/gotools/mongoreplay/monitor.go index 27a82254673..a1cdae26d45 100644 --- a/src/mongo/gotools/mongoreplay/monitor.go +++ b/src/mongo/gotools/mongoreplay/monitor.go @@ -56,7 +56,7 @@ func (gen *RegularStatGenerator) AddUnresolvedOp(op *RecordedOp, parsedOp Op, re // // recordedReply is the just received reply in the form of a RecordedOp, which // contains additional metadata. parsedReply is the same reply, parsed so that -// the payload of the op can be accesssed. replyStat is the OpStat created by +// the payload of the op can be accessed. replyStat is the OpStat created by // the GenerateOpStat function, containing computed metadata about the reply. func (gen *RegularStatGenerator) ResolveOp(recordedReply *RecordedOp, reply Replyable, replyStat *OpStat) *OpStat { result := &OpStat{} diff --git a/src/mongo/gotools/mongoreplay/pcap_test.go b/src/mongo/gotools/mongoreplay/pcap_test.go index 247a8672d48..0f8f8f7b6d3 100644 --- a/src/mongo/gotools/mongoreplay/pcap_test.go +++ b/src/mongo/gotools/mongoreplay/pcap_test.go @@ -36,7 +36,7 @@ func TestOpCommandFromPcapFileLiveDB(t *testing.T) { TestNum int `bson:"op_command_test"` }{} - t.Log("Querying database to ensure insert occured successfully") + t.Log("Querying database to ensure insert occurred successfully") ind := 1 for iter.Next(&result) { if result.TestNum != ind { diff --git a/src/mongo/gotools/mongoreplay/play_livedb_test.go b/src/mongo/gotools/mongoreplay/play_livedb_test.go index e44819c1ef9..e56a526a368 100644 --- a/src/mongo/gotools/mongoreplay/play_livedb_test.go +++ b/src/mongo/gotools/mongoreplay/play_livedb_test.go @@ -182,7 +182,7 @@ func TestOpInsertLiveDB(t *testing.T) { result := testDoc{} // iterate over the results of the query and ensure they match expected documents - t.Log("Querying database to ensure insert occured successfully") + t.Log("Querying database to ensure insert occurred successfully") for iter.Next(&result) { t.Logf("Query result: %#v\n", result) if result.DocumentNumber != ind { @@ -306,7 +306,7 @@ func TestUpdateOpLiveDB(t *testing.T) { }{} // iterate over the results of the query and ensure they match expected documents - t.Log("Querying database to ensure insert occured successfully") + t.Log("Querying database to ensure insert occurred successfully") for iter.Next(&result) { t.Logf("Query result: %#v\n", result) if result.DocumentNumber != ind { @@ -452,10 +452,10 @@ func TestOpGetMoreLiveDB(t *testing.T) { // TestOpGetMoreMultiCursorLiveDB tests the functionality of getmores using // multiple cursors against a live database. It generates a series of inserts -// followed by two seperate queries. It then uses each of those queries to +// followed by two separate queries. It then uses each of those queries to // generate multiple getmores. TestOpGetMoreMultiCursorLiveDB uses a // BufferedStatCollector to ensure that each getmore played against the database -// is executed and recieves the response expected +// is executed and receives the response expected func TestOpGetMoreMultiCursorLiveDB(t *testing.T) { if err := teardownDB(); err != nil { t.Error(err) @@ -570,11 +570,11 @@ func TestOpGetMoreMultiCursorLiveDB(t *testing.T) { // TestOpKillCursorsLiveDB tests the functionality of killcursors using multiple // cursors against a live database. It generates a series of inserts followed -// by two seperate queries. It then uses each of those queries to generate +// by two separate queries. It then uses each of those queries to generate // multiple getmores. Finally, it runs a killcursors op and one getmore for // each killed cursor TestOpKillCursorsLiveDB uses a BufferedStatCollector to // ensure that each killcursors played against the database is executed and -// recieves the response expected +// receives the response expected func TestOpKillCursorsLiveDB(t *testing.T) { if err := teardownDB(); err != nil { t.Error(err) @@ -745,7 +745,7 @@ func TestCommandOpInsertLiveDB(t *testing.T) { result := testDoc{} // iterate over the results of the query and ensure they match expected documents - t.Log("Querying database to ensure insert occured successfully") + t.Log("Querying database to ensure insert occurred successfully") for iter.Next(&result) { t.Logf("Query result: %#v\n", result) if result.DocumentNumber != ind { @@ -948,7 +948,7 @@ func TestMsgOpInsertLiveDB(t *testing.T) { result := testDoc{} // iterate over the results of the query and ensure they match expected documents - t.Log("Querying database to ensure insert occured successfully") + t.Log("Querying database to ensure insert occurred successfully") for iter.Next(&result) { t.Logf("Query result: %#v\n", result) if result.DocumentNumber != ind { diff --git a/src/mongo/gotools/mongoreplay/record.go b/src/mongo/gotools/mongoreplay/record.go index 125e2893843..00b1e103a0e 100644 --- a/src/mongo/gotools/mongoreplay/record.go +++ b/src/mongo/gotools/mongoreplay/record.go @@ -172,7 +172,7 @@ func Record(ctx *packetHandlerContext, var fail error for op := range ctx.mongoOpStream.Ops { // since we don't currently have a way to shutdown packetHandler.Handle() - // continue to read from ctx.mongoOpStream.Ops even after a faltal error + // continue to read from ctx.mongoOpStream.Ops even after a fatal error if fail != nil { toolDebugLogger.Logvf(DebugHigh, "not recording op because of record error %v", fail) continue diff --git a/src/mongo/gotools/mongoreplay/reply_op.go b/src/mongo/gotools/mongoreplay/reply_op.go index ec26138902c..546f4433d6a 100644 --- a/src/mongo/gotools/mongoreplay/reply_op.go +++ b/src/mongo/gotools/mongoreplay/reply_op.go @@ -125,7 +125,7 @@ func stringifyReplyDocs(d []bson.Raw) string { // getCursorID implements the Replyable interface method. It returns the // cursorID stored in this reply. It returns an error if there is an issue -// unmarshaling the underlying bson. It caches the cursorID in the ReplyOp +// unmarshalling the underlying bson. It caches the cursorID in the ReplyOp // struct so that subsequent calls to this function do not incur cost of // unmarshalling the bson each time. func (op *ReplyOp) getCursorID() (int64, error) { diff --git a/src/mongo/gotools/mongoreplay/stat_format.go b/src/mongo/gotools/mongoreplay/stat_format.go index 25125e8ea88..effc5b76d43 100644 --- a/src/mongo/gotools/mongoreplay/stat_format.go +++ b/src/mongo/gotools/mongoreplay/stat_format.go @@ -51,7 +51,7 @@ type OpStat struct { PlayAt *time.Time `json:"play_at,omitempty"` // PlaybackLagMicros is the time difference in microseconds between the time - // that the operation was supposed to be played, and the time it was actualy played. + // that the operation was supposed to be played, and the time it was actually played. // High values indicate that playback is falling behind the intended rate. PlaybackLagMicros int64 `json:"playbacklag_us,omitempty"` diff --git a/src/mongo/gotools/mongoreplay/util.go b/src/mongo/gotools/mongoreplay/util.go index 8f38bfbc136..9851e5479bd 100644 --- a/src/mongo/gotools/mongoreplay/util.go +++ b/src/mongo/gotools/mongoreplay/util.go @@ -258,7 +258,7 @@ func extractErrorsFromDoc(doc *bson.D) []error { return errors } -// readCStringFromReader reads a null turminated string from an io.Reader. +// readCStringFromReader reads a null terminated string from an io.Reader. func readCStringFromReader(r io.Reader) ([]byte, error) { var b []byte var n [1]byte @@ -578,7 +578,7 @@ func bsonToWriter(writer io.Writer, in interface{}) error { return nil } -// bufferWaiter is a channel-like structure which only recieves a buffer +// bufferWaiter is a channel-like structure which only receives a buffer // from its channel once on the first Get() call, then yields the same // buffer upon subsequent Get() calls. type bufferWaiter struct { diff --git a/src/mongo/gotools/mongorestore/mongorestore_archive_test.go b/src/mongo/gotools/mongorestore/mongorestore_archive_test.go index 84bea7d94e3..29f0f465c0c 100644 --- a/src/mongo/gotools/mongorestore/mongorestore_archive_test.go +++ b/src/mongo/gotools/mongorestore/mongorestore_archive_test.go @@ -28,7 +28,8 @@ func init() { } var ( - testArchive = "testdata/test.bar.archive" + testArchive = "testdata/test.bar.archive" + testArchiveWithOplog = "testdata/dump-w-oplog.archive" ) func TestMongorestoreShortArchive(t *testing.T) { @@ -86,3 +87,33 @@ func TestMongorestoreShortArchive(t *testing.T) { } }) } + +func TestMongorestoreArchiveWithOplog(t *testing.T) { + testutil.VerifyTestType(t, testutil.IntegrationTestType) + _, err := testutil.GetBareSession() + if err != nil { + t.Fatalf("No server available") + } + + Convey("With a test MongoRestore", t, func() { + inputOptions := &InputOptions{ + Archive: testArchiveWithOplog, + OplogReplay: true, + } + outputOptions := &OutputOptions{ + Drop: true, + } + nsOptions := &NSOptions{} + provider, toolOpts, err := testutil.GetBareSessionProvider() + + restore := MongoRestore{ + ToolOptions: toolOpts, + OutputOptions: outputOptions, + InputOptions: inputOptions, + NSOptions: nsOptions, + SessionProvider: provider, + } + err = restore.Restore() + So(err, ShouldBeNil) + }) +} diff --git a/src/mongo/gotools/mongorestore/ns/ns.go b/src/mongo/gotools/mongorestore/ns/ns.go index d7e637e3214..e540e42b1fc 100644 --- a/src/mongo/gotools/mongorestore/ns/ns.go +++ b/src/mongo/gotools/mongorestore/ns/ns.go @@ -16,7 +16,7 @@ import ( type Renamer struct { // List of regexps to match namespaces against matchers []*regexp.Regexp - // List of regexp-syle replacement strings to use with the matcher + // List of regexp-style replacement strings to use with the matcher replacers []string } diff --git a/src/mongo/gotools/mongorestore/oplog.go b/src/mongo/gotools/mongorestore/oplog.go index 00746cb8d15..db134247c35 100644 --- a/src/mongo/gotools/mongorestore/oplog.go +++ b/src/mongo/gotools/mongorestore/oplog.go @@ -104,6 +104,9 @@ func (restore *MongoRestore) RestoreOplog() error { } log.Logvf(log.Info, "applied %v ops", totalOps) + if err := bsonSource.Err(); err != nil { + return fmt.Errorf("error reading oplog bson input: %v", err) + } return nil } @@ -261,7 +264,7 @@ type nestedApplyOps struct { // unwrapNestedApplyOps converts a bson.D to a typed data structure. // Unfortunately, we're forced to convert by marshaling to bytes and -// unmarshaling. +// unmarshalling. func unwrapNestedApplyOps(doc bson.D) ([]db.Oplog, error) { // Doc to bytes bs, err := bson.Marshal(doc) @@ -281,7 +284,7 @@ func unwrapNestedApplyOps(doc bson.D) ([]db.Oplog, error) { // wrapNestedApplyOps converts a typed data structure to a bson.D. // Unfortunately, we're forced to convert by marshaling to bytes and -// unmarshaling. +// unmarshalling. func wrapNestedApplyOps(ops []db.Oplog) (bson.D, error) { cmd := &nestedApplyOps{ApplyOps: ops} diff --git a/src/mongo/gotools/mongorestore/restore.go b/src/mongo/gotools/mongorestore/restore.go index a0175807d37..7e19c07dd02 100644 --- a/src/mongo/gotools/mongorestore/restore.go +++ b/src/mongo/gotools/mongorestore/restore.go @@ -237,7 +237,7 @@ func (restore *MongoRestore) RestoreIntent(intent *intents.Intent) error { } // RestoreCollectionToDB pipes the given BSON data into the database. -// Returns the number of documents restored and any errors that occured. +// Returns the number of documents restored and any errors that occurred. func (restore *MongoRestore) RestoreCollectionToDB(dbName, colName string, bsonSource *db.DecodedBSONSource, file PosReader, fileSize int64) (int64, error) { diff --git a/src/mongo/gotools/mongorestore/testdata/dump-w-oplog.archive b/src/mongo/gotools/mongorestore/testdata/dump-w-oplog.archive Binary files differnew file mode 100644 index 00000000000..06da5b6eddc --- /dev/null +++ b/src/mongo/gotools/mongorestore/testdata/dump-w-oplog.archive diff --git a/src/mongo/gotools/mongostat/stat_consumer/formatter.go b/src/mongo/gotools/mongostat/stat_consumer/formatter.go index eda62b0dda6..352a285d7c1 100644 --- a/src/mongo/gotools/mongostat/stat_consumer/formatter.go +++ b/src/mongo/gotools/mongostat/stat_consumer/formatter.go @@ -19,7 +19,7 @@ type LineFormatter interface { // IsFinished returns true iff the formatter cannot print any more data IsFinished() bool - // Finish() is called whem mongostat is shutting down so that the fomatter can clean up + // Finish() is called when mongostat is shutting down so that the formatter can clean up Finish() } diff --git a/src/mongo/gotools/mongostat/stat_consumer/interactive_line_formatter.go b/src/mongo/gotools/mongostat/stat_consumer/interactive_line_formatter.go index a11483c0f11..d67e7ed5fa8 100644 --- a/src/mongo/gotools/mongostat/stat_consumer/interactive_line_formatter.go +++ b/src/mongo/gotools/mongostat/stat_consumer/interactive_line_formatter.go @@ -71,7 +71,7 @@ func (ilf *InteractiveLineFormatter) Finish() { // FormatLines formats the StatLines as a table in the terminal ui func (ilf *InteractiveLineFormatter) FormatLines(lines []*line.StatLine, headerKeys []string, keyNames map[string]string) string { - defer ilf.update() // so that it runs after the unlock, bnecause update locks again + defer ilf.update() // so that it runs after the unlock, because update locks again ilf.Lock() defer ilf.Unlock() // keep ordering consistent @@ -181,7 +181,7 @@ func (ilf *InteractiveLineFormatter) handleEvent(ev termbox.Event) { case ev.Ch == '?': ilf.showHelp = !ilf.showHelp default: - // ouput a bell on unknown inputs + // output a bell on unknown inputs fmt.Printf("\a") } } diff --git a/src/mongo/gotools/mongostat/status/server_status.go b/src/mongo/gotools/mongostat/status/server_status.go index 2aea470f2fd..76019710a7c 100644 --- a/src/mongo/gotools/mongostat/status/server_status.go +++ b/src/mongo/gotools/mongostat/status/server_status.go @@ -165,7 +165,7 @@ type NetworkStats struct { NumRequests int64 `bson:"numRequests"` } -// OpcountStats stores information related to comamnds and basic CRUD operations. +// OpcountStats stores information related to commands and basic CRUD operations. type OpcountStats struct { Insert int64 `bson:"insert"` Query int64 `bson:"query"` diff --git a/src/mongo/gotools/vendor.sh b/src/mongo/gotools/vendor.sh index f1b55db94b0..a58d5c527a8 100755 --- a/src/mongo/gotools/vendor.sh +++ b/src/mongo/gotools/vendor.sh @@ -33,7 +33,7 @@ set_dependencies() { git clone $giturl "$install_path" - ( cd $install_path && git checkout "$version" ) + ( cd $install_path && git checkout "$version" && rm -rf '.git' ) done < $1 echo ">> All Done" diff --git a/src/mongo/gotools/vendor/src/github.com/10gen/llmgo/auth.go b/src/mongo/gotools/vendor/src/github.com/10gen/llmgo/auth.go index 70ac345677d..9be48322e98 100644 --- a/src/mongo/gotools/vendor/src/github.com/10gen/llmgo/auth.go +++ b/src/mongo/gotools/vendor/src/github.com/10gen/llmgo/auth.go @@ -12,6 +12,7 @@ package mgo import ( "crypto/md5" "crypto/sha1" + "crypto/sha256" "encoding/hex" "errors" "fmt" @@ -19,6 +20,7 @@ import ( "github.com/10gen/llmgo/bson" "github.com/10gen/llmgo/internal/scram" + "github.com/xdg/stringprep" ) type authCmd struct { @@ -70,6 +72,17 @@ type saslResult struct { ErrMsg string } +type saslMechNegotation struct { + IsMaster int `bson:"ismaster"` + SaslSupportedMechs string `bson:"saslSupportedMechs"` +} + +type saslMechResult struct { + Ok bool `bson:"ok"` + SaslSupportedMechs []string `bson:"saslSupportedMechs,omitempty"` + ErrMsg string +} + type saslStepper interface { Step(serverData []byte) (clientData []byte, done bool, err error) Close() @@ -143,12 +156,31 @@ func (socket *MongoSocket) resetNonce() { func (socket *MongoSocket) Login(cred Credential) error { socket.Lock() - if cred.Mechanism == "" && socket.serverInfo.MaxWireVersion >= 3 { - cred.Mechanism = "SCRAM-SHA-1" + maxWire := socket.serverInfo.MaxWireVersion + socket.Unlock() + + // Must update credential mechanism before later caching + if cred.Mechanism == "" { + switch { + case maxWire >= 7: + debugf("Needs mechanism negotiation") + mech, err := socket.negotiateDefaultMech(cred) + if err != nil { + return err + } + cred.Mechanism = mech + debugf("Got mechanism '%s'", mech) + case maxWire >= 3: + cred.Mechanism = "SCRAM-SHA-1" + default: + cred.Mechanism = "MONGODB-CR" + } } + + socket.Lock() for _, sockCred := range socket.creds { if sockCred == cred { - debugf("Socket %p to %s: login: db=%q user=%q (already logged in)", socket, socket.addr, cred.Source, cred.Username) + debugf("Socket %p to %s: login: db=%q user=%q mech=%s", socket, socket.addr, cred.Source, cred.Username, cred.Mechanism) socket.Unlock() return nil } @@ -165,7 +197,7 @@ func (socket *MongoSocket) Login(cred Credential) error { var err error switch cred.Mechanism { - case "", "MONGODB-CR", "MONGO-CR": // Name changed to MONGODB-CR in SERVER-8501. + case "MONGODB-CR", "MONGO-CR": // Name changed to MONGODB-CR in SERVER-8501. err = socket.loginClassic(cred) case "PLAIN": err = socket.loginPlain(cred) @@ -184,12 +216,33 @@ func (socket *MongoSocket) Login(cred Credential) error { return err } +func (socket *MongoSocket) negotiateDefaultMech(cred Credential) (string, error) { + user := cred.Source + "." + cred.Username + req := &saslMechNegotation{IsMaster: 1, SaslSupportedMechs: user} + res := saslMechResult{} + err := socket.loginRun(cred.Source, &req, &res, func() error { + if !res.Ok { + return errors.New(res.ErrMsg) + } + return nil + }) + if err != nil { + return "", err + } + for _, mech := range res.SaslSupportedMechs { + if mech == "SCRAM-SHA-256" { + return "SCRAM-SHA-256", nil + } + } + return "SCRAM-SHA-1", nil +} + func (socket *MongoSocket) loginClassic(cred Credential) error { // Note that this only works properly because this function is // synchronous, which means the nonce won't get reset while we're // using it and any other login requests will block waiting for a // new nonce. - socket.resetNonce(); + socket.resetNonce() nonce, err := socket.getNonce() if err != nil { return err @@ -257,9 +310,11 @@ func (socket *MongoSocket) loginPlain(cred Credential) error { func (socket *MongoSocket) loginSASL(cred Credential) error { var sasl saslStepper var err error + // SCRAM is handled without external libraries. if cred.Mechanism == "SCRAM-SHA-1" { - // SCRAM is handled without external libraries. - sasl = saslNewScram(cred) + sasl = saslNewScram1(cred) + } else if cred.Mechanism == "SCRAM-SHA-256" { + sasl, err = saslNewScram256(cred) } else if len(cred.ServiceHost) > 0 { sasl, err = saslNew(cred, cred.ServiceHost) } else { @@ -336,13 +391,23 @@ func (socket *MongoSocket) loginSASL(cred Credential) error { return nil } -func saslNewScram(cred Credential) *saslScram { +func saslNewScram1(cred Credential) *saslScram { credsum := md5.New() credsum.Write([]byte(cred.Username + ":mongo:" + cred.Password)) client := scram.NewClient(sha1.New, cred.Username, hex.EncodeToString(credsum.Sum(nil))) return &saslScram{cred: cred, client: client} } +func saslNewScram256(cred Credential) (*saslScram, error) { + preppedPass, err := stringprep.SASLprep.Prepare(cred.Password) + if err != nil { + return nil, err + } + + client := scram.NewClient(sha256.New, cred.Username, preppedPass) + return &saslScram{cred: cred, client: client}, nil +} + type saslScram struct { cred Credential client *scram.Client |