summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Schubert <thomas.schubert@mongodb.com>2017-06-06 11:38:42 -0400
committerThomas Schubert <thomas.schubert@mongodb.com>2017-06-06 11:38:42 -0400
commit9676bdc2b561f23805769478643e7c026b3ea9bc (patch)
treec043c1f29ed1e54efc96956352cfca729570943d
parent17334c154308b7b2fef687c5cb627978241707f8 (diff)
downloadmongo-9676bdc2b561f23805769478643e7c026b3ea9bc.tar.gz
Import tools: 1fa8108e8fdd955f7e12550bf2e5a56471c3c73e from branch v3.4
ref: 17fbdf31ab..1fa8108e8f for: 3.4.5 TOOLS-1605 Conditionally create a Windows job object in smoke.py TOOLS-1623 merge mgo's fix for "Apparent performance degradation in mongodump" TOOLS-1635 Mongostat panic error TOOLS-1645 short archives cause mongorestore to hang instead of failing TOOLS-1679 stop running mongod's in tests with --nohttpinterface TOOLS-1682 don't run native cert tests against mongod 3.5 on the 3.4 branch
-rw-r--r--src/mongo/gotools/Godeps2
-rw-r--r--src/mongo/gotools/common.yml5
-rw-r--r--src/mongo/gotools/common/archive/demultiplexer.go48
-rw-r--r--src/mongo/gotools/common/archive/multiplexer_roundtrip_test.go10
-rw-r--r--src/mongo/gotools/common/archive/parser.go10
-rw-r--r--src/mongo/gotools/common/archive/parser_test.go33
-rw-r--r--src/mongo/gotools/import.data3
-rw-r--r--src/mongo/gotools/mongorestore/mongorestore.go34
-rw-r--r--src/mongo/gotools/mongorestore/mongorestore_archive_test.go95
-rw-r--r--src/mongo/gotools/mongorestore/testdata/test.bar.archivebin0 -> 896 bytes
-rw-r--r--src/mongo/gotools/mongostat/main/mongostat.go1
-rw-r--r--src/mongo/gotools/mongostat/stat_consumer/formatter.go2
-rw-r--r--src/mongo/gotools/mongostat/stat_consumer/grid_line_formatter.go3
-rw-r--r--src/mongo/gotools/mongostat/stat_consumer/interactive_line_formatter.go17
-rw-r--r--src/mongo/gotools/mongostat/stat_consumer/json_line_formatter.go2
-rw-r--r--src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/bson.go7
-rw-r--r--src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/bson_test.go62
-rw-r--r--src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/decode.go176
-rw-r--r--src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/encode.go13
-rwxr-xr-xsrc/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/specdata/update.sh26
-rw-r--r--src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/specdata_test.go241
-rw-r--r--src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/internal/json/decode.go8
-rw-r--r--src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/internal/json/extension_test.go3
-rw-r--r--src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/internal/json/stream_test.go2
-rw-r--r--src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/session.go144
-rw-r--r--src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/session_test.go139
-rw-r--r--src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/socket.go1
27 files changed, 631 insertions, 456 deletions
diff --git a/src/mongo/gotools/Godeps b/src/mongo/gotools/Godeps
index c5d6318b56c..509c3865580 100644
--- a/src/mongo/gotools/Godeps
+++ b/src/mongo/gotools/Godeps
@@ -1,4 +1,4 @@
-gopkg.in/mgo.v2 1e52f6152a9b262873f831bb5a94bcd29ef38c38 github.com/10gen/mgo
+gopkg.in/mgo.v2 39b4000d99037e917f3a3b9d2dcab667a9ef284a github.com/10gen/mgo
gopkg.in/tomb.v2 14b3d72120e8d10ea6e6b7f87f7175734b1faab8
github.com/jtolds/gls 8ddce2a84170772b95dd5d576c48d517b22cac63
github.com/jacobsa/oglematchers 3ecefc49db07722beca986d9bb71ddd026b133f0
diff --git a/src/mongo/gotools/common.yml b/src/mongo/gotools/common.yml
index 7453ec02145..79b68563d0d 100644
--- a/src/mongo/gotools/common.yml
+++ b/src/mongo/gotools/common.yml
@@ -6,7 +6,6 @@ command_type: system
# run the same task in the previous revision if the current task fails
stepback: true
-disable_cleanup: true
mongo_tools_variables:
@@ -1235,7 +1234,7 @@ tasks:
- func: "setup credentials"
- func: "download mongod"
vars:
- mongo_version: "latest"
+ mongo_version: "3.4"
- func: "fetch tool"
vars:
tool: mongoimport
@@ -1738,7 +1737,6 @@ buildvariants:
excludes: requires_mmap_available,requires_mongo_24,requires_mongo_26,requires_mongo_30
resmoke_args: -j 2
multiversion_override: "skip"
- mongo_version_always_use_latest: true
arch: "linux/s390x"
edition: enterprise
run_kinit: true
@@ -1762,7 +1760,6 @@ buildvariants:
excludes: requires_mmap_available,requires_large_ram,requires_mongo_24,requires_mongo_26,requires_mongo_30
resmoke_args: -j 2
multiversion_override: "skip"
- mongo_version_always_use_latest: true
arch: "linux/arm64"
edition: ssl
integration_test_args: integration
diff --git a/src/mongo/gotools/common/archive/demultiplexer.go b/src/mongo/gotools/common/archive/demultiplexer.go
index 5b0503b0208..b56cc2132e2 100644
--- a/src/mongo/gotools/common/archive/demultiplexer.go
+++ b/src/mongo/gotools/common/archive/demultiplexer.go
@@ -23,6 +23,12 @@ type DemuxOut interface {
Sum64() (uint64, bool)
}
+const (
+ NamespaceUnopened = iota
+ NamespaceOpened
+ NamespaceClosed
+)
+
// Demultiplexer implements Parser.
type Demultiplexer struct {
In io.Reader
@@ -33,6 +39,20 @@ type Demultiplexer struct {
buf [db.MaxBSONSize]byte
NamespaceChan chan string
NamespaceErrorChan chan error
+ NamespaceStatus map[string]int
+}
+
+func CreateDemux(namespaceMetadatas []*CollectionMetadata, in io.Reader) *Demultiplexer {
+ demux := &Demultiplexer{
+ NamespaceStatus: make(map[string]int),
+ In: in,
+ }
+ for _, cm := range namespaceMetadatas {
+ ns := cm.Database + "." + cm.Collection
+ demux.NamespaceStatus[ns] = NamespaceUnopened
+
+ }
+ return demux
}
// Run creates and runs a parser with the Demultiplexer as a consumer
@@ -42,6 +62,7 @@ func (demux *Demultiplexer) Run() error {
if len(demux.outs) > 0 {
log.Logvf(log.Always, "demux finishing when there are still outs (%v)", len(demux.outs))
}
+
log.Logvf(log.DebugLow, "demux finishing (err:%v)", err)
return err
}
@@ -89,6 +110,10 @@ func (demux *Demultiplexer) HeaderBSON(buf []byte) error {
}
demux.currentNamespace = colHeader.Database + "." + colHeader.Collection
if _, ok := demux.outs[demux.currentNamespace]; !ok {
+ if demux.NamespaceStatus[demux.currentNamespace] != NamespaceUnopened {
+ return newError("namespace header for already opened namespace")
+ }
+ demux.NamespaceStatus[demux.currentNamespace] = NamespaceOpened
if demux.NamespaceChan != nil {
demux.NamespaceChan <- demux.currentNamespace
err := <-demux.NamespaceErrorChan
@@ -105,7 +130,11 @@ func (demux *Demultiplexer) HeaderBSON(buf []byte) error {
}
}
if colHeader.EOF {
+ if rcr, ok := demux.outs[demux.currentNamespace].(*RegularCollectionReceiver); ok {
+ rcr.err = io.EOF
+ }
demux.outs[demux.currentNamespace].Close()
+ demux.NamespaceStatus[demux.currentNamespace] = NamespaceClosed
length := int64(demux.lengths[demux.currentNamespace])
crcUInt64, ok := demux.outs[demux.currentNamespace].Sum64()
if ok {
@@ -137,18 +166,30 @@ func (demux *Demultiplexer) HeaderBSON(buf []byte) error {
// End is part of the ParserConsumer interface and receives the end of archive notification.
func (demux *Demultiplexer) End() error {
log.Logvf(log.DebugHigh, "demux End")
+ var err error
if len(demux.outs) != 0 {
openNss := []string{}
for ns := range demux.outs {
openNss = append(openNss, ns)
+ if rcr, ok := demux.outs[ns].(*RegularCollectionReceiver); ok {
+ rcr.err = newError("archive io error")
+ }
+ demux.outs[ns].Close()
+ }
+ err = newError(fmt.Sprintf("archive finished but contained files were unfinished (%v)", openNss))
+ } else {
+ for ns, status := range demux.NamespaceStatus {
+ if status != NamespaceClosed {
+ err = newError(fmt.Sprintf("archive finished before all collections were seen (%v)", ns))
+ }
}
- return newError(fmt.Sprintf("archive finished but contained files were unfinished (%v)", openNss))
}
if demux.NamespaceChan != nil {
close(demux.NamespaceChan)
}
- return nil
+
+ return err
}
// BodyBSON is part of the ParserConsumer interface and receives BSON bodies from the parser.
@@ -196,6 +237,7 @@ type RegularCollectionReceiver struct {
hash hash.Hash64
closeOnce sync.Once
openOnce sync.Once
+ err error
}
func (receiver *RegularCollectionReceiver) Sum64() (uint64, bool) {
@@ -219,7 +261,7 @@ func (receiver *RegularCollectionReceiver) Read(r []byte) (int, error) {
wLen, ok := <-receiver.readLenChan
if !ok {
close(receiver.readBufChan)
- return 0, io.EOF
+ return 0, receiver.err
}
if wLen > db.MaxBSONSize {
return 0, fmt.Errorf("incomming buffer size is too big %v", wLen)
diff --git a/src/mongo/gotools/common/archive/multiplexer_roundtrip_test.go b/src/mongo/gotools/common/archive/multiplexer_roundtrip_test.go
index 025121b704a..18fd73729db 100644
--- a/src/mongo/gotools/common/archive/multiplexer_roundtrip_test.go
+++ b/src/mongo/gotools/common/archive/multiplexer_roundtrip_test.go
@@ -86,7 +86,10 @@ func TestBasicMux(t *testing.T) {
err = <-mux.Completed
So(err, ShouldBeNil)
- demux := &Demultiplexer{In: buf}
+ demux := &Demultiplexer{
+ In: buf,
+ NamespaceStatus: make(map[string]int),
+ }
demuxOuts := map[string]*RegularCollectionReceiver{}
errChan := make(chan error)
@@ -121,7 +124,10 @@ func TestParallelMux(t *testing.T) {
mux := NewMultiplexer(writePipe, new(testNotifier))
muxIns := map[string]*MuxIn{}
- demux := &Demultiplexer{In: readPipe}
+ demux := &Demultiplexer{
+ In: readPipe,
+ NamespaceStatus: make(map[string]int),
+ }
demuxOuts := map[string]*RegularCollectionReceiver{}
inChecksum := map[string]hash.Hash{}
diff --git a/src/mongo/gotools/common/archive/parser.go b/src/mongo/gotools/common/archive/parser.go
index 2dd75807a8b..8be0529a6dc 100644
--- a/src/mongo/gotools/common/archive/parser.go
+++ b/src/mongo/gotools/common/archive/parser.go
@@ -104,8 +104,9 @@ func (parse *Parser) ReadAllBlocks(consumer ParserConsumer) (err error) {
for err == nil {
err = parse.ReadBlock(consumer)
}
+ endError := consumer.End()
if err == io.EOF {
- return nil
+ return endError
}
return err
}
@@ -119,13 +120,6 @@ func (parse *Parser) ReadAllBlocks(consumer ParserConsumer) (err error) {
// parsing failure.
func (parse *Parser) ReadBlock(consumer ParserConsumer) (err error) {
isTerminator, err := parse.readBSONOrTerminator()
- if err == io.EOF {
- handlerErr := consumer.End()
- if handlerErr != nil {
- return newParserWrappedError("ParserConsumer.End", handlerErr)
- }
- return err
- }
if err != nil {
return err
}
diff --git a/src/mongo/gotools/common/archive/parser_test.go b/src/mongo/gotools/common/archive/parser_test.go
index dba7e1d0263..bcbe2880f10 100644
--- a/src/mongo/gotools/common/archive/parser_test.go
+++ b/src/mongo/gotools/common/archive/parser_test.go
@@ -50,7 +50,7 @@ func TestParsing(t *testing.T) {
Convey("With a parser with a simple parser consumer", t, func() {
tc := &testConsumer{}
parser := Parser{}
- Convey("a well formed header and body data parse correctly", func() {
+ Convey("a well formed header and body", func() {
buf := bytes.Buffer{}
b, _ := bson.Marshal(strStruct{"header"})
buf.Write(b)
@@ -58,15 +58,24 @@ func TestParsing(t *testing.T) {
buf.Write(b)
buf.Write(term)
parser.In = &buf
- err := parser.ReadBlock(tc)
- So(err, ShouldBeNil)
- So(tc.eof, ShouldBeFalse)
- So(tc.headers[0], ShouldEqual, "header")
- So(tc.bodies[0], ShouldEqual, "body")
+ Convey("ReadBlock data parses correctly", func() {
+ err := parser.ReadBlock(tc)
+ So(err, ShouldBeNil)
+ So(tc.eof, ShouldBeFalse)
+ So(tc.headers[0], ShouldEqual, "header")
+ So(tc.bodies[0], ShouldEqual, "body")
- err = parser.ReadBlock(tc)
- So(err, ShouldEqual, io.EOF)
- So(tc.eof, ShouldBeTrue)
+ err = parser.ReadBlock(tc)
+ So(err, ShouldEqual, io.EOF)
+ })
+ Convey("ReadAllBlock data parses correctly", func() {
+ err := parser.ReadAllBlocks(tc)
+ So(err, ShouldEqual, nil)
+ So(tc.eof, ShouldBeTrue)
+ So(tc.headers[0], ShouldEqual, "header")
+ So(tc.bodies[0], ShouldEqual, "body")
+
+ })
})
Convey("a well formed header and multiple body datas parse correctly", func() {
buf := bytes.Buffer{}
@@ -90,7 +99,7 @@ func TestParsing(t *testing.T) {
err = parser.ReadBlock(tc)
So(err, ShouldEqual, io.EOF)
- So(tc.eof, ShouldBeTrue)
+ So(tc.eof, ShouldBeFalse)
})
Convey("an incorrect terminator should cause an error", func() {
buf := bytes.Buffer{}
@@ -108,13 +117,13 @@ func TestParsing(t *testing.T) {
parser.In = &buf
err := parser.ReadBlock(tc)
So(err, ShouldEqual, io.EOF)
- So(tc.eof, ShouldBeTrue)
+ So(tc.eof, ShouldBeFalse)
})
Convey("an error comming from the consumer should propigate through the parser", func() {
tc.eof = true
buf := bytes.Buffer{}
parser.In = &buf
- err := parser.ReadBlock(tc)
+ err := parser.ReadAllBlocks(tc)
So(err.Error(), ShouldContainSubstring, "double end")
})
Convey("a partial block should result in a non-EOF error", func() {
diff --git a/src/mongo/gotools/import.data b/src/mongo/gotools/import.data
index d261a42e87c..cd1daead6bc 100644
--- a/src/mongo/gotools/import.data
+++ b/src/mongo/gotools/import.data
@@ -1,5 +1,6 @@
{
- "commit": "17fbdf31abca50cdfe27482b05b1476f42ecab0a",
+ "commit": "1fa8108e8fdd955f7e12550bf2e5a56471c3c73e",
"github": "mongodb/mongo-tools.git",
+ "vendor": "tools",
"branch": "v3.4"
}
diff --git a/src/mongo/gotools/mongorestore/mongorestore.go b/src/mongo/gotools/mongorestore/mongorestore.go
index 3e96bba0b0e..c79b04e5acf 100644
--- a/src/mongo/gotools/mongorestore/mongorestore.go
+++ b/src/mongo/gotools/mongorestore/mongorestore.go
@@ -248,13 +248,15 @@ func (restore *MongoRestore) Restore() error {
}
if restore.InputOptions.Archive != "" {
- archiveReader, err := restore.getArchiveReader()
- if err != nil {
- return err
- }
- restore.archive = &archive.Reader{
- In: archiveReader,
- Prelude: &archive.Prelude{},
+ if restore.archive == nil {
+ archiveReader, err := restore.getArchiveReader()
+ if err != nil {
+ return err
+ }
+ restore.archive = &archive.Reader{
+ In: archiveReader,
+ Prelude: &archive.Prelude{},
+ }
}
err = restore.archive.Prelude.Read(restore.archive.In)
if err != nil {
@@ -316,9 +318,7 @@ func (restore *MongoRestore) Restore() error {
// Create the demux before intent creation, because muted archive intents need
// to register themselves with the demux directly
if restore.InputOptions.Archive != "" {
- restore.archive.Demux = &archive.Demultiplexer{
- In: restore.archive.In,
- }
+ restore.archive.Demux = archive.CreateDemux(restore.archive.Prelude.NamespaceMetadatas, restore.archive.In)
}
switch {
@@ -386,13 +386,18 @@ func (restore *MongoRestore) Restore() error {
return nil
}
+ demuxFinished := make(chan interface{})
+ var demuxErr error
if restore.InputOptions.Archive != "" {
namespaceChan := make(chan string, 1)
namespaceErrorChan := make(chan error)
restore.archive.Demux.NamespaceChan = namespaceChan
restore.archive.Demux.NamespaceErrorChan = namespaceErrorChan
- go restore.archive.Demux.Run()
+ go func() {
+ demuxErr = restore.archive.Demux.Run()
+ close(demuxFinished)
+ }()
// consume the new namespace announcement from the demux for all of the special collections
// that get cached when being read out of the archive.
// The first regular collection found gets pushed back on to the namespaceChan
@@ -481,7 +486,12 @@ func (restore *MongoRestore) Restore() error {
}
}
- log.Logv(log.Always, "done")
+ defer log.Logv(log.Always, "done")
+
+ if restore.InputOptions.Archive != "" {
+ <-demuxFinished
+ return demuxErr
+ }
return nil
}
diff --git a/src/mongo/gotools/mongorestore/mongorestore_archive_test.go b/src/mongo/gotools/mongorestore/mongorestore_archive_test.go
new file mode 100644
index 00000000000..cbcefe40696
--- /dev/null
+++ b/src/mongo/gotools/mongorestore/mongorestore_archive_test.go
@@ -0,0 +1,95 @@
+package mongorestore
+
+import (
+ "github.com/mongodb/mongo-tools/common/archive"
+ "github.com/mongodb/mongo-tools/common/db"
+ "github.com/mongodb/mongo-tools/common/log"
+ "github.com/mongodb/mongo-tools/common/options"
+ "github.com/mongodb/mongo-tools/common/testutil"
+ "github.com/mongodb/mongo-tools/common/util"
+
+ . "github.com/smartystreets/goconvey/convey"
+
+ "io"
+ "io/ioutil"
+ "os"
+ "testing"
+)
+
+func init() {
+ // bump up the verbosity to make checking debug log output possible
+ log.SetVerbosity(&options.Verbosity{
+ VLevel: 4,
+ })
+}
+
+var (
+ testArchive = "testdata/test.bar.archive"
+)
+
+func TestMongorestoreShortArchive(t *testing.T) {
+ Convey("With a test MongoRestore", t, func() {
+ ssl := testutil.GetSSLOptions()
+ auth := testutil.GetAuthOptions()
+
+ testutil.VerifyTestType(t, testutil.IntegrationTestType)
+ toolOptions := &options.ToolOptions{
+ Connection: &options.Connection{
+ Host: testServer,
+ Port: testPort,
+ },
+ Auth: &auth,
+ SSL: &ssl,
+ }
+ inputOptions := &InputOptions{
+ Archive: testArchive,
+ }
+ outputOptions := &OutputOptions{
+ NumParallelCollections: 1,
+ NumInsertionWorkers: 1,
+ WriteConcern: "majority",
+ Drop: true,
+ }
+ nsOptions := &NSOptions{}
+ provider, err := db.NewSessionProvider(*toolOptions)
+ if err != nil {
+ log.Logvf(log.Always, "error connecting to host: %v", err)
+ os.Exit(util.ExitError)
+ }
+ file, err := os.Open(testArchive)
+ So(file, ShouldNotBeNil)
+ So(err, ShouldBeNil)
+
+ fi, err := file.Stat()
+ So(fi, ShouldNotBeNil)
+ So(err, ShouldBeNil)
+
+ fileSize := fi.Size()
+
+ for i := fileSize; i >= 0; i-- {
+
+ log.Logvf(log.Always, "Restoring from the first %v bytes of a archive of size %v", i, fileSize)
+
+ _, err = file.Seek(0, 0)
+ So(err, ShouldBeNil)
+
+ restore := MongoRestore{
+ ToolOptions: toolOptions,
+ OutputOptions: outputOptions,
+ InputOptions: inputOptions,
+ NSOptions: nsOptions,
+ SessionProvider: provider,
+ archive: &archive.Reader{
+ Prelude: &archive.Prelude{},
+ In: ioutil.NopCloser(io.LimitReader(file, i)),
+ },
+ }
+ err = restore.Restore()
+ if i == fileSize {
+ So(err, ShouldBeNil)
+ } else {
+ So(err, ShouldNotBeNil)
+ }
+ }
+ })
+}
diff --git a/src/mongo/gotools/mongorestore/testdata/test.bar.archive b/src/mongo/gotools/mongorestore/testdata/test.bar.archive
new file mode 100644
index 00000000000..50f8f1c877f
--- /dev/null
+++ b/src/mongo/gotools/mongorestore/testdata/test.bar.archive
Binary files differ
diff --git a/src/mongo/gotools/mongostat/main/mongostat.go b/src/mongo/gotools/mongostat/main/mongostat.go
index 8663248ceb6..97c79f0ca4f 100644
--- a/src/mongo/gotools/mongostat/main/mongostat.go
+++ b/src/mongo/gotools/mongostat/main/mongostat.go
@@ -229,6 +229,7 @@ func main() {
// kick it off
err = stat.Run()
+ formatter.Finish()
if err != nil {
log.Logvf(log.Always, "Failed: %v", err)
os.Exit(util.ExitError)
diff --git a/src/mongo/gotools/mongostat/stat_consumer/formatter.go b/src/mongo/gotools/mongostat/stat_consumer/formatter.go
index 2fb1a4c5506..1acaa842e09 100644
--- a/src/mongo/gotools/mongostat/stat_consumer/formatter.go
+++ b/src/mongo/gotools/mongostat/stat_consumer/formatter.go
@@ -13,6 +13,8 @@ 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()
}
type limitableFormatter struct {
diff --git a/src/mongo/gotools/mongostat/stat_consumer/grid_line_formatter.go b/src/mongo/gotools/mongostat/stat_consumer/grid_line_formatter.go
index 53a6e2d4a37..7a03a21118c 100644
--- a/src/mongo/gotools/mongostat/stat_consumer/grid_line_formatter.go
+++ b/src/mongo/gotools/mongostat/stat_consumer/grid_line_formatter.go
@@ -40,6 +40,9 @@ func init() {
// headerInterval is the number of chunks before the header is re-printed in GridLineFormatter
const headerInterval = 10
+func (glf *GridLineFormatter) Finish() {
+}
+
// FormatLines formats the StatLines as a grid
func (glf *GridLineFormatter) FormatLines(lines []*line.StatLine, headerKeys []string, keyNames map[string]string) string {
buf := &bytes.Buffer{}
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 87cc08fef26..466e7c93d83 100644
--- a/src/mongo/gotools/mongostat/stat_consumer/interactive_line_formatter.go
+++ b/src/mongo/gotools/mongostat/stat_consumer/interactive_line_formatter.go
@@ -6,6 +6,7 @@ import (
"fmt"
"sort"
"strings"
+ "sync"
"github.com/mongodb/mongo-tools/mongostat/stat_consumer/line"
"github.com/nsf/termbox-go"
@@ -19,6 +20,7 @@ type InteractiveLineFormatter struct {
table []*column
row, col int
showHelp bool
+ sync.Mutex
}
func NewInteractiveLineFormatter(_ int64, includeHeader bool) LineFormatter {
@@ -30,6 +32,7 @@ func NewInteractiveLineFormatter(_ int64, includeHeader bool) LineFormatter {
fmt.Printf("Error setting up terminal UI: %v", err)
panic("could not set up interactive terminal interface")
}
+
go func() {
for {
ilf.handleEvent(termbox.PollEvent())
@@ -56,8 +59,15 @@ type cell struct {
header bool
}
+func (ilf *InteractiveLineFormatter) Finish() {
+ termbox.Close()
+}
+
// 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
+ ilf.Lock()
+ defer ilf.Unlock()
// keep ordering consistent
sort.Sort(line.StatLines(lines))
@@ -99,14 +109,16 @@ func (ilf *InteractiveLineFormatter) FormatLines(lines []*line.StatLine, headerK
}
}
- ilf.update()
return ""
}
func (ilf *InteractiveLineFormatter) handleEvent(ev termbox.Event) {
+ ilf.Lock()
+ defer ilf.Unlock()
if ev.Type != termbox.EventKey {
return
}
+
currSelected := ilf.table[ilf.col].cells[ilf.row].selected
switch {
case ev.Key == termbox.KeyCtrlC:
@@ -114,7 +126,6 @@ func (ilf *InteractiveLineFormatter) handleEvent(ev termbox.Event) {
case ev.Key == termbox.KeyEsc:
fallthrough
case ev.Ch == 'q':
- termbox.Close()
// our max rowCount is set to 1; increment to exit
ilf.increment()
case ev.Key == termbox.KeyArrowRight:
@@ -190,6 +201,8 @@ func writeString(x, y int, text string, fg, bg termbox.Attribute) {
}
func (ilf *InteractiveLineFormatter) update() {
+ ilf.Lock()
+ defer ilf.Unlock()
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
x := 0
for i, column := range ilf.table {
diff --git a/src/mongo/gotools/mongostat/stat_consumer/json_line_formatter.go b/src/mongo/gotools/mongostat/stat_consumer/json_line_formatter.go
index 0452072ad39..0e214f73e02 100644
--- a/src/mongo/gotools/mongostat/stat_consumer/json_line_formatter.go
+++ b/src/mongo/gotools/mongostat/stat_consumer/json_line_formatter.go
@@ -21,6 +21,8 @@ func NewJSONLineFormatter(maxRows int64, _ bool) LineFormatter {
func init() {
FormatterConstructors["json"] = NewJSONLineFormatter
}
+func (glf *JSONLineFormatter) Finish() {
+}
// FormatLines formats the StatLines as JSON
func (jlf *JSONLineFormatter) FormatLines(lines []*line.StatLine, headerKeys []string, keyNames map[string]string) string {
diff --git a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/bson.go b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/bson.go
index 7fb7f8cae48..c023b0638bb 100644
--- a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/bson.go
+++ b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/bson.go
@@ -51,6 +51,8 @@ import (
"time"
)
+//go:generate go run bson_corpus_spec_test_generator.go
+
// --------------------------------------------------------------------------
// The public API.
@@ -279,7 +281,7 @@ var nullBytes = []byte("null")
func (id *ObjectId) UnmarshalJSON(data []byte) error {
if len(data) > 0 && (data[0] == '{' || data[0] == 'O') {
var v struct {
- Id json.RawMessage `json:"$oid"`
+ Id json.RawMessage `json:"$oid"`
Func struct {
Id json.RawMessage
} `json:"$oidFunc"`
@@ -561,6 +563,9 @@ func Unmarshal(in []byte, out interface{}) (err error) {
case reflect.Map:
d := newDecoder(in)
d.readDocTo(v)
+ if d.i < len(d.in) {
+ return errors.New("Document is corrupted")
+ }
case reflect.Struct:
return errors.New("Unmarshal can't deal with struct values. Use a pointer.")
default:
diff --git a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/bson_test.go b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/bson_test.go
index 37451f9fdc2..11fdfae4185 100644
--- a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/bson_test.go
+++ b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/bson_test.go
@@ -29,19 +29,16 @@ package bson_test
import (
"encoding/binary"
- "encoding/hex"
"encoding/json"
"encoding/xml"
"errors"
"net/url"
"reflect"
- "strings"
"testing"
"time"
. "gopkg.in/check.v1"
"gopkg.in/mgo.v2/bson"
- "gopkg.in/yaml.v2"
)
func TestAll(t *testing.T) {
@@ -1581,65 +1578,6 @@ func (s *S) TestObjectIdJSONMarshaling(c *C) {
}
// --------------------------------------------------------------------------
-// Spec tests
-
-type specTest struct {
- Description string
- Documents []struct {
- Decoded map[string]interface{}
- Encoded string
- DecodeOnly bool `yaml:"decodeOnly"`
- Error interface{}
- }
-}
-
-func (s *S) TestSpecTests(c *C) {
- for _, data := range specTests {
- var test specTest
- err := yaml.Unmarshal([]byte(data), &test)
- c.Assert(err, IsNil)
-
- c.Logf("Running spec test set %q", test.Description)
-
- for _, doc := range test.Documents {
- if doc.Error != nil {
- continue
- }
- c.Logf("Ensuring %q decodes as %v", doc.Encoded, doc.Decoded)
- var decoded map[string]interface{}
- encoded, err := hex.DecodeString(doc.Encoded)
- c.Assert(err, IsNil)
- err = bson.Unmarshal(encoded, &decoded)
- c.Assert(err, IsNil)
- c.Assert(decoded, DeepEquals, doc.Decoded)
- }
-
- for _, doc := range test.Documents {
- if doc.DecodeOnly || doc.Error != nil {
- continue
- }
- c.Logf("Ensuring %v encodes as %q", doc.Decoded, doc.Encoded)
- encoded, err := bson.Marshal(doc.Decoded)
- c.Assert(err, IsNil)
- c.Assert(strings.ToUpper(hex.EncodeToString(encoded)), Equals, doc.Encoded)
- }
-
- for _, doc := range test.Documents {
- if doc.Error == nil {
- continue
- }
- c.Logf("Ensuring %q errors when decoded: %s", doc.Encoded, doc.Error)
- var decoded map[string]interface{}
- encoded, err := hex.DecodeString(doc.Encoded)
- c.Assert(err, IsNil)
- err = bson.Unmarshal(encoded, &decoded)
- c.Assert(err, NotNil)
- c.Logf("Failed with: %v", err)
- }
- }
-}
-
-// --------------------------------------------------------------------------
// ObjectId Text encoding.TextUnmarshaler.
var textIdTests = []struct {
diff --git a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/decode.go b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/decode.go
index 7c2d8416afe..fd29da45071 100644
--- a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/decode.go
+++ b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/decode.go
@@ -225,61 +225,65 @@ func (d *decoder) readDocTo(out reflect.Value) {
panic("Unsupported document type for unmarshalling: " + out.Type().String())
}
- end := int(d.readInt32())
- end += d.i - 4
- if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' {
- corrupted()
- }
- for d.in[d.i] != '\x00' {
- kind := d.readByte()
- name := d.readCStr()
- if d.i >= end {
+ if outt == typeRaw {
+ d.skipDoc()
+ } else {
+ end := int(d.readInt32())
+ end += d.i - 4
+ if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' {
corrupted()
}
+ for d.in[d.i] != '\x00' {
+ kind := d.readByte()
+ name := d.readCStr()
+ if d.i >= end {
+ corrupted()
+ }
- switch outk {
- case reflect.Map:
- e := reflect.New(elemType).Elem()
- if d.readElemTo(e, kind) {
- k := reflect.ValueOf(name)
- if convertKey {
- k = k.Convert(keyType)
+ switch outk {
+ case reflect.Map:
+ e := reflect.New(elemType).Elem()
+ if d.readElemTo(e, kind) {
+ k := reflect.ValueOf(name)
+ if convertKey {
+ k = k.Convert(keyType)
+ }
+ out.SetMapIndex(k, e)
}
- out.SetMapIndex(k, e)
- }
- case reflect.Struct:
- if outt == typeRaw {
- d.dropElem(kind)
- } else {
- if info, ok := fieldsMap[name]; ok {
- if info.Inline == nil {
- d.readElemTo(out.Field(info.Num), kind)
+ case reflect.Struct:
+ if outt == typeRaw {
+ d.dropElem(kind)
+ } else {
+ if info, ok := fieldsMap[name]; ok {
+ if info.Inline == nil {
+ d.readElemTo(out.Field(info.Num), kind)
+ } else {
+ d.readElemTo(out.FieldByIndex(info.Inline), kind)
+ }
+ } else if inlineMap.IsValid() {
+ if inlineMap.IsNil() {
+ inlineMap.Set(reflect.MakeMap(inlineMap.Type()))
+ }
+ e := reflect.New(elemType).Elem()
+ if d.readElemTo(e, kind) {
+ inlineMap.SetMapIndex(reflect.ValueOf(name), e)
+ }
} else {
- d.readElemTo(out.FieldByIndex(info.Inline), kind)
- }
- } else if inlineMap.IsValid() {
- if inlineMap.IsNil() {
- inlineMap.Set(reflect.MakeMap(inlineMap.Type()))
- }
- e := reflect.New(elemType).Elem()
- if d.readElemTo(e, kind) {
- inlineMap.SetMapIndex(reflect.ValueOf(name), e)
+ d.dropElem(kind)
}
- } else {
- d.dropElem(kind)
}
+ case reflect.Slice:
}
- case reflect.Slice:
- }
- if d.i >= end {
+ if d.i >= end {
+ corrupted()
+ }
+ }
+ d.i++ // '\x00'
+ if d.i != end {
corrupted()
}
}
- d.i++ // '\x00'
- if d.i != end {
- corrupted()
- }
d.docType = docType
if outt == typeRaw {
@@ -431,7 +435,62 @@ func (d *decoder) readDocWith(f func(kind byte, name string)) {
var blackHole = settableValueOf(struct{}{})
func (d *decoder) dropElem(kind byte) {
- d.readElemTo(blackHole, kind)
+ switch kind {
+ case 0x01, 0x09, 0x11, 0x12: // double, utc datetime, timestamp, int64
+ d.i += 8
+ case 0x02, 0x0D, 0x0E: // string, javascript, symbol
+ l := int(d.readInt32())
+ if l <= 0 || d.i+l >= len(d.in) || d.in[d.i+l-1] != 0x00 {
+ corrupted()
+ }
+ d.i += l
+ case 0x03, 0x04: // doc, array
+ d.skipDoc()
+ case 0x05: // binary
+ l := int(d.readInt32())
+ k := d.readByte()
+ if k == 0x02 && l > 4 {
+ rl := int(d.readInt32())
+ if rl != l-4 {
+ corrupted()
+ }
+ }
+ d.i += l
+ case 0x06: // undefined
+ case 0x07: // objectID
+ d.i += 12
+ case 0x08:
+ k := d.readByte()
+ if k != 0x00 && k != 0x01 {
+ corrupted()
+ }
+ case 0x0A: // null
+ case 0x0B: // regex
+ d.readCStr()
+ d.readCStr()
+ case 0x0C: // dbpointer
+ d.dropElem(0x02)
+ d.i += 12
+ case 0x0F:
+ start := d.i
+ l := int(d.readInt32())
+ d.dropElem(0x02) // string
+ d.skipDoc()
+ if d.i != start+l {
+ corrupted()
+ }
+ case 0x10: // int32
+ d.i += 4
+ case 0x13: // decimal
+ d.i += 16
+ case 0xFF, 0x7F: //min key, max key
+ default:
+ d.readElemTo(blackHole, kind)
+ }
+
+ if d.i > len(d.in) {
+ corrupted()
+ }
}
// Attempt to decode an element from the document and put it into out.
@@ -461,11 +520,11 @@ func (d *decoder) readElemTo(out reflect.Value, kind byte) (good bool) {
case typeRawDocElem:
out.Set(d.readRawDocElems(outt))
default:
- d.readDocTo(blackHole)
+ d.skipDoc()
}
return true
}
- d.readDocTo(blackHole)
+ d.skipDoc()
return true
}
@@ -529,9 +588,13 @@ func (d *decoder) readElemTo(out reflect.Value, kind byte) (good bool) {
case 0x0E: // Symbol
in = Symbol(d.readStr())
case 0x0F: // JavaScript with scope
- d.i += 4 // Skip length
+ start := d.i
+ l := int(d.readInt32())
js := JavaScript{d.readStr(), make(M)}
d.readDocTo(reflect.ValueOf(js.Scope))
+ if d.i != start+l {
+ corrupted()
+ }
in = js
case 0x10: // Int32
in = int(d.readInt32())
@@ -748,6 +811,15 @@ func (d *decoder) readElemTo(out reflect.Value, kind byte) (good bool) {
// --------------------------------------------------------------------------
// Parsers of basic types.
+func (d *decoder) skipDoc() {
+ end := int(d.readInt32())
+ end += d.i - 4
+ if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' {
+ corrupted()
+ }
+ d.i = end
+}
+
func (d *decoder) readRegEx() RegEx {
re := RegEx{}
re.Pattern = d.readCStr()
@@ -759,11 +831,15 @@ func (d *decoder) readBinary() Binary {
l := d.readInt32()
b := Binary{}
b.Kind = d.readByte()
- b.Data = d.readBytes(l)
- if b.Kind == 0x02 && len(b.Data) >= 4 {
+ if b.Kind == 0x02 && l > 4 {
// Weird obsolete format with redundant length.
- b.Data = b.Data[4:]
+ rl := d.readInt32()
+ if rl != l-4 {
+ corrupted()
+ }
+ l = rl
}
+ b.Data = d.readBytes(l)
return b
}
diff --git a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/encode.go b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/encode.go
index add39e865dd..25f1adc6303 100644
--- a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/encode.go
+++ b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/encode.go
@@ -33,6 +33,7 @@ import (
"math"
"net/url"
"reflect"
+ "sort"
"strconv"
"time"
)
@@ -419,7 +420,9 @@ func (e *encoder) addElem(name string, v reflect.Value, minSize bool) {
case RegEx:
e.addElemName(0x0B, name)
e.addCStr(s.Pattern)
- e.addCStr(s.Options)
+ options := runes(s.Options)
+ sort.Sort(options)
+ e.addCStr(string(options))
case JavaScript:
if s.Scope == nil {
@@ -455,6 +458,14 @@ func (e *encoder) addElem(name string, v reflect.Value, minSize bool) {
}
}
+// -------------
+// Helper method for sorting regex options
+type runes []rune
+
+func (a runes) Len() int { return len(a) }
+func (a runes) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a runes) Less(i, j int) bool { return a[i] < a[j] }
+
// --------------------------------------------------------------------------
// Marshaling of base types.
diff --git a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/specdata/update.sh b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/specdata/update.sh
index 1efd3d3b66d..7b1141c105e 100755
--- a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/specdata/update.sh
+++ b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/specdata/update.sh
@@ -1,27 +1,9 @@
-#!/bin/sh
+#/bin/sh
set -e
-if [ ! -d specifications ]; then
- git clone -b bson git@github.com:jyemin/specifications
-fi
+rm -rf specifications
-TESTFILE="../specdata_test.go"
+git clone git@github.com:mongodb/specifications
-cat <<END > $TESTFILE
-package bson_test
-
-var specTests = []string{
-END
-
-for file in specifications/source/bson/tests/*.yml; do
- (
- echo '`'
- cat $file
- echo -n '`,'
- ) >> $TESTFILE
-done
-
-echo '}' >> $TESTFILE
-
-gofmt -w $TESTFILE
+go generate ../ \ No newline at end of file
diff --git a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/specdata_test.go b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/specdata_test.go
deleted file mode 100644
index 513f9b209c7..00000000000
--- a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/bson/specdata_test.go
+++ /dev/null
@@ -1,241 +0,0 @@
-package bson_test
-
-var specTests = []string{
- `
----
-description: "Array type"
-documents:
- -
- decoded:
- a : []
- encoded: 0D000000046100050000000000
- -
- decoded:
- a: [10]
- encoded: 140000000461000C0000001030000A0000000000
- -
- # Decode an array that uses an empty string as the key
- decodeOnly : true
- decoded:
- a: [10]
- encoded: 130000000461000B00000010000A0000000000
- -
- # Decode an array that uses a non-numeric string as the key
- decodeOnly : true
- decoded:
- a: [10]
- encoded: 150000000461000D000000106162000A0000000000
-
-
-`, `
----
-description: "Boolean type"
-documents:
- -
- encoded: "090000000862000100"
- decoded: { "b" : true }
- -
- encoded: "090000000862000000"
- decoded: { "b" : false }
-
-
- `, `
----
-description: "Corrupted BSON"
-documents:
- -
- encoded: "09000000016600"
- error: "truncated double"
- -
- encoded: "09000000026600"
- error: "truncated string"
- -
- encoded: "09000000036600"
- error: "truncated document"
- -
- encoded: "09000000046600"
- error: "truncated array"
- -
- encoded: "09000000056600"
- error: "truncated binary"
- -
- encoded: "09000000076600"
- error: "truncated objectid"
- -
- encoded: "09000000086600"
- error: "truncated boolean"
- -
- encoded: "09000000096600"
- error: "truncated date"
- -
- encoded: "090000000b6600"
- error: "truncated regex"
- -
- encoded: "090000000c6600"
- error: "truncated db pointer"
- -
- encoded: "0C0000000d6600"
- error: "truncated javascript"
- -
- encoded: "0C0000000e6600"
- error: "truncated symbol"
- -
- encoded: "0C0000000f6600"
- error: "truncated javascript with scope"
- -
- encoded: "0C000000106600"
- error: "truncated int32"
- -
- encoded: "0C000000116600"
- error: "truncated timestamp"
- -
- encoded: "0C000000126600"
- error: "truncated int64"
- -
- encoded: "0400000000"
- error: basic
- -
- encoded: "0500000001"
- error: basic
- -
- encoded: "05000000"
- error: basic
- -
- encoded: "0700000002610078563412"
- error: basic
- -
- encoded: "090000001061000500"
- error: basic
- -
- encoded: "00000000000000000000"
- error: basic
- -
- encoded: "1300000002666f6f00040000006261720000"
- error: "basic"
- -
- encoded: "1800000003666f6f000f0000001062617200ffffff7f0000"
- error: basic
- -
- encoded: "1500000003666f6f000c0000000862617200010000"
- error: basic
- -
- encoded: "1c00000003666f6f001200000002626172000500000062617a000000"
- error: basic
- -
- encoded: "1000000002610004000000616263ff00"
- error: string is not null-terminated
- -
- encoded: "0c0000000200000000000000"
- error: bad_string_length
- -
- encoded: "120000000200ffffffff666f6f6261720000"
- error: bad_string_length
- -
- encoded: "0c0000000e00000000000000"
- error: bad_string_length
- -
- encoded: "120000000e00ffffffff666f6f6261720000"
- error: bad_string_length
- -
- encoded: "180000000c00fa5bd841d6585d9900"
- error: ""
- -
- encoded: "1e0000000c00ffffffff666f6f626172005259b56afa5bd841d6585d9900"
- error: bad_string_length
- -
- encoded: "0c0000000d00000000000000"
- error: bad_string_length
- -
- encoded: "0c0000000d00ffffffff0000"
- error: bad_string_length
- -
- encoded: "1c0000000f001500000000000000000c000000020001000000000000"
- error: bad_string_length
- -
- encoded: "1c0000000f0015000000ffffffff000c000000020001000000000000"
- error: bad_string_length
- -
- encoded: "1c0000000f001500000001000000000c000000020000000000000000"
- error: bad_string_length
- -
- encoded: "1c0000000f001500000001000000000c0000000200ffffffff000000"
- error: bad_string_length
- -
- encoded: "0E00000008616263646566676869707172737475"
- error: "Run-on CString"
- -
- encoded: "0100000000"
- error: "An object size that's too small to even include the object size, but is correctly encoded, along with a correct EOO (and no data)"
- -
- encoded: "1a0000000e74657374000c00000068656c6c6f20776f726c6400000500000000"
- error: "One object, but with object size listed smaller than it is in the data"
- -
- encoded: "05000000"
- error: "One object, missing the EOO at the end"
- -
- encoded: "0500000001"
- error: "One object, sized correctly, with a spot for an EOO, but the EOO is 0x01"
- -
- encoded: "05000000ff"
- error: "One object, sized correctly, with a spot for an EOO, but the EOO is 0xff"
- -
- encoded: "0500000070"
- error: "One object, sized correctly, with a spot for an EOO, but the EOO is 0x70"
- -
- encoded: "07000000000000"
- error: "Invalid BSON type low range"
- -
- encoded: "07000000800000"
- error: "Invalid BSON type high range"
- -
- encoded: "090000000862000200"
- error: "Invalid boolean value of 2"
- -
- encoded: "09000000086200ff00"
- error: "Invalid boolean value of -1"
- `, `
----
-description: "Int32 type"
-documents:
- -
- decoded:
- i: -2147483648
- encoded: 0C0000001069000000008000
- -
- decoded:
- i: 2147483647
- encoded: 0C000000106900FFFFFF7F00
- -
- decoded:
- i: -1
- encoded: 0C000000106900FFFFFFFF00
- -
- decoded:
- i: 0
- encoded: 0C0000001069000000000000
- -
- decoded:
- i: 1
- encoded: 0C0000001069000100000000
-
-`, `
----
-description: "String type"
-documents:
- -
- decoded:
- s : ""
- encoded: 0D000000027300010000000000
- -
- decoded:
- s: "a"
- encoded: 0E00000002730002000000610000
- -
- decoded:
- s: "This is a string"
- encoded: 1D0000000273001100000054686973206973206120737472696E670000
- -
- decoded:
- s: "κόσμε"
- encoded: 180000000273000C000000CEBAE1BDB9CF83CEBCCEB50000
-`}
diff --git a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/internal/json/decode.go b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/internal/json/decode.go
index ce7c7d2493d..2171d91a711 100644
--- a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/internal/json/decode.go
+++ b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/internal/json/decode.go
@@ -773,7 +773,7 @@ func (d *decodeState) isNull(off int) bool {
// name consumes a const or function from d.data[d.off-1:], decoding into the value v.
// the first byte of the function name has been read already.
func (d *decodeState) name(v reflect.Value) {
- if d.isNull(d.off-1) {
+ if d.isNull(d.off - 1) {
d.literal(v)
return
}
@@ -1036,7 +1036,7 @@ func (d *decodeState) keyed() (interface{}, bool) {
break
}
- name := d.data[d.off-1+start : d.off-1+end]
+ name := bytes.Trim(d.data[d.off-1+start:d.off-1+end], " \n\t")
var key []byte
var ok bool
@@ -1076,9 +1076,9 @@ func (d *decodeState) storeKeyed(v reflect.Value) bool {
}
var (
- trueBytes = []byte("true")
+ trueBytes = []byte("true")
falseBytes = []byte("false")
- nullBytes = []byte("null")
+ nullBytes = []byte("null")
)
func (d *decodeState) storeValue(v reflect.Value, from interface{}) {
diff --git a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/internal/json/extension_test.go b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/internal/json/extension_test.go
index 8c228189724..c5db5cf559f 100644
--- a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/internal/json/extension_test.go
+++ b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/internal/json/extension_test.go
@@ -131,6 +131,9 @@ var extDecodeTests = []extDecodeTest{
{in: `Const1`, ptr: new(interface{}), out: const1},
{in: `{"c": Const1}`, ptr: new(struct{ C *const1Type }), out: struct{ C *const1Type }{const1}},
+ // Space after quoted key
+ {in: `{"c" : Const1}`, ptr: new(struct{ C *const1Type }), out: struct{ C *const1Type }{const1}},
+
// Keyed documents
{in: `{"v": {"$key1": 1}}`, ptr: new(interface{}), out: map[string]interface{}{"v": keyed(`{"$key1": 1}`)}},
{in: `{"k": {"$key1": 1}}`, ptr: new(keyedType), out: keyedType{K: keyed(`{"$key1": 1}`)}},
diff --git a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/internal/json/stream_test.go b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/internal/json/stream_test.go
index 0abdf7b5654..4ebeaba961c 100644
--- a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/internal/json/stream_test.go
+++ b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/internal/json/stream_test.go
@@ -131,7 +131,7 @@ func TestDecoder(t *testing.T) {
for _, c := range nlines(streamEncoded, i) {
// That's stupid isn't it!? nulltrue!?!? :/
//if c != '\n' {
- buf.WriteRune(c)
+ buf.WriteRune(c)
//}
}
out := make([]interface{}, i)
diff --git a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/session.go b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/session.go
index 12ca8f2ac37..108cdae8848 100644
--- a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/session.go
+++ b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/session.go
@@ -279,44 +279,81 @@ func ParseURL(url string) (*DialInfo, error) {
source := ""
setName := ""
poolLimit := 0
- for k, v := range uinfo.options {
- switch k {
+ readPreferenceMode := Primary
+ var readPreferenceTagSets []bson.D
+ for _, opt := range uinfo.options {
+ switch opt.key {
case "authSource":
- source = v
+ source = opt.value
case "authMechanism":
- mechanism = v
+ mechanism = opt.value
case "gssapiServiceName":
- service = v
+ service = opt.value
case "replicaSet":
- setName = v
+ setName = opt.value
case "maxPoolSize":
- poolLimit, err = strconv.Atoi(v)
+ poolLimit, err = strconv.Atoi(opt.value)
if err != nil {
- return nil, errors.New("bad value for maxPoolSize: " + v)
+ return nil, errors.New("bad value for maxPoolSize: " + opt.value)
}
+ case "readPreference":
+ switch opt.value {
+ case "nearest":
+ readPreferenceMode = Nearest
+ case "primary":
+ readPreferenceMode = Primary
+ case "primaryPreferred":
+ readPreferenceMode = PrimaryPreferred
+ case "secondary":
+ readPreferenceMode = Secondary
+ case "secondaryPreferred":
+ readPreferenceMode = SecondaryPreferred
+ default:
+ return nil, errors.New("bad value for readPreference: " + opt.value)
+ }
+ case "readPreferenceTags":
+ tags := strings.Split(opt.value, ",")
+ var doc bson.D
+ for _, tag := range tags {
+ kvp := strings.Split(tag, ":")
+ if len(kvp) != 2 {
+ return nil, errors.New("bad value for readPreferenceTags: " + opt.value)
+ }
+ doc = append(doc, bson.DocElem{Name: strings.TrimSpace(kvp[0]), Value: strings.TrimSpace(kvp[1])})
+ }
+ readPreferenceTagSets = append(readPreferenceTagSets, doc)
case "connect":
- if v == "direct" {
+ if opt.value == "direct" {
direct = true
break
}
- if v == "replicaSet" {
+ if opt.value == "replicaSet" {
break
}
fallthrough
default:
- return nil, errors.New("unsupported connection URL option: " + k + "=" + v)
+ return nil, errors.New("unsupported connection URL option: " + opt.key + "=" + opt.value)
}
}
+
+ if readPreferenceMode == Primary && len(readPreferenceTagSets) > 0 {
+ return nil, errors.New("readPreferenceTagSet may not be specified when readPreference is primary")
+ }
+
info := DialInfo{
- Addrs: uinfo.addrs,
- Direct: direct,
- Database: uinfo.db,
- Username: uinfo.user,
- Password: uinfo.pass,
- Mechanism: mechanism,
- Service: service,
- Source: source,
- PoolLimit: poolLimit,
+ Addrs: uinfo.addrs,
+ Direct: direct,
+ Database: uinfo.db,
+ Username: uinfo.user,
+ Password: uinfo.pass,
+ Mechanism: mechanism,
+ Service: service,
+ Source: source,
+ PoolLimit: poolLimit,
+ ReadPreference: &ReadPreference{
+ Mode: readPreferenceMode,
+ TagSets: readPreferenceTagSets,
+ },
ReplicaSetName: setName,
}
return &info, nil
@@ -384,6 +421,10 @@ type DialInfo struct {
// See Session.SetPoolLimit for details.
PoolLimit int
+ // ReadPreference defines the manner in which servers are chosen. See
+ // Session.SetMode and Session.SelectServers.
+ ReadPreference *ReadPreference
+
// DialServer optionally specifies the dial function for establishing
// connections with the MongoDB servers.
DialServer func(addr *ServerAddr) (net.Conn, error)
@@ -392,6 +433,15 @@ type DialInfo struct {
Dial func(addr net.Addr) (net.Conn, error)
}
+// ReadPreference defines the manner in which servers are chosen.
+type ReadPreference struct {
+ // Mode determines the consistency of results. See Session.SetMode.
+ Mode Mode
+
+ // TagSets indicates which servers are allowed to be used. See Session.SelectServers.
+ TagSets []bson.D
+}
+
// mgo.v3: Drop DialInfo.Dial.
// ServerAddr represents the address for establishing a connection to an
@@ -464,7 +514,14 @@ func DialWithInfo(info *DialInfo) (*Session, error) {
session.Close()
return nil, err
}
- session.SetMode(Strong, true)
+
+ if info.ReadPreference != nil {
+ session.SelectServers(info.ReadPreference.TagSets...)
+ session.SetMode(info.ReadPreference.Mode, true)
+ } else {
+ session.SetMode(Strong, true)
+ }
+
return session, nil
}
@@ -477,21 +534,26 @@ type urlInfo struct {
user string
pass string
db string
- options map[string]string
+ options []urlInfoOption
+}
+
+type urlInfoOption struct {
+ key string
+ value string
}
func extractURL(s string) (*urlInfo, error) {
if strings.HasPrefix(s, "mongodb://") {
s = s[10:]
}
- info := &urlInfo{options: make(map[string]string)}
+ info := &urlInfo{}
if c := strings.Index(s, "?"); c != -1 {
for _, pair := range strings.FieldsFunc(s[c+1:], isOptSep) {
l := strings.SplitN(pair, "=", 2)
if len(l) != 2 || l[0] == "" || l[1] == "" {
return nil, errors.New("connection option must be key=value: " + pair)
}
- info.options[l[0]] = l[1]
+ info.options = append(info.options, urlInfoOption{key: l[0], value: l[1]})
}
s = s[:c]
}
@@ -569,6 +631,17 @@ func (s *Session) LiveServers() (addrs []string) {
return addrs
}
+// ReadableServer returns a server address which is suitable for reading
+// according to the current session.
+func (s *Session) ReadableServer() (string, error) {
+ socket, err := s.acquireSocket(true)
+ if err != nil {
+ return "", err
+ }
+ defer socket.Release()
+ return socket.server.Addr, nil
+}
+
// DB returns a value representing the named database. If name
// is empty, the database name provided in the dialed URL is
// used instead. If that is also empty, "test" is used as a
@@ -1096,6 +1169,14 @@ type Collation struct {
// distinguished at strength > 3). Defaults to "non-ignorable".
Alternate string `bson:"alternate,omitempty"`
+ // MaxVariable defines which characters are affected when the value for Alternate is
+ // "shifted". It may be set to "punct" to affect punctuation or spaces, or "space" to
+ // affect only spaces.
+ MaxVariable string `bson:"maxVariable,omitempty"`
+
+ // Normalization defines whether text is normalized into Unicode NFD.
+ Normalization bool `bson:"normalization,omitempty"`
+
// Backwards defines whether to have secondary differences considered in reverse order,
// as done in the French language.
Backwards bool `bson:"backwards,omitempty"`
@@ -2299,6 +2380,11 @@ func (c *Collection) NewIter(session *Session, firstBatch []bson.Raw, cursorId i
timeout: -1,
err: err,
}
+
+ if socket.ServerInfo().MaxWireVersion >= 4 && c.FullName != "admin.$cmd" {
+ iter.findCmd = true
+ }
+
iter.gotReply.L = &iter.m
for _, doc := range firstBatch {
iter.docData.Push(doc.Data)
@@ -2858,6 +2944,14 @@ func (q *Query) Sort(fields ...string) *Query {
return q
}
+func (q *Query) Collation(collation *Collation) *Query {
+ q.m.Lock()
+ q.op.options.Collation = collation
+ q.op.hasOptions = true
+ q.m.Unlock()
+ return q
+}
+
// Explain returns a number of details about how the MongoDB server would
// execute the requested query, such as the number of objects examined,
// the number of times the read lock was yielded to allow writes to go in,
@@ -3161,6 +3255,7 @@ func prepareFindOp(socket *mongoSocket, op *queryOp, limit int32) bool {
Comment: op.options.Comment,
Snapshot: op.options.Snapshot,
OplogReplay: op.flags&flagLogReplay != 0,
+ Collation: op.options.Collation,
}
if op.limit < 0 {
find.BatchSize = -op.limit
@@ -3222,6 +3317,7 @@ type findCmd struct {
OplogReplay bool `bson:"oplogReplay,omitempty"`
NoCursorTimeout bool `bson:"noCursorTimeout,omitempty"`
AllowPartialResults bool `bson:"allowPartialResults,omitempty"`
+ Collation *Collation `bson:"collation,omitempty"`
}
// getMoreCmd holds the command used for requesting more query results on MongoDB 3.2+.
diff --git a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/session_test.go b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/session_test.go
index a89279d38b1..314ace5cd5c 100644
--- a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/session_test.go
+++ b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/session_test.go
@@ -132,6 +132,93 @@ func (s *S) TestURLParsing(c *C) {
}
}
+func (s *S) TestURLReadPreference(c *C) {
+ type test struct {
+ url string
+ mode mgo.Mode
+ }
+
+ tests := []test{
+ {"localhost:40001?readPreference=primary", mgo.Primary},
+ {"localhost:40001?readPreference=primaryPreferred", mgo.PrimaryPreferred},
+ {"localhost:40001?readPreference=secondary", mgo.Secondary},
+ {"localhost:40001?readPreference=secondaryPreferred", mgo.SecondaryPreferred},
+ {"localhost:40001?readPreference=nearest", mgo.Nearest},
+ }
+
+ for _, test := range tests {
+ info, err := mgo.ParseURL(test.url)
+ c.Assert(err, IsNil)
+ c.Assert(info.ReadPreference, NotNil)
+ c.Assert(info.ReadPreference.Mode, Equals, test.mode)
+ }
+}
+
+func (s *S) TestURLInvalidReadPreference(c *C) {
+ urls := []string{
+ "localhost:40001?readPreference=foo",
+ "localhost:40001?readPreference=primarypreferred",
+ }
+ for _, url := range urls {
+ _, err := mgo.ParseURL(url)
+ c.Assert(err, NotNil)
+ }
+}
+
+func (s *S) TestURLReadPreferenceTags(c *C) {
+ type test struct {
+ url string
+ tagSets []bson.D
+ }
+
+ tests := []test{
+ {"localhost:40001?readPreference=secondary&readPreferenceTags=dc:ny,rack:1", []bson.D{{{"dc", "ny"}, {"rack", "1"}}}},
+ {"localhost:40001?readPreference=secondary&readPreferenceTags= dc : ny , rack : 1 ", []bson.D{{{"dc", "ny"}, {"rack", "1"}}}},
+ {"localhost:40001?readPreference=secondary&readPreferenceTags=dc:ny", []bson.D{{{"dc", "ny"}}}},
+ {"localhost:40001?readPreference=secondary&readPreferenceTags=rack:1&readPreferenceTags=dc:ny", []bson.D{{{"rack", "1"}}, {{"dc", "ny"}}}},
+ }
+
+ for _, test := range tests {
+ info, err := mgo.ParseURL(test.url)
+ c.Assert(err, IsNil)
+ c.Assert(info.ReadPreference, NotNil)
+ c.Assert(info.ReadPreference.TagSets, DeepEquals, test.tagSets)
+ }
+}
+
+func (s *S) TestURLInvalidReadPreferenceTags(c *C) {
+ urls := []string{
+ "localhost:40001?readPreference=secondary&readPreferenceTags=dc",
+ "localhost:40001?readPreference=secondary&readPreferenceTags=dc:ny,rack",
+ "localhost:40001?readPreference=secondary&readPreferenceTags=dc,rack",
+ "localhost:40001?readPreference=primary&readPreferenceTags=dc:ny",
+ }
+ for _, url := range urls {
+ _, err := mgo.ParseURL(url)
+ c.Assert(err, NotNil)
+ }
+}
+
+func (s *S) TestReadableServer(c *C) {
+ session, err := mgo.Dial("localhost:40011,localhost:40012,localhost:40013")
+ c.Assert(err, IsNil)
+ defer session.Close()
+
+ session.SetMode(mgo.Primary, true)
+ primary_address, err := session.ReadableServer()
+ c.Assert(err, IsNil)
+ c.Assert(primary_address, Equals, "localhost:40011")
+
+ session.SetMode(mgo.Secondary, true)
+ secondary_address, err := session.ReadableServer()
+ c.Assert(err, IsNil)
+
+ valid_addresses := []string{"localhost:40012", "localhost:40013"}
+ if secondary_address != valid_addresses[0] && secondary_address != valid_addresses[1] {
+ c.Fatalf("secondary_address should be in %v, not: %v", valid_addresses, secondary_address)
+ }
+}
+
func (s *S) TestInsertFindOne(c *C) {
session, err := mgo.Dial("localhost:40001")
c.Assert(err, IsNil)
@@ -4159,11 +4246,11 @@ func (s *S) TestBypassValidation(c *C) {
func (s *S) TestVersionAtLeast(c *C) {
tests := [][][]int{
- {{3,2,1}, {3,2,0}},
- {{3,2,1}, {3,2}},
- {{3,2,1}, {2,5,5,5}},
- {{3,2,1}, {2,5,5}},
- {{3,2,1}, {2,5}},
+ {{3, 2, 1}, {3, 2, 0}},
+ {{3, 2, 1}, {3, 2}},
+ {{3, 2, 1}, {2, 5, 5, 5}},
+ {{3, 2, 1}, {2, 5, 5}},
+ {{3, 2, 1}, {2, 5}},
}
for _, pair := range tests {
bi := mgo.BuildInfo{VersionArray: pair[0]}
@@ -4180,6 +4267,48 @@ func (s *S) TestVersionAtLeast(c *C) {
}
}
+func (s *S) TestCollationQueries(c *C) {
+ if !s.versionAtLeast(3, 3, 12) {
+ c.Skip("collations being released with 3.4")
+ }
+ session, err := mgo.Dial("localhost:40001")
+ c.Assert(err, IsNil)
+ defer session.Close()
+
+ docsToInsert := []bson.M{
+ {"text_number": "010"},
+ {"text_number": "2"},
+ {"text_number": "10"},
+ }
+
+ coll := session.DB("mydb").C("mycoll")
+ for _, doc := range docsToInsert {
+ err = coll.Insert(doc)
+ c.Assert(err, IsNil)
+ }
+
+ collation := &mgo.Collation{
+ Locale: "en",
+ NumericOrdering: true,
+ }
+
+ err = coll.EnsureIndex(mgo.Index{
+ Key: []string{"text_number"},
+ Collation: collation,
+ })
+ c.Assert(err, IsNil)
+
+ iter := coll.Find(nil).Sort("text_number").Collation(collation).Iter()
+ defer iter.Close()
+ for _, expectedRes := range []string{"2", "010", "10"} {
+ res := make(bson.M)
+ found := iter.Next(&res)
+ c.Assert(iter.Err(), IsNil)
+ c.Assert(found, Equals, true)
+ c.Assert(res["text_number"], Equals, expectedRes)
+ }
+}
+
// --------------------------------------------------------------------------
// Some benchmarks that require a running database.
diff --git a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/socket.go b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/socket.go
index 8891dd5d734..f4941269217 100644
--- a/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/socket.go
+++ b/src/mongo/gotools/vendor/src/gopkg.in/mgo.v2/socket.go
@@ -91,6 +91,7 @@ type queryWrapper struct {
MaxScan int "$maxScan,omitempty"
MaxTimeMS int "$maxTimeMS,omitempty"
Comment string "$comment,omitempty"
+ Collation *Collation "$collation,omitempty"
}
func (op *queryOp) finalQuery(socket *mongoSocket) interface{} {