summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormike o'brien <mpobrien005@gmail.com>2014-10-31 17:44:18 -0400
committermike o'brien <mpobrien005@gmail.com>2014-10-31 17:44:18 -0400
commitcbe7045c2f95bd79c76f8249252333b53cf2027c (patch)
tree320fee4f253ba213ba2a18b2d3b7dcc7b7f45d11
parentbadedb9cf4f931245a43ba90753f96e7f6990336 (diff)
downloadmongo-cbe7045c2f95bd79c76f8249252333b53cf2027c.tar.gz
TOOLS-328 switch back to mgo mainline
Former-commit-id: e7a604ead7164e42ada231105cd53c1e3ba535e6
-rw-r--r--Godeps2
-rw-r--r--vendor/src/github.com/spacemonkeygo/spacelog/setup.go5
-rw-r--r--vendor/src/gopkg.in/mgo.v2/auth.go30
-rw-r--r--vendor/src/gopkg.in/mgo.v2/auth_test.go115
-rw-r--r--vendor/src/gopkg.in/mgo.v2/bson/bson.go7
-rw-r--r--vendor/src/gopkg.in/mgo.v2/bson/bson_test.go39
-rw-r--r--vendor/src/gopkg.in/mgo.v2/bson/decode.go56
-rw-r--r--vendor/src/gopkg.in/mgo.v2/cluster.go22
-rw-r--r--vendor/src/gopkg.in/mgo.v2/gridfs.go15
-rw-r--r--vendor/src/gopkg.in/mgo.v2/gridfs_test.go28
-rw-r--r--vendor/src/gopkg.in/mgo.v2/sasl/sasl_windows.go9
-rw-r--r--vendor/src/gopkg.in/mgo.v2/server.go7
-rw-r--r--vendor/src/gopkg.in/mgo.v2/session.go354
-rw-r--r--vendor/src/gopkg.in/mgo.v2/session_test.go152
-rw-r--r--vendor/src/gopkg.in/mgo.v2/testdb/dropall.js9
-rw-r--r--vendor/src/gopkg.in/mgo.v2/testdb/init.js7
-rwxr-xr-xvendor/src/gopkg.in/mgo.v2/testdb/setup.sh14
-rw-r--r--vendor/src/gopkg.in/mgo.v2/testdb/supervisord.conf3
-rw-r--r--vendor/src/gopkg.in/mgo.v2/txn/flusher.go12
-rw-r--r--vendor/src/gopkg.in/mgo.v2/txn/txn.go159
-rw-r--r--vendor/src/gopkg.in/mgo.v2/txn/txn_test.go27
21 files changed, 873 insertions, 199 deletions
diff --git a/Godeps b/Godeps
index 4aa87399522..8cac493af50 100644
--- a/Godeps
+++ b/Godeps
@@ -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++ {