diff options
author | mike o'brien <mpobrien005@gmail.com> | 2014-10-31 17:44:18 -0400 |
---|---|---|
committer | mike o'brien <mpobrien005@gmail.com> | 2014-10-31 17:44:18 -0400 |
commit | cbe7045c2f95bd79c76f8249252333b53cf2027c (patch) | |
tree | 320fee4f253ba213ba2a18b2d3b7dcc7b7f45d11 | |
parent | badedb9cf4f931245a43ba90753f96e7f6990336 (diff) | |
download | mongo-cbe7045c2f95bd79c76f8249252333b53cf2027c.tar.gz |
TOOLS-328 switch back to mgo mainline
Former-commit-id: e7a604ead7164e42ada231105cd53c1e3ba535e6
21 files changed, 873 insertions, 199 deletions
@@ -1,4 +1,4 @@ -gopkg.in/mgo.v2 98b2d11ed7d2eabebc1db08a581d8d511209bb62 github.com/vkarpov15/mgo +gopkg.in/mgo.v2 231ce7e0549b5ce60d35ed805bff505cab7098e4 github.com/jacobsa/oglematchers 4fc24f97b5b74022c2a3f4ca7eed57ca29083d3e github.com/smartystreets/goconvey 75bc4a2dad71e5c5b51c5009b33eba766ec57051 github.com/jessevdk/go-flags 8ec9564882e7923e632f012761c81c46dcf5bec1 diff --git a/vendor/src/github.com/spacemonkeygo/spacelog/setup.go b/vendor/src/github.com/spacemonkeygo/spacelog/setup.go index c0c6327e674..bd46b0ab27e 100644 --- a/vendor/src/github.com/spacemonkeygo/spacelog/setup.go +++ b/vendor/src/github.com/spacemonkeygo/spacelog/setup.go @@ -140,7 +140,7 @@ func Setup(procname string, config SetupConfig) error { t = ColorTemplate } textout = NewWriterOutput(os.Stdout) - case "stderr": + case "stderr", "": if t == nil { t = ColorTemplate } @@ -171,6 +171,9 @@ func Setup(procname string, config SetupConfig) error { } SetHandler(nil, NewTextHandler(t, textout)) log.SetFlags(log.Lshortfile) + if config.Stdlevel == "" { + config.Stdlevel = "warn" + } stdlog_level_val, err := LevelFromString(config.Stdlevel) if err != nil { return err diff --git a/vendor/src/gopkg.in/mgo.v2/auth.go b/vendor/src/gopkg.in/mgo.v2/auth.go index cbc710a801a..1761d0d6bb2 100644 --- a/vendor/src/gopkg.in/mgo.v2/auth.go +++ b/vendor/src/gopkg.in/mgo.v2/auth.go @@ -160,6 +160,9 @@ 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" + } 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) @@ -179,12 +182,12 @@ func (socket *mongoSocket) Login(cred Credential) error { var err error switch cred.Mechanism { - case "", "MONGODB-CR": + case "", "MONGODB-CR", "MONGO-CR": // Name changed to MONGODB-CR in SERVER-8501. err = socket.loginClassic(cred) case "PLAIN": err = socket.loginPlain(cred) - case "MONGO-X509": - err = fmt.Errorf("unsupported authentication mechanism: %s", cred.Mechanism) + case "MONGODB-X509": + err = socket.loginX509(cred) default: // Try SASL for everything else, if it is available. err = socket.loginSASL(cred) @@ -232,6 +235,27 @@ func (socket *mongoSocket) loginClassic(cred Credential) error { }) } +type authX509Cmd struct { + Authenticate int + User string + Mechanism string +} + +func (socket *mongoSocket) loginX509(cred Credential) error { + cmd := authX509Cmd{Authenticate: 1, User: cred.Username, Mechanism: "MONGODB-X509"} + res := authResult{} + return socket.loginRun(cred.Source, &cmd, &res, func() error { + if !res.Ok { + return errors.New(res.ErrMsg) + } + socket.Lock() + socket.dropAuth(cred.Source) + socket.creds = append(socket.creds, cred) + socket.Unlock() + return nil + }) +} + func (socket *mongoSocket) loginPlain(cred Credential) error { cmd := saslCmd{Start: 1, Mechanism: "PLAIN", Payload: []byte("\x00" + cred.Username + "\x00" + cred.Password)} res := authResult{} diff --git a/vendor/src/gopkg.in/mgo.v2/auth_test.go b/vendor/src/gopkg.in/mgo.v2/auth_test.go index 46e0d2b11cd..a9c0b27f8e0 100644 --- a/vendor/src/gopkg.in/mgo.v2/auth_test.go +++ b/vendor/src/gopkg.in/mgo.v2/auth_test.go @@ -27,8 +27,11 @@ package mgo_test import ( + "crypto/tls" "flag" "fmt" + "io/ioutil" + "net" "net/url" "os" "runtime" @@ -53,7 +56,7 @@ func (s *S) TestAuthLoginDatabase(c *C) { admindb := session.DB("admin") err = admindb.Login("root", "wrong") - c.Assert(err, ErrorMatches, "auth fail(s|ed)") + c.Assert(err, ErrorMatches, "auth fail(s|ed)|.*Authentication failed.") err = admindb.Login("root", "rapadura") c.Assert(err, IsNil) @@ -79,7 +82,7 @@ func (s *S) TestAuthLoginSession(c *C) { Password: "wrong", } err = session.Login(&cred) - c.Assert(err, ErrorMatches, "auth fail(s|ed)") + c.Assert(err, ErrorMatches, "auth fail(s|ed)|.*Authentication failed.") cred.Password = "rapadura" @@ -160,7 +163,7 @@ func (s *S) TestAuthUpsertUserErrors(c *C) { c.Assert(err, ErrorMatches, "user has both Password/PasswordHash and UserSource set") err = mydb.UpsertUser(&mgo.User{Username: "user", Password: "pass", OtherDBRoles: map[string][]mgo.Role{"db": nil}}) - c.Assert(err, ErrorMatches, "user with OtherDBRoles is only supported in admin database") + c.Assert(err, ErrorMatches, "user with OtherDBRoles is only supported in the admin or \\$external databases") } func (s *S) TestAuthUpsertUser(c *C) { @@ -241,7 +244,7 @@ func (s *S) TestAuthUpsertUser(c *C) { // Can't login directly into the database using UserSource, though. err = myotherdb.Login("myrwuser", "mypass") - c.Assert(err, ErrorMatches, "auth fail(s|ed)") + c.Assert(err, ErrorMatches, "auth fail(s|ed)|.*Authentication failed.") } func (s *S) TestAuthUpsertUserOtherDBRoles(c *C) { @@ -386,7 +389,7 @@ func (s *S) TestAuthAddUserReplaces(c *C) { admindb.Logout() err = mydb.Login("myuser", "myoldpass") - c.Assert(err, ErrorMatches, "auth fail(s|ed)") + c.Assert(err, ErrorMatches, "auth fail(s|ed)|.*Authentication failed.") err = mydb.Login("myuser", "mynewpass") c.Assert(err, IsNil) @@ -413,7 +416,7 @@ func (s *S) TestAuthRemoveUser(c *C) { c.Assert(err, Equals, mgo.ErrNotFound) err = mydb.Login("myuser", "mypass") - c.Assert(err, ErrorMatches, "auth fail(s|ed)") + c.Assert(err, ErrorMatches, "auth fail(s|ed)|.*Authentication failed.") } func (s *S) TestAuthLoginTwiceDoesNothing(c *C) { @@ -731,7 +734,7 @@ func (s *S) TestAuthURLWrongCredentials(c *C) { if session != nil { session.Close() } - c.Assert(err, ErrorMatches, "auth fail(s|ed)") + c.Assert(err, ErrorMatches, "auth fail(s|ed)|.*Authentication failed.") c.Assert(session, IsNil) } @@ -842,12 +845,9 @@ func (s *S) TestAuthDirectWithLogin(c *C) { } } -// TODO SCRAM-SHA-1 will become the default, and this flag will go away. -var scramFlag = flag.String("scram", "", "Host to test SCRAM-SHA-1 authentication against (depends on custom environment)") - func (s *S) TestAuthScramSha1Cred(c *C) { - if *scramFlag == "" { - c.Skip("no -plain") + if !s.versionAtLeast(2, 7, 7) { + c.Skip("SCRAM-SHA-1 tests depend on 2.7.7") } cred := &mgo.Credential{ Username: "root", @@ -855,8 +855,9 @@ func (s *S) TestAuthScramSha1Cred(c *C) { Mechanism: "SCRAM-SHA-1", Source: "admin", } - c.Logf("Connecting to %s...", *scramFlag) - session, err := mgo.Dial(*scramFlag) + host := "localhost:40002" + c.Logf("Connecting to %s...", host) + session, err := mgo.Dial(host) c.Assert(err, IsNil) defer session.Close() @@ -876,6 +877,92 @@ func (s *S) TestAuthScramSha1Cred(c *C) { c.Assert(err, Equals, mgo.ErrNotFound) } +func (s *S) TestAuthScramSha1URL(c *C) { + if !s.versionAtLeast(2, 7, 7) { + c.Skip("SCRAM-SHA-1 tests depend on 2.7.7") + } + host := "localhost:40002" + c.Logf("Connecting to %s...", host) + session, err := mgo.Dial(fmt.Sprintf("root:rapadura@%s?authMechanism=SCRAM-SHA-1", host)) + c.Assert(err, IsNil) + defer session.Close() + + mycoll := session.DB("admin").C("mycoll") + + c.Logf("Connected! Testing the need for authentication...") + err = mycoll.Find(nil).One(nil) + c.Assert(err, Equals, mgo.ErrNotFound) +} + +func (s *S) TestAuthX509Cred(c *C) { + session, err := mgo.Dial("localhost:40001") + c.Assert(err, IsNil) + defer session.Close() + binfo, err := session.BuildInfo() + c.Assert(err, IsNil) + if binfo.OpenSSLVersion == "" { + c.Skip("server does not support SSL") + } + + clientCertPEM, err := ioutil.ReadFile("testdb/client.pem") + c.Assert(err, IsNil) + + clientCert, err := tls.X509KeyPair(clientCertPEM, clientCertPEM) + c.Assert(err, IsNil) + + tlsConfig := &tls.Config{ + // Isolating tests to client certs, don't care about server validation. + InsecureSkipVerify: true, + Certificates: []tls.Certificate{clientCert}, + } + + var host = "localhost:40003" + c.Logf("Connecting to %s...", host) + session, err = mgo.DialWithInfo(&mgo.DialInfo{ + Addrs: []string{host}, + DialServer: func(addr *mgo.ServerAddr) (net.Conn, error) { + return tls.Dial("tcp", addr.String(), tlsConfig) + }, + }) + c.Assert(err, IsNil) + defer session.Close() + + err = session.Login(&mgo.Credential{Username: "root", Password: "rapadura"}) + c.Assert(err, IsNil) + + // This needs to be kept in sync with client.pem + x509Subject := "CN=localhost,OU=Client,O=MGO,L=MGO,ST=MGO,C=GO" + + externalDB := session.DB("$external") + var x509User mgo.User = mgo.User{ + Username: x509Subject, + OtherDBRoles: map[string][]mgo.Role{"admin": []mgo.Role{mgo.RoleRoot}}, + } + err = externalDB.UpsertUser(&x509User) + c.Assert(err, IsNil) + + session.LogoutAll() + + c.Logf("Connected! Ensuring authentication is required...") + names, err := session.DatabaseNames() + c.Assert(err, ErrorMatches, "not authorized .*") + + cred := &mgo.Credential{ + Username: x509Subject, + Mechanism: "MONGODB-X509", + Source: "$external", + } + + c.Logf("Authenticating...") + err = session.Login(cred) + c.Assert(err, IsNil) + c.Logf("Authenticated!") + + names, err = session.DatabaseNames() + c.Assert(err, IsNil) + c.Assert(len(names) > 0, Equals, true) +} + var ( plainFlag = flag.String("plain", "", "Host to test PLAIN authentication against (depends on custom environment)") plainUser = "einstein" diff --git a/vendor/src/gopkg.in/mgo.v2/bson/bson.go b/vendor/src/gopkg.in/mgo.v2/bson/bson.go index da2273a5e81..68e932fb14b 100644 --- a/vendor/src/gopkg.in/mgo.v2/bson/bson.go +++ b/vendor/src/gopkg.in/mgo.v2/bson/bson.go @@ -386,9 +386,10 @@ type JavaScript struct { Scope interface{} } -// DBPointer is a type that refers to a document in some namespace by wrapping -// a string containing the namespace itself, and the ObjectId in which the _id -// of the document is contained +// DBPointer refers to a document id in a namespace. +// +// This type is deprecated in the BSON specification and should not be used +// except for backwards compatibility with ancient applications. type DBPointer struct { Namespace string Id ObjectId diff --git a/vendor/src/gopkg.in/mgo.v2/bson/bson_test.go b/vendor/src/gopkg.in/mgo.v2/bson/bson_test.go index 2967590271d..0606c49050b 100644 --- a/vendor/src/gopkg.in/mgo.v2/bson/bson_test.go +++ b/vendor/src/gopkg.in/mgo.v2/bson/bson_test.go @@ -67,7 +67,7 @@ func makeZeroDoc(value interface{}) (zero interface{}) { case reflect.Ptr: pv := reflect.New(v.Type().Elem()) zero = pv.Interface() - case reflect.Slice: + case reflect.Slice, reflect.Int: zero = reflect.New(t).Interface() default: panic("unsupported doc type") @@ -1026,6 +1026,36 @@ type inlineBadKeyMap struct { M map[int]int ",inline" } +type getterSetterD bson.D + +func (s getterSetterD) GetBSON() (interface{}, error) { + if len(s) == 0 { + return bson.D{}, nil + } + return bson.D(s[:len(s)-1]), nil +} + +func (s *getterSetterD) SetBSON(raw bson.Raw) error { + var doc bson.D + err := raw.Unmarshal(&doc) + doc = append(doc, bson.DocElem{"suffix", true}) + *s = getterSetterD(doc) + return err +} + +type getterSetterInt int + +func (i getterSetterInt) GetBSON() (interface{}, error) { + return bson.D{{"a", int(i)}}, nil +} + +func (i *getterSetterInt) SetBSON(raw bson.Raw) error { + var doc struct{ A int } + err := raw.Unmarshal(&doc) + *i = getterSetterInt(doc.A) + return err +} + type ( MyString string MyBytes []byte @@ -1043,6 +1073,8 @@ var ( int64ptr = &int64var intvar = int(42) intptr = &intvar + + gsintvar = getterSetterInt(42) ) func parseURL(s string) *url.URL { @@ -1228,6 +1260,7 @@ var twoWayCrossItems = []crossTypeItem{ // bson.D <=> []DocElem {&bson.D{{"a", bson.D{{"b", 1}, {"c", 2}}}}, &bson.D{{"a", bson.D{{"b", 1}, {"c", 2}}}}}, {&bson.D{{"a", bson.D{{"b", 1}, {"c", 2}}}}, &MyD{{"a", MyD{{"b", 1}, {"c", 2}}}}}, + {&struct{ V MyD }{MyD{{"a", 1}}}, &bson.D{{"v", bson.D{{"a", 1}}}}}, // bson.RawD <=> []RawDocElem {&bson.RawD{{"a", bson.Raw{0x08, []byte{0x01}}}}, &bson.RawD{{"a", bson.Raw{0x08, []byte{0x01}}}}}, @@ -1244,6 +1277,10 @@ var twoWayCrossItems = []crossTypeItem{ {&struct{ N json.Number }{"5"}, map[string]interface{}{"n": int64(5)}}, {&struct{ N json.Number }{"5.05"}, map[string]interface{}{"n": 5.05}}, {&struct{ N json.Number }{"9223372036854776000"}, map[string]interface{}{"n": float64(1 << 63)}}, + + // bson.D <=> non-struct getter/setter + {&bson.D{{"a", 1}}, &getterSetterD{{"a", 1}, {"suffix", true}}}, + {&bson.D{{"a", 42}}, &gsintvar}, } // Same thing, but only one way (obj1 => obj2). diff --git a/vendor/src/gopkg.in/mgo.v2/bson/decode.go b/vendor/src/gopkg.in/mgo.v2/bson/decode.go index b389f60e4bd..782e9338aab 100644 --- a/vendor/src/gopkg.in/mgo.v2/bson/decode.go +++ b/vendor/src/gopkg.in/mgo.v2/bson/decode.go @@ -73,35 +73,39 @@ const ( setterAddr ) -var setterStyle map[reflect.Type]int +var setterStyles map[reflect.Type]int var setterIface reflect.Type var setterMutex sync.RWMutex func init() { var iface Setter setterIface = reflect.TypeOf(&iface).Elem() - setterStyle = make(map[reflect.Type]int) + setterStyles = make(map[reflect.Type]int) } -func getSetter(outt reflect.Type, out reflect.Value) Setter { +func setterStyle(outt reflect.Type) int { setterMutex.RLock() - style := setterStyle[outt] + style := setterStyles[outt] setterMutex.RUnlock() - if style == setterNone { - return nil - } if style == setterUnknown { setterMutex.Lock() defer setterMutex.Unlock() if outt.Implements(setterIface) { - setterStyle[outt] = setterType + setterStyles[outt] = setterType } else if reflect.PtrTo(outt).Implements(setterIface) { - setterStyle[outt] = setterAddr + setterStyles[outt] = setterAddr } else { - setterStyle[outt] = setterNone - return nil + setterStyles[outt] = setterNone } - style = setterStyle[outt] + style = setterStyles[outt] + } + return style +} + +func getSetter(outt reflect.Type, out reflect.Value) Setter { + style := setterStyle(outt) + if style == setterNone { + return nil } if style == setterAddr { if !out.CanAddr() { @@ -434,20 +438,28 @@ func (d *decoder) readElemTo(out reflect.Value, kind byte) (good bool) { start := d.i if kind == '\x03' { - // Special case for documents. Delegate to readDocTo(). - switch out.Kind() { + // Delegate unmarshaling of documents. + outt := out.Type() + outk := out.Kind() + switch outk { case reflect.Interface, reflect.Ptr, reflect.Struct, reflect.Map: d.readDocTo(out) - default: - switch out.Interface().(type) { - case D: - out.Set(d.readDocElems(out.Type())) - case RawD: - out.Set(d.readRawDocElems(out.Type())) - default: - d.readDocTo(blackHole) + return true + } + if setterStyle(outt) != setterNone { + d.readDocTo(out) + return true + } + if outk == reflect.Slice { + switch outt.Elem() { + case typeDocElem: + out.Set(d.readDocElems(outt)) + case typeRawDocElem: + out.Set(d.readRawDocElems(outt)) } + return true } + d.readDocTo(blackHole) return true } diff --git a/vendor/src/gopkg.in/mgo.v2/cluster.go b/vendor/src/gopkg.in/mgo.v2/cluster.go index 10db6372d8a..104dd3988d8 100644 --- a/vendor/src/gopkg.in/mgo.v2/cluster.go +++ b/vendor/src/gopkg.in/mgo.v2/cluster.go @@ -124,13 +124,14 @@ func (cluster *mongoCluster) removeServer(server *mongoServer) { } type isMasterResult struct { - IsMaster bool - Secondary bool - Primary string - Hosts []string - Passives []string - Tags bson.D - Msg string + IsMaster bool + Secondary bool + Primary string + Hosts []string + Passives []string + Tags bson.D + Msg string + MaxWireVersion int `bson:"maxWireVersion"` } func (cluster *mongoCluster) isMaster(socket *mongoSocket, result *isMasterResult) error { @@ -214,9 +215,10 @@ func (cluster *mongoCluster) syncServer(server *mongoServer) (info *mongoServerI } info = &mongoServerInfo{ - Master: result.IsMaster, - Mongos: result.Msg == "isdbgrid", - Tags: result.Tags, + Master: result.IsMaster, + Mongos: result.Msg == "isdbgrid", + Tags: result.Tags, + MaxWireVersion: result.MaxWireVersion, } hosts = make([]string, 0, 1+len(result.Hosts)+len(result.Passives)) diff --git a/vendor/src/gopkg.in/mgo.v2/gridfs.go b/vendor/src/gopkg.in/mgo.v2/gridfs.go index 6eea5f8c549..3439462f7f5 100644 --- a/vendor/src/gopkg.in/mgo.v2/gridfs.go +++ b/vendor/src/gopkg.in/mgo.v2/gridfs.go @@ -481,6 +481,17 @@ func (file *GridFile) UploadDate() time.Time { return file.doc.UploadDate } +// SetUploadDate changes the file upload time. +// +// It is a runtime error to call this function when the file is not open +// for writing. +func (file *GridFile) SetUploadDate(t time.Time) { + file.assertMode(gfsWriting) + file.m.Lock() + file.doc.UploadDate = t + file.m.Unlock() +} + // Close flushes any pending changes in case the file is being written // to, waits for any background operations to finish, and closes the file. // @@ -515,7 +526,9 @@ func (file *GridFile) completeWrite() { return } hexsum := hex.EncodeToString(file.wsum.Sum(nil)) - file.doc.UploadDate = bson.Now() + if file.doc.UploadDate.IsZero() { + file.doc.UploadDate = bson.Now() + } file.doc.MD5 = hexsum file.err = file.gfs.Files.Insert(file.doc) file.gfs.Chunks.EnsureIndexKey("files_id", "n") diff --git a/vendor/src/gopkg.in/mgo.v2/gridfs_test.go b/vendor/src/gopkg.in/mgo.v2/gridfs_test.go index 7a9533449ee..9afd2454cf6 100644 --- a/vendor/src/gopkg.in/mgo.v2/gridfs_test.go +++ b/vendor/src/gopkg.in/mgo.v2/gridfs_test.go @@ -183,6 +183,34 @@ func (s *S) TestGridFSFileDetails(c *C) { c.Assert(result, DeepEquals, expected) } +func (s *S) TestGridFSSetUploadDate(c *C) { + session, err := mgo.Dial("localhost:40011") + c.Assert(err, IsNil) + defer session.Close() + + db := session.DB("mydb") + + gfs := db.GridFS("fs") + file, err := gfs.Create("") + c.Assert(err, IsNil) + + t := time.Date(2014, 1, 1, 1, 1, 1, 0, time.Local) + file.SetUploadDate(t) + + err = file.Close() + c.Assert(err, IsNil) + + // Check the file information. + result := M{} + err = db.C("fs.files").Find(nil).One(result) + c.Assert(err, IsNil) + + ud := result["uploadDate"].(time.Time) + if !ud.Equal(t) { + c.Fatalf("want upload date %s, got %s", t, ud) + } +} + func (s *S) TestGridFSCreateWithChunking(c *C) { session, err := mgo.Dial("localhost:40011") c.Assert(err, IsNil) diff --git a/vendor/src/gopkg.in/mgo.v2/sasl/sasl_windows.go b/vendor/src/gopkg.in/mgo.v2/sasl/sasl_windows.go index 82e904cd981..3302cfe05d6 100644 --- a/vendor/src/gopkg.in/mgo.v2/sasl/sasl_windows.go +++ b/vendor/src/gopkg.in/mgo.v2/sasl/sasl_windows.go @@ -36,7 +36,6 @@ type saslSession struct { // Keep track of pointers we need to explicitly free stringsToFree []*C.char - buffersToFree []C.PVOID } var initError error @@ -93,9 +92,6 @@ func (ss *saslSession) Close() { for _, cstr := range ss.stringsToFree { C.free(unsafe.Pointer(cstr)) } - for _, cbuf := range ss.buffersToFree { - C.free(unsafe.Pointer(cbuf)) - } } func (ss *saslSession) Step(serverData []byte) (clientData []byte, done bool, err error) { @@ -113,11 +109,12 @@ func (ss *saslSession) Step(serverData []byte) (clientData []byte, done bool, er if ss.authComplete { // Step 3: last bit of magic to use the correct server credentials status = C.sspi_send_client_authz_id(&ss.context, &buffer, &bufferLength, ss.cstr(ss.userPlusRealm)) - ss.buffersToFree = append(ss.buffersToFree, buffer) } else { // Step 1 + Step 2: set up security context with the server and TGT status = C.sspi_step(&ss.credHandle, ss.hasContext, &ss.context, &buffer, &bufferLength, ss.cstr(ss.target)) - ss.buffersToFree = append(ss.buffersToFree, buffer) + } + if buffer != C.PVOID(nil) { + defer C.free(unsafe.Pointer(buffer)) } if status != C.SEC_E_OK && status != C.SEC_I_CONTINUE_NEEDED { ss.errored = true diff --git a/vendor/src/gopkg.in/mgo.v2/server.go b/vendor/src/gopkg.in/mgo.v2/server.go index eb89dfd561e..8c130bed9c9 100644 --- a/vendor/src/gopkg.in/mgo.v2/server.go +++ b/vendor/src/gopkg.in/mgo.v2/server.go @@ -67,9 +67,10 @@ func (dial dialer) isSet() bool { } type mongoServerInfo struct { - Master bool - Mongos bool - Tags bson.D + Master bool + Mongos bool + Tags bson.D + MaxWireVersion int } var defaultServerInfo mongoServerInfo diff --git a/vendor/src/gopkg.in/mgo.v2/session.go b/vendor/src/gopkg.in/mgo.v2/session.go index 7e5f31e14d6..de3ad5639f5 100644 --- a/vendor/src/gopkg.in/mgo.v2/session.go +++ b/vendor/src/gopkg.in/mgo.v2/session.go @@ -372,7 +372,8 @@ func DialWithInfo(info *DialInfo) (*Session, error) { } if info.Username != "" { source := session.sourcedb - if info.Source == "" && (info.Mechanism == "GSSAPI" || info.Mechanism == "PLAIN") { + if info.Source == "" && + (info.Mechanism == "GSSAPI" || info.Mechanism == "PLAIN" || info.Mechanism == "MONGODB-X509") { source = "$external" } session.dialCred = &Credential{ @@ -779,8 +780,8 @@ func (db *Database) UpsertUser(user *User) error { if (user.Password != "" || user.PasswordHash != "") && user.UserSource != "" { return fmt.Errorf("user has both Password/PasswordHash and UserSource set") } - if len(user.OtherDBRoles) > 0 && db.Name != "admin" { - return fmt.Errorf("user with OtherDBRoles is only supported in admin database") + if len(user.OtherDBRoles) > 0 && db.Name != "admin" && db.Name != "$external" { + return fmt.Errorf("user with OtherDBRoles is only supported in the admin or $external databases") } // Attempt to run this using 2.6+ commands. @@ -790,7 +791,8 @@ func (db *Database) UpsertUser(user *User) error { rundb = db.Session.DB(user.UserSource) } err := rundb.runUserCmd("updateUser", user) - if isNotFound(err) { + // retry with createUser when isAuthError in order to enable the "localhost exception" + if isNotFound(err) || isAuthError(err) { return rundb.runUserCmd("createUser", user) } if !isNoCmd(err) { @@ -844,6 +846,11 @@ func isNotFound(err error) bool { return ok && e.Code == 11 } +func isAuthError(err error) bool { + e, ok := err.(*QueryError) + return ok && e.Code == 13 +} + func (db *Database) runUserCmd(cmdName string, user *User) error { cmd := make(bson.D, 0, 16) cmd = append(cmd, bson.DocElem{cmdName, user.Username}) @@ -921,14 +928,17 @@ func (db *Database) RemoveUser(user string) error { } type indexSpec struct { - Name, NS string - Key bson.D - Unique bool ",omitempty" - DropDups bool "dropDups,omitempty" - Background bool ",omitempty" - Sparse bool ",omitempty" - Bits, Min, Max int ",omitempty" - ExpireAfter int "expireAfterSeconds,omitempty" + Name, NS string + Key bson.D + Unique bool ",omitempty" + DropDups bool "dropDups,omitempty" + Background bool ",omitempty" + Sparse bool ",omitempty" + Bits, Min, Max int ",omitempty" + ExpireAfter int "expireAfterSeconds,omitempty" + Weights bson.D ",omitempty" + DefaultLanguage string "default_language,omitempty" + LanguageOverride string "language_override,omitempty" } type Index struct { @@ -938,19 +948,35 @@ type Index struct { Background bool // Build index in background and return immediately Sparse bool // Only index documents containing the Key fields - ExpireAfter time.Duration // Periodically delete docs with indexed time.Time older than that. + // If ExpireAfter is defined the server will periodically delete + // documents with indexed time.Time older than the provided delta. + ExpireAfter time.Duration + + // Index name computed by EnsureIndex during creation. + Name string - Name string // Index name, computed by EnsureIndex + // Properties for spatial indexes. + Bits, Min, Max int - Bits, Min, Max int // Properties for spatial indexes + // Properties for text indexes. + DefaultLanguage string + LanguageOverride string } -func parseIndexKey(key []string) (name string, realKey bson.D, err error) { +type indexKeyInfo struct { + name string + key bson.D + weights bson.D +} + +func parseIndexKey(key []string) (*indexKeyInfo, error) { + var keyInfo indexKeyInfo + isText := false var order interface{} for _, field := range key { raw := field - if name != "" { - name += "_" + if keyInfo.name != "" { + keyInfo.name += "_" } var kind string if field != "" { @@ -958,7 +984,7 @@ func parseIndexKey(key []string) (name string, realKey bson.D, err error) { if c := strings.Index(field, ":"); c > 1 && c < len(field)-1 { kind = field[1:c] field = field[c+1:] - name += field + "_" + kind + keyInfo.name += field + "_" + kind } } switch field[0] { @@ -971,32 +997,40 @@ func parseIndexKey(key []string) (name string, realKey bson.D, err error) { // The shell used to render this field as key_ instead of key_2d, // and mgo followed suit. This has been fixed in recent server // releases, and mgo followed as well. - name += field + "_2d" + keyInfo.name += field + "_2d" case '-': order = -1 field = field[1:] - name += field + "_-1" + keyInfo.name += field + "_-1" case '+': field = field[1:] fallthrough default: if kind == "" { order = 1 - name += field + "_1" + keyInfo.name += field + "_1" } else { order = kind } } } if field == "" || kind != "" && order != kind { - return "", nil, fmt.Errorf(`invalid index key: want "[$<kind>:][-]<field name>", got %q`, raw) + return nil, fmt.Errorf(`invalid index key: want "[$<kind>:][-]<field name>", got %q`, raw) + } + if kind == "text" { + if !isText { + keyInfo.key = append(keyInfo.key, bson.DocElem{"_fts", "text"}, bson.DocElem{"_ftsx", 1}) + isText = true + } + keyInfo.weights = append(keyInfo.weights, bson.DocElem{field, 1}) + } else { + keyInfo.key = append(keyInfo.key, bson.DocElem{field, order}) } - realKey = append(realKey, bson.DocElem{field, order}) } - if name == "" { - return "", nil, errors.New("invalid index key: no fields provided") + if keyInfo.name == "" { + return nil, errors.New("invalid index key: no fields provided") } - return + return &keyInfo, nil } // EnsureIndexKey ensures an index with the given key exists, creating it @@ -1083,29 +1117,32 @@ func (c *Collection) EnsureIndexKey(key ...string) error { // http://www.mongodb.org/display/DOCS/Multikeys // func (c *Collection) EnsureIndex(index Index) error { - name, realKey, err := parseIndexKey(index.Key) + keyInfo, err := parseIndexKey(index.Key) if err != nil { return err } session := c.Database.Session - cacheKey := c.FullName + "\x00" + name + cacheKey := c.FullName + "\x00" + keyInfo.name if session.cluster().HasCachedIndex(cacheKey) { return nil } spec := indexSpec{ - Name: name, - NS: c.FullName, - Key: realKey, - Unique: index.Unique, - DropDups: index.DropDups, - Background: index.Background, - Sparse: index.Sparse, - Bits: index.Bits, - Min: index.Min, - Max: index.Max, - ExpireAfter: int(index.ExpireAfter / time.Second), + Name: keyInfo.name, + NS: c.FullName, + Key: keyInfo.key, + Unique: index.Unique, + DropDups: index.DropDups, + Background: index.Background, + Sparse: index.Sparse, + Bits: index.Bits, + Min: index.Min, + Max: index.Max, + ExpireAfter: int(index.ExpireAfter / time.Second), + Weights: keyInfo.weights, + DefaultLanguage: index.DefaultLanguage, + LanguageOverride: index.LanguageOverride, } session = session.Clone() @@ -1134,13 +1171,13 @@ func (c *Collection) EnsureIndex(index Index) error { // // See the EnsureIndex method for more details on indexes. func (c *Collection) DropIndex(key ...string) error { - name, _, err := parseIndexKey(key) + keyInfo, err := parseIndexKey(key) if err != nil { return err } session := c.Database.Session - cacheKey := c.FullName + "\x00" + name + cacheKey := c.FullName + "\x00" + keyInfo.name session.cluster().CacheIndex(cacheKey, false) session = session.Clone() @@ -1152,7 +1189,7 @@ func (c *Collection) DropIndex(key ...string) error { ErrMsg string Ok bool }{} - err = db.Run(bson.D{{"dropIndexes", c.Name}, {"index", name}}, &result) + err = db.Run(bson.D{{"dropIndexes", c.Name}, {"index", keyInfo.name}}, &result) if err != nil { return err } @@ -1179,6 +1216,23 @@ func (c *Collection) DropIndex(key ...string) error { // // See the EnsureIndex method for more details on indexes. func (c *Collection) Indexes() (indexes []Index, err error) { + // Try with a command. + var cmdResult struct { + Indexes []indexSpec + } + err = c.Database.Run(bson.D{{"listIndexes", c.Name}}, &cmdResult) + if err == nil { + for _, spec := range cmdResult.Indexes { + indexes = append(indexes, indexFromSpec(spec)) + } + sort.Sort(indexSlice(indexes)) + return indexes, nil + } + if err != nil && !isNoCmd(err) { + return nil, err + } + + // Command not yet supported. Query the database instead. query := c.Database.C("system.indexes").Find(bson.M{"ns": c.FullName}) iter := query.Sort("name").Iter() for { @@ -1186,21 +1240,31 @@ func (c *Collection) Indexes() (indexes []Index, err error) { if !iter.Next(&spec) { break } - index := Index{ - Name: spec.Name, - Key: simpleIndexKey(spec.Key), - Unique: spec.Unique, - DropDups: spec.DropDups, - Background: spec.Background, - Sparse: spec.Sparse, - ExpireAfter: time.Duration(spec.ExpireAfter) * time.Second, - } - indexes = append(indexes, index) + indexes = append(indexes, indexFromSpec(spec)) } err = iter.Close() - return + sort.Sort(indexSlice(indexes)) + return indexes, nil +} + +func indexFromSpec(spec indexSpec) Index { + return Index{ + Name: spec.Name, + Key: simpleIndexKey(spec.Key), + Unique: spec.Unique, + DropDups: spec.DropDups, + Background: spec.Background, + Sparse: spec.Sparse, + ExpireAfter: time.Duration(spec.ExpireAfter) * time.Second, + } } +type indexSlice []Index + +func (idxs indexSlice) Len() int { return len(idxs) } +func (idxs indexSlice) Less(i, j int) bool { return idxs[i].Name < idxs[j].Name } +func (idxs indexSlice) Swap(i, j int) { idxs[i], idxs[j] = idxs[j], idxs[i] } + func simpleIndexKey(realKey bson.D) (key []string) { for i := range realKey { field := realKey[i].Name @@ -1765,6 +1829,20 @@ type Pipe struct { session *Session collection *Collection pipeline interface{} + allowDisk bool + batchSize int +} + +type pipeCmd struct { + Aggregate string + Pipeline interface{} + Cursor *pipeCmdCursor ",omitempty" + Explain bool ",omitempty" + AllowDisk bool "allowDiskUse,omitempty" +} + +type pipeCmdCursor struct { + BatchSize int `bson:"batchSize,omitempty"` } // Pipe prepares a pipeline to aggregate. The pipeline document @@ -1783,29 +1861,80 @@ type Pipe struct { // func (c *Collection) Pipe(pipeline interface{}) *Pipe { session := c.Database.Session + session.m.Lock() + batchSize := int(session.queryConfig.op.limit) + session.m.Unlock() return &Pipe{ session: session, collection: c, pipeline: pipeline, + batchSize: batchSize, } } // Iter executes the pipeline and returns an iterator capable of going // over all the generated results. func (p *Pipe) Iter() *Iter { + + // Clone session and set it to strong mode so that the server + // used for the query may be safely obtained afterwards, if + // necessary for iteration when a cursor is received. + cloned := p.session.Clone() + cloned.SetMode(Strong, false) + defer cloned.Close() + c := p.collection.With(cloned) + iter := &Iter{ session: p.session, timeout: -1, } iter.gotReply.L = &iter.m - var result struct{ Result []bson.Raw } - c := p.collection - iter.err = c.Database.Run(bson.D{{"aggregate", c.Name}, {"pipeline", p.pipeline}}, &result) + + var result struct { + // 2.4, no cursors. + Result []bson.Raw + + // 2.6+, with cursors. + Cursor struct { + FirstBatch []bson.Raw "firstBatch" + Id int64 + } + } + + cmd := pipeCmd{ + Aggregate: c.Name, + Pipeline: p.pipeline, + AllowDisk: p.allowDisk, + Cursor: &pipeCmdCursor{p.batchSize}, + } + iter.err = c.Database.Run(cmd, &result) + if e, ok := iter.err.(*QueryError); ok && e.Message == `unrecognized field "cursor` { + cmd.Cursor = nil + cmd.AllowDisk = false + iter.err = c.Database.Run(cmd, &result) + } if iter.err != nil { return iter } - for i := range result.Result { - iter.docData.Push(result.Result[i].Data) + docs := result.Result + if docs == nil { + docs = result.Cursor.FirstBatch + } + for i := range docs { + iter.docData.Push(docs[i].Data) + } + if result.Cursor.Id != 0 { + socket, err := cloned.acquireSocket(true) + if err != nil { + // Cloned session is in strong mode, and the query + // above succeeded. Should have a reserved socket. + panic("internal error: " + err.Error()) + } + iter.server = socket.Server() + socket.Release() + iter.op.cursorId = result.Cursor.Id + iter.op.collection = c.FullName + iter.op.replyFunc = iter.replyFunc() } return iter } @@ -1829,6 +1958,47 @@ func (p *Pipe) One(result interface{}) error { return ErrNotFound } +// Explain returns a number of details about how the MongoDB server would +// execute the requested pipeline, such as the number of objects examined, +// the number of times the read lock was yielded to allow writes to go in, +// and so on. +// +// For example: +// +// var m bson.M +// err := collection.Pipe(pipeline).Explain(&m) +// if err == nil { +// fmt.Printf("Explain: %#v\n", m) +// } +// +func (p *Pipe) Explain(result interface{}) error { + c := p.collection + cmd := pipeCmd{ + Aggregate: c.Name, + Pipeline: p.pipeline, + AllowDisk: p.allowDisk, + Explain: true, + } + return c.Database.Run(cmd, result) +} + +// AllowDiskUse enables writing to the "<dbpath>/_tmp" server directory so +// that aggregation pipelines do not have to be held entirely in memory. +func (p *Pipe) AllowDiskUse() *Pipe { + p.allowDisk = true + return p +} + +// Batch sets the batch size used when fetching documents from the database. +// It's possible to change this setting on a per-session basis as well, using +// the Batch method of Session. +// +// The default batch size is defined by the database server. +func (p *Pipe) Batch(n int) *Pipe { + p.batchSize = n + return p +} + type LastError struct { Err string Code, N, Waited int @@ -2182,18 +2352,25 @@ func (q *Query) Select(selector interface{}) *Query { // query1 := collection.Find(nil).Sort("firstname", "lastname") // query2 := collection.Find(nil).Sort("-age") // query3 := collection.Find(nil).Sort("$natural") +// query4 := collection.Find(nil).Select(bson.M{"score": bson.M{"$meta": "textScore"}}).Sort("$textScore:score") // // Relevant documentation: // // http://www.mongodb.org/display/DOCS/Sorting+and+Natural+Order // func (q *Query) Sort(fields ...string) *Query { - // TODO // query4 := collection.Find(nil).Sort("score:{$meta:textScore}") q.m.Lock() var order bson.D for _, field := range fields { n := 1 + var kind string if field != "" { + if field[0] == '$' { + if c := strings.Index(field, ":"); c > 1 && c < len(field)-1 { + kind = field[1:c] + field = field[c+1:] + } + } switch field[0] { case '+': field = field[1:] @@ -2205,7 +2382,11 @@ func (q *Query) Sort(fields ...string) *Query { if field == "" { panic("Sort: empty field name") } - order = append(order, bson.DocElem{field, n}) + if kind == "textScore" { + order = append(order, bson.DocElem{field, bson.M{"$meta": kind}}) + } else { + order = append(order, bson.DocElem{field, n}) + } } q.op.options.OrderBy = order q.op.hasOptions = true @@ -2215,7 +2396,7 @@ func (q *Query) Sort(fields ...string) *Query { // 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 time the read lock was yielded to allow writes to go in, +// the number of times the read lock was yielded to allow writes to go in, // and so on. // // For example: @@ -2265,8 +2446,8 @@ func (q *Query) Explain(result interface{}) error { // func (q *Query) Hint(indexKey ...string) *Query { q.m.Lock() - _, realKey, err := parseIndexKey(indexKey) - q.op.options.Hint = realKey + keyInfo, err := parseIndexKey(indexKey) + q.op.options.Hint = keyInfo.key q.op.hasOptions = true q.m.Unlock() if err != nil { @@ -2472,14 +2653,33 @@ func (s *Session) FindRef(ref *DBRef) *Query { return c.FindId(ref.Id) } -// CollectionNames returns the collection names present in database. +// CollectionNames returns the collection names present in the db database. func (db *Database) CollectionNames() (names []string, err error) { - c := len(db.Name) + 1 + // Try with a command. + var cmdResult struct { + Collections []struct { + Name string + } + } + err = db.Run(bson.D{{"listCollections", 1}}, &cmdResult) + if err == nil { + for _, coll := range cmdResult.Collections { + names = append(names, coll.Name) + } + sort.Strings(names) + return names, err + } + if err != nil && !isNoCmd(err) { + return nil, err + } + + // Command not yet supported. Query the database instead. + nameIndex := len(db.Name) + 1 iter := db.C("system.namespaces").Find(nil).Iter() var result *struct{ Name string } for iter.Next(&result) { if strings.Index(result.Name, "$") < 0 || strings.Index(result.Name, ".oplog.$") >= 0 { - names = append(names, result.Name[c:]) + names = append(names, result.Name[nameIndex:]) } } if err := iter.Close(); err != nil { @@ -3353,13 +3553,14 @@ func (q *Query) Apply(change Change, result interface{}) (info *ChangeInfo, err // internally assembled from the Version information for previous versions. // In both cases, VersionArray is guaranteed to have at least 4 entries. type BuildInfo struct { - Version string - VersionArray []int `bson:"versionArray"` // On MongoDB 2.0+; assembled from Version otherwise - GitVersion string `bson:"gitVersion"` - SysInfo string `bson:"sysInfo"` - Bits int - Debug bool - MaxObjectSize int `bson:"maxBsonObjectSize"` + Version string + VersionArray []int `bson:"versionArray"` // On MongoDB 2.0+; assembled from Version otherwise + GitVersion string `bson:"gitVersion"` + OpenSSLVersion string `bson:"OpenSSLVersion"` + SysInfo string `bson:"sysInfo"` + Bits int + Debug bool + MaxObjectSize int `bson:"maxBsonObjectSize"` } // VersionAtLeast returns whether the BuildInfo version is greater than or @@ -3393,6 +3594,11 @@ func (s *Session) BuildInfo() (info BuildInfo, err error) { for len(info.VersionArray) < 4 { info.VersionArray = append(info.VersionArray, 0) } + if i := strings.IndexByte(info.GitVersion, ' '); i >= 0 { + // Strip off the " modules: enterprise" suffix. This is a _git version_. + // That information may be moved to another field if people need it. + info.GitVersion = info.GitVersion[:i] + } return } diff --git a/vendor/src/gopkg.in/mgo.v2/session_test.go b/vendor/src/gopkg.in/mgo.v2/session_test.go index 00eaacda11a..a80714e2b25 100644 --- a/vendor/src/gopkg.in/mgo.v2/session_test.go +++ b/vendor/src/gopkg.in/mgo.v2/session_test.go @@ -2092,6 +2092,58 @@ func (s *S) TestSortWithBadArgs(c *C) { } } +func (s *S) TestSortScoreText(c *C) { + session, err := mgo.Dial("localhost:40001") + c.Assert(err, IsNil) + defer session.Close() + + coll := session.DB("mydb").C("mycoll") + + err = coll.EnsureIndex(mgo.Index{ + Key: []string{"$text:a", "$text:b"}, + }) + c.Assert(err, IsNil) + + err = coll.Insert(M{ + "a": "none", + "b": "twice: foo foo", + }) + c.Assert(err, IsNil) + err = coll.Insert(M{ + "a": "just once: foo", + "b": "none", + }) + c.Assert(err, IsNil) + err = coll.Insert(M{ + "a": "many: foo foo foo", + "b": "none", + }) + c.Assert(err, IsNil) + err = coll.Insert(M{ + "a": "none", + "b": "none", + "c": "ignore: foo", + }) + c.Assert(err, IsNil) + + query := coll.Find(M{"$text": M{"$search": "foo"}}) + query.Select(M{"score": M{"$meta": "textScore"}}) + query.Sort("$textScore:score") + iter := query.Iter() + + var r struct{ A, B string } + var results []string + for iter.Next(&r) { + results = append(results, r.A, r.B) + } + + c.Assert(results, DeepEquals, []string{ + "many: foo foo foo", "none", + "none", "twice: foo foo", + "just once: foo", "none", + }) +} + func (s *S) TestPrefetching(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) @@ -2399,13 +2451,28 @@ func (s *S) TestEnsureIndex(c *C) { Bits: 32, } - coll := session.DB("mydb").C("mycoll") + index5 := mgo.Index{ + Key: []string{"$text:a", "$text:b"}, + } - for _, index := range []mgo.Index{index1, index2, index3, index4} { - err = coll.EnsureIndex(index) + index6 := mgo.Index{ + Key: []string{"$text:a"}, + DefaultLanguage: "portuguese", + LanguageOverride: "idioma", + } + + coll1 := session.DB("mydb").C("mycoll1") + coll2 := session.DB("mydb").C("mycoll2") + + for _, index := range []mgo.Index{index1, index2, index3, index4, index5} { + err = coll1.EnsureIndex(index) c.Assert(err, IsNil) } + // Cannot have multiple text indexes on the same collection. + err = coll2.EnsureIndex(index6) + c.Assert(err, IsNil) + sysidx := session.DB("mydb").C("system.indexes") result1 := M{} @@ -2424,11 +2491,19 @@ func (s *S) TestEnsureIndex(c *C) { err = sysidx.Find(M{"name": "loc_2d"}).One(result4) c.Assert(err, IsNil) + result5 := M{} + err = sysidx.Find(M{"name": "a_text_b_text"}).One(result5) + c.Assert(err, IsNil) + + result6 := M{} + err = sysidx.Find(M{"name": "a_text"}).One(result6) + c.Assert(err, IsNil) + delete(result1, "v") expected1 := M{ "name": "a_1", "key": M{"a": 1}, - "ns": "mydb.mycoll", + "ns": "mydb.mycoll1", "background": true, } c.Assert(result1, DeepEquals, expected1) @@ -2437,17 +2512,21 @@ func (s *S) TestEnsureIndex(c *C) { expected2 := M{ "name": "a_1_b_-1", "key": M{"a": 1, "b": -1}, - "ns": "mydb.mycoll", + "ns": "mydb.mycoll1", "unique": true, "dropDups": true, } + if s.versionAtLeast(2, 7) { + // Was deprecated in 2.6, and not being reported by 2.7+. + delete(expected2, "dropDups") + } c.Assert(result2, DeepEquals, expected2) delete(result3, "v") expected3 := M{ "name": "loc_old_2d", "key": M{"loc_old": "2d"}, - "ns": "mydb.mycoll", + "ns": "mydb.mycoll1", "min": -500, "max": 500, "bits": 32, @@ -2458,17 +2537,41 @@ func (s *S) TestEnsureIndex(c *C) { expected4 := M{ "name": "loc_2d", "key": M{"loc": "2d"}, - "ns": "mydb.mycoll", + "ns": "mydb.mycoll1", "min": -500, "max": 500, "bits": 32, } c.Assert(result4, DeepEquals, expected4) + delete(result5, "v") + expected5 := M{ + "name": "a_text_b_text", + "key": M{"_fts": "text", "_ftsx": 1}, + "ns": "mydb.mycoll1", + "weights": M{"a": 1, "b": 1}, + "default_language": "english", + "language_override": "language", + "textIndexVersion": 2, + } + c.Assert(result5, DeepEquals, expected5) + + delete(result6, "v") + expected6 := M{ + "name": "a_text", + "key": M{"_fts": "text", "_ftsx": 1}, + "ns": "mydb.mycoll2", + "weights": M{"a": 1}, + "default_language": "portuguese", + "language_override": "idioma", + "textIndexVersion": 2, + } + c.Assert(result6, DeepEquals, expected6) + // Ensure the index actually works for real. - err = coll.Insert(M{"a": 1, "b": 1}) + err = coll1.Insert(M{"a": 1, "b": 1}) c.Assert(err, IsNil) - err = coll.Insert(M{"a": 1, "b": 1}) + err = coll1.Insert(M{"a": 1, "b": 1}) c.Assert(err, ErrorMatches, ".*duplicate key error.*") c.Assert(mgo.IsDup(err), Equals, true) } @@ -3105,7 +3208,15 @@ func (s *S) TestPipeIter(c *C) { coll.Insert(M{"n": n}) } - iter := coll.Pipe([]M{{"$match": M{"n": M{"$gte": 42}}}}).Iter() + pipe := coll.Pipe([]M{{"$match": M{"n": M{"$gte": 42}}}}) + + // Ensure cursor logic is working by forcing a small batch. + pipe.Batch(2) + + // Smoke test for AllowDiskUse. + pipe.AllowDiskUse() + + iter := pipe.Iter() result := struct{ N int }{} for i := 2; i < 7; i++ { ok := iter.Next(&result) @@ -3167,6 +3278,27 @@ func (s *S) TestPipeOne(c *C) { c.Assert(err, Equals, mgo.ErrNotFound) } +func (s *S) TestPipeExplain(c *C) { + if !s.versionAtLeast(2, 1) { + c.Skip("Pipe only works on 2.1+") + } + + session, err := mgo.Dial("localhost:40001") + c.Assert(err, IsNil) + defer session.Close() + + coll := session.DB("mydb").C("mycoll") + coll.Insert(M{"a": 1, "b": 2}) + + pipe := coll.Pipe([]M{{"$project": M{"a": 1, "b": M{"$add": []interface{}{"$b", 1}}}}}) + + // The explain command result changes across versions. + var result struct{ Ok int } + err = pipe.Explain(&result) + c.Assert(err, IsNil) + c.Assert(result.Ok, Equals, 1) +} + func (s *S) TestBatch1Bug(c *C) { session, err := mgo.Dial("localhost:40001") c.Assert(err, IsNil) diff --git a/vendor/src/gopkg.in/mgo.v2/testdb/dropall.js b/vendor/src/gopkg.in/mgo.v2/testdb/dropall.js index ca12892633e..232eca3c302 100644 --- a/vendor/src/gopkg.in/mgo.v2/testdb/dropall.js +++ b/vendor/src/gopkg.in/mgo.v2/testdb/dropall.js @@ -1,13 +1,19 @@ var ports = [40001, 40002, 40011, 40012, 40013, 40021, 40022, 40023, 40041, 40101, 40102, 40103, 40201, 40202, 40203] var auth = [40002, 40103, 40203, 40031] +var db1 = new Mongo("localhost:40001") + +if (db1.getDB("admin").serverBuildInfo().OpenSSLVersion != "") { + ports.push(40003) + auth.push(40003) +} for (var i in ports) { var port = ports[i] var server = "localhost:" + port var mongo = new Mongo("localhost:" + port) var admin = mongo.getDB("admin") - + for (var j in auth) { if (auth[j] == port) { admin.auth("root", "rapadura") @@ -27,7 +33,6 @@ for (var i in ports) { var result = admin.runCommand({"listDatabases": 1}) // Why is the command returning undefined!? while (typeof result.databases == "undefined") { - print("dropall.js: listing databases of :" + port + " got:", result) result = admin.runCommand({"listDatabases": 1}) } var dbs = result.databases diff --git a/vendor/src/gopkg.in/mgo.v2/testdb/init.js b/vendor/src/gopkg.in/mgo.v2/testdb/init.js index 02a6c61c84e..7deb67e1ce4 100644 --- a/vendor/src/gopkg.in/mgo.v2/testdb/init.js +++ b/vendor/src/gopkg.in/mgo.v2/testdb/init.js @@ -32,6 +32,10 @@ for (var i = 0; i != 60; i++) { sleep(1000) } +function hasSSL() { + return db1.serverBuildInfo().OpenSSLVersion != "" +} + rs1a.runCommand({replSetInitiate: rs1cfg}) rs2a.runCommand({replSetInitiate: rs2cfg}) rs3a.runCommand({replSetInitiate: rs3cfg}) @@ -50,6 +54,9 @@ function configShards() { function configAuth() { var addrs = ["127.0.0.1:40002", "127.0.0.1:40203", "127.0.0.1:40031"] + if (hasSSL()) { + addrs.push("127.0.0.1:40003") + } for (var i in addrs) { var db = new Mongo(addrs[i]).getDB("admin") var v = db.serverBuildInfo().versionArray diff --git a/vendor/src/gopkg.in/mgo.v2/testdb/setup.sh b/vendor/src/gopkg.in/mgo.v2/testdb/setup.sh index 27200c09030..317e8e5ab39 100755 --- a/vendor/src/gopkg.in/mgo.v2/testdb/setup.sh +++ b/vendor/src/gopkg.in/mgo.v2/testdb/setup.sh @@ -3,13 +3,17 @@ start() { mkdir _testdb cd _testdb - mkdir db1 db2 rs1a rs1b rs1c rs2a rs2b rs2c rs3a rs3b rs3c rs4a cfg1 cfg2 cfg3 - ln -s ../testdb/supervisord.conf supervisord.conf + mkdir db1 db2 db3 rs1a rs1b rs1c rs2a rs2b rs2c rs3a rs3b rs3c rs4a cfg1 cfg2 cfg3 + cp ../testdb/supervisord.conf supervisord.conf + cp ../testdb/server.pem server.pem echo keyfile > keyfile chmod 600 keyfile + COUNT=$(grep '^\[program' supervisord.conf | wc -l | tr -d ' ') + if ! mongod --help | grep -q -- --ssl; then + COUNT=$(($COUNT - 1)) + fi echo "Running supervisord..." supervisord || ( echo "Supervisord failed executing ($?)" && exit 1 ) - COUNT=$(grep '^\[program' supervisord.conf | wc -l | tr -d ' ') echo "Supervisord is up, starting $COUNT processes..." for i in $(seq 10); do RUNNING=$(supervisorctl status | grep RUNNING | wc -l | tr -d ' ') @@ -42,11 +46,11 @@ fi case "$1" in start) - start + start $2 ;; stop) - stop + stop $2 ;; esac diff --git a/vendor/src/gopkg.in/mgo.v2/testdb/supervisord.conf b/vendor/src/gopkg.in/mgo.v2/testdb/supervisord.conf index b0aca01a93e..1c2b859a28b 100644 --- a/vendor/src/gopkg.in/mgo.v2/testdb/supervisord.conf +++ b/vendor/src/gopkg.in/mgo.v2/testdb/supervisord.conf @@ -19,6 +19,9 @@ command = mongod --nohttpinterface --noprealloc --nojournal --smallfiles --nssiz [program:db2] command = mongod --nohttpinterface --noprealloc --nojournal --smallfiles --nssize=1 --oplogSize=1 --shardsvr --dbpath %(here)s/db2 --bind_ip=127.0.0.1 --port 40002 --auth +[program:db3] +command = mongod -nohttpinterface --noprealloc --nojournal --smallfiles --nssize=1 --oplogSize=1 --dbpath %(here)s/db3 --bind_ip=127.0.0.1 --port 40003 --auth --sslMode preferSSL --sslCAFile %(here)s/server.pem --sslPEMKeyFile %(here)s/server.pem + [program:rs1a] command = mongod --nohttpinterface --noprealloc --nojournal --smallfiles --nssize=1 --oplogSize=1 --shardsvr --replSet rs1 --dbpath %(here)s/rs1a --bind_ip=127.0.0.1 --port 40011 [program:rs1b] diff --git a/vendor/src/gopkg.in/mgo.v2/txn/flusher.go b/vendor/src/gopkg.in/mgo.v2/txn/flusher.go index 5318ad0c903..25b2f031950 100644 --- a/vendor/src/gopkg.in/mgo.v2/txn/flusher.go +++ b/vendor/src/gopkg.in/mgo.v2/txn/flusher.go @@ -2,7 +2,6 @@ package txn import ( "fmt" - "sort" "gopkg.in/mgo.v2" "gopkg.in/mgo.v2/bson" @@ -225,10 +224,9 @@ func (f *flusher) prepare(t *transaction, force bool) (revnos []int64, err error } f.debugf("Preparing %s", t) - // Iterate in a stable way across all runners. This isn't - // strictly required, but reduces the chances of cycles. + // dkeys being sorted means stable iteration across all runners. This + // isn't strictly required, but reduces the chances of cycles. dkeys := t.docKeys() - sort.Sort(dkeys) revno := make(map[docKey]int64) info := txnInfo{} @@ -380,10 +378,10 @@ func (f *flusher) rescan(t *transaction, force bool) (revnos []int64, err error) panic(fmt.Errorf("rescanning transaction in invalid state: %q", t.State)) } - // Iterate in a stable way across all runners. This isn't - // strictly required, but reduces the chances of cycles. + // dkeys being sorted means stable iteration across all + // runners. This isn't strictly required, but reduces the chances + // of cycles. dkeys := t.docKeys() - sort.Sort(dkeys) tt := t.token() if !force { diff --git a/vendor/src/gopkg.in/mgo.v2/txn/txn.go b/vendor/src/gopkg.in/mgo.v2/txn/txn.go index a235f90322a..5809e2d3a88 100644 --- a/vendor/src/gopkg.in/mgo.v2/txn/txn.go +++ b/vendor/src/gopkg.in/mgo.v2/txn/txn.go @@ -9,12 +9,14 @@ package txn import ( "encoding/binary" "fmt" - "gopkg.in/mgo.v2" - "gopkg.in/mgo.v2/bson" "reflect" "sort" + "strings" "sync" + "gopkg.in/mgo.v2" + "gopkg.in/mgo.v2/bson" + crand "crypto/rand" mrand "math/rand" ) @@ -455,38 +457,6 @@ func (r *Runner) load(id bson.ObjectId) (*transaction, error) { return &t, nil } -type docKey struct { - C string - Id interface{} -} - -type docKeys []docKey - -func (ks docKeys) Len() int { return len(ks) } -func (ks docKeys) Swap(i, j int) { ks[i], ks[j] = ks[j], ks[i] } -func (ks docKeys) Less(i, j int) bool { - a, b := ks[i], ks[j] - if a.C != b.C { - return a.C < b.C - } - av, an := valueNature(a.Id) - bv, bn := valueNature(b.Id) - if an != bn { - return an < bn - } - switch an { - case natureString: - return av.(string) < bv.(string) - case natureInt: - return av.(int64) < bv.(int64) - case natureFloat: - return av.(float64) < bv.(float64) - case natureBool: - return !av.(bool) && bv.(bool) - } - panic("unreachable") -} - type typeNature int const ( @@ -498,6 +468,7 @@ const ( natureInt natureFloat natureBool + natureStruct ) func valueNature(v interface{}) (value interface{}, nature typeNature) { @@ -513,6 +484,126 @@ func valueNature(v interface{}) (value interface{}, nature typeNature) { return rv.Float(), natureFloat case reflect.Bool: return rv.Bool(), natureBool + case reflect.Struct: + return v, natureStruct } panic("document id type unsupported by txn: " + rv.Kind().String()) } + +type docKey struct { + C string + Id interface{} +} + +type docKeys []docKey + +func (ks docKeys) Len() int { return len(ks) } +func (ks docKeys) Swap(i, j int) { ks[i], ks[j] = ks[j], ks[i] } +func (ks docKeys) Less(i, j int) bool { + a, b := ks[i], ks[j] + if a.C != b.C { + return a.C < b.C + } + return valuecmp(a.Id, b.Id) == -1 +} + +func valuecmp(a, b interface{}) int { + av, an := valueNature(a) + bv, bn := valueNature(b) + if an < bn { + return -1 + } + if an > bn { + return 1 + } + + if av == bv { + return 0 + } + var less bool + switch an { + case natureString: + less = av.(string) < bv.(string) + case natureInt: + less = av.(int64) < bv.(int64) + case natureFloat: + less = av.(float64) < bv.(float64) + case natureBool: + less = !av.(bool) && bv.(bool) + case natureStruct: + less = structcmp(av, bv) == -1 + default: + panic("unreachable") + } + if less { + return -1 + } + return 1 +} + +func structcmp(a, b interface{}) int { + av := reflect.ValueOf(a) + bv := reflect.ValueOf(b) + + var ai, bi = 0, 0 + var an, bn = av.NumField(), bv.NumField() + var avi, bvi interface{} + var af, bf reflect.StructField + for { + for ai < an { + af = av.Type().Field(ai) + if isExported(af.Name) { + avi = av.Field(ai).Interface() + ai++ + break + } + ai++ + } + for bi < bn { + bf = bv.Type().Field(bi) + if isExported(bf.Name) { + bvi = bv.Field(bi).Interface() + bi++ + break + } + bi++ + } + if n := valuecmp(avi, bvi); n != 0 { + return n + } + nameA := getFieldName(af) + nameB := getFieldName(bf) + if nameA < nameB { + return -1 + } + if nameA > nameB { + return 1 + } + if ai == an && bi == bn { + return 0 + } + if ai == an || bi == bn { + if ai == bn { + return -1 + } + return 1 + } + } + panic("unreachable") +} + +func isExported(name string) bool { + a := name[0] + return a >= 'A' && a <= 'Z' +} + +func getFieldName(f reflect.StructField) string { + name := f.Tag.Get("bson") + if i := strings.Index(name, ","); i >= 0 { + name = name[:i] + } + if name == "" { + name = strings.ToLower(f.Name) + } + return name +} diff --git a/vendor/src/gopkg.in/mgo.v2/txn/txn_test.go b/vendor/src/gopkg.in/mgo.v2/txn/txn_test.go index 119bf21d604..1e396eadcbe 100644 --- a/vendor/src/gopkg.in/mgo.v2/txn/txn_test.go +++ b/vendor/src/gopkg.in/mgo.v2/txn/txn_test.go @@ -107,6 +107,31 @@ func (s *S) TestInsert(c *C) { c.Assert(account.Balance, Equals, 200) } +func (s *S) TestInsertStructID(c *C) { + type id struct { + FirstName string + LastName string + } + ops := []txn.Op{{ + C: "accounts", + Id: id{FirstName: "John", LastName: "Jones"}, + Assert: txn.DocMissing, + Insert: M{"balance": 200}, + }, { + C: "accounts", + Id: id{FirstName: "Sally", LastName: "Smith"}, + Assert: txn.DocMissing, + Insert: M{"balance": 800}, + }} + + err := s.runner.Run(ops, "", nil) + c.Assert(err, IsNil) + + n, err := s.accounts.Find(nil).Count() + c.Assert(err, IsNil) + c.Assert(n, Equals, 2) +} + func (s *S) TestRemove(c *C) { err := s.accounts.Insert(M{"_id": 0, "balance": 300}) c.Assert(err, IsNil) @@ -578,8 +603,6 @@ func (s *S) TestTxnQueueStressTest(c *C) { const runners = 4 const changes = 1000 - txn.SetDebug(true) - var wg sync.WaitGroup wg.Add(runners) for n := 0; n < runners; n++ { |