├── internal ├── llmgo │ ├── raceoff.go │ ├── raceon.go │ ├── Makefile │ ├── README.md │ ├── dbtest │ │ ├── export_test.go │ │ └── dbserver_test.go │ ├── testserver │ │ ├── export_test.go │ │ └── testserver_test.go │ ├── saslstub.go │ ├── syscall_windows_test.go │ ├── syscall_test.go │ ├── saslimpl.go │ ├── internal │ │ ├── sasl │ │ │ ├── sasl_windows.h │ │ │ ├── sasl.c │ │ │ └── sspi_windows.h │ │ └── scram │ │ │ └── scram_test.go │ ├── bson │ │ └── specdata │ │ │ └── update.sh │ ├── export_test.go │ ├── txn │ │ ├── tarjan_test.go │ │ ├── chaos.go │ │ ├── mgo_test.go │ │ ├── tarjan.go │ │ └── debug.go │ ├── doc.go │ ├── testdb │ │ ├── setup.sh │ │ ├── dropall.js │ │ ├── wait.js │ │ ├── server.pem │ │ └── client.pem │ ├── queue_test.go │ ├── queue.go │ ├── THIRD-PARTY-NOTICES │ ├── stats.go │ └── log.go └── legacy │ ├── json │ ├── testdata │ │ └── code.json.gz │ ├── consts.go │ ├── nan.go │ ├── hex.go │ ├── infinity.go │ ├── undefined.go │ ├── maxkey.go │ ├── minkey.go │ ├── tags_test.go │ ├── unquoted.go │ ├── tags.go │ ├── iso_date.go │ ├── objectid.go │ ├── boolean.go │ ├── bindata.go │ ├── csv_format.go │ ├── timestamp.go │ ├── dbref.go │ ├── dbpointer.go │ ├── date.go │ ├── objectid_test.go │ ├── nan_test.go │ ├── helpers.go │ ├── single_quoted.go │ ├── undefined_test.go │ ├── timestamp_test.go │ ├── new.go │ ├── bindata_test.go │ ├── float_test.go │ ├── dbpointer_test.go │ ├── infinity_test.go │ ├── date_test.go │ ├── frac_test.go │ └── tagkey_test.go │ ├── util │ ├── util.go │ ├── exit_code.go │ ├── strings.go │ ├── net.go │ ├── format_date.go │ ├── bool.go │ ├── format_date_test.go │ ├── slice.go │ ├── file.go │ ├── bool_test.go │ ├── math.go │ └── math_test.go │ ├── failpoint │ ├── failpoints.go │ ├── failpoint_disabled.go │ ├── failpoint.go │ └── failpoint_test.go │ ├── options │ ├── options_fp.go │ ├── options_fp_disabled.go │ ├── options_openssl.go │ ├── options_gssapi.go │ └── options_ssl.go │ ├── lldb │ ├── tlsgo │ │ ├── rootcerts.go │ │ └── rootcerts_darwin.go │ ├── db_tlsgo.go │ ├── db_openssl.go │ ├── kerberos │ │ └── gssapi.go │ ├── openssl │ │ └── openssl_fips.go │ └── connector.go │ ├── password │ ├── pass_util.go │ └── password.go │ └── testtype │ └── types.go ├── mongoreplay ├── compressed.pcap ├── mongoreplay_test.out ├── pcap_test_run.playback ├── testPcap │ ├── make_WorkloadWithEOF.js │ ├── make_SingleChannelNoDeadlock.js │ └── make_MultiCursorWait.js ├── version.go ├── mongoreplay.go ├── unknown_op.go ├── execute_test.go ├── recorded_op.go ├── log_wrapper.go ├── killcursors_op.go ├── opcode.go ├── message.go ├── delete_op.go ├── insert_op.go ├── connection_stub.go └── play_test.go ├── go.mod ├── runTests.sh ├── sanity_check.sh ├── set_goenv.sh ├── main └── mongoreplay.go └── README.md /internal/llmgo/raceoff.go: -------------------------------------------------------------------------------- 1 | // +build !race 2 | 3 | package mgo 4 | 5 | const raceDetector = false 6 | -------------------------------------------------------------------------------- /internal/llmgo/raceon.go: -------------------------------------------------------------------------------- 1 | // +build race 2 | 3 | package mgo 4 | 5 | const raceDetector = true 6 | -------------------------------------------------------------------------------- /internal/llmgo/Makefile: -------------------------------------------------------------------------------- 1 | startdb: 2 | @testdb/setup.sh start 3 | 4 | stopdb: 5 | @testdb/setup.sh stop 6 | -------------------------------------------------------------------------------- /mongoreplay/compressed.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mongodb-labs/mongoreplay/HEAD/mongoreplay/compressed.pcap -------------------------------------------------------------------------------- /mongoreplay/mongoreplay_test.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mongodb-labs/mongoreplay/HEAD/mongoreplay/mongoreplay_test.out -------------------------------------------------------------------------------- /mongoreplay/pcap_test_run.playback: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mongodb-labs/mongoreplay/HEAD/mongoreplay/pcap_test_run.playback -------------------------------------------------------------------------------- /internal/legacy/json/testdata/code.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mongodb-labs/mongoreplay/HEAD/internal/legacy/json/testdata/code.json.gz -------------------------------------------------------------------------------- /internal/llmgo/README.md: -------------------------------------------------------------------------------- 1 | The MongoDB driver for Go 2 | ------------------------- 3 | 4 | Please go to [http://labix.org/mgo](http://labix.org/mgo) for all project details. 5 | -------------------------------------------------------------------------------- /internal/llmgo/dbtest/export_test.go: -------------------------------------------------------------------------------- 1 | package dbtest 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | func (dbs *DBServer) ProcessTest() *os.Process { 8 | if dbs.server == nil { 9 | return nil 10 | } 11 | return dbs.server.Process 12 | } 13 | -------------------------------------------------------------------------------- /internal/llmgo/testserver/export_test.go: -------------------------------------------------------------------------------- 1 | package testserver 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | func (ts *TestServer) ProcessTest() *os.Process { 8 | if ts.server == nil { 9 | return nil 10 | } 11 | return ts.server.Process 12 | } 13 | -------------------------------------------------------------------------------- /internal/llmgo/saslstub.go: -------------------------------------------------------------------------------- 1 | //+build !sasl 2 | 3 | package mgo 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | func saslNew(cred Credential, host string) (saslStepper, error) { 10 | return nil, fmt.Errorf("SASL support not enabled during build (-tags sasl)") 11 | } 12 | -------------------------------------------------------------------------------- /internal/llmgo/syscall_windows_test.go: -------------------------------------------------------------------------------- 1 | package mgo_test 2 | 3 | func stop(pid int) (err error) { 4 | panicOnWindows() // Always does. 5 | return nil 6 | } 7 | 8 | func cont(pid int) (err error) { 9 | panicOnWindows() // Always does. 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /internal/llmgo/syscall_test.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package mgo_test 4 | 5 | import ( 6 | "syscall" 7 | ) 8 | 9 | func stop(pid int) (err error) { 10 | return syscall.Kill(pid, syscall.SIGSTOP) 11 | } 12 | 13 | func cont(pid int) (err error) { 14 | return syscall.Kill(pid, syscall.SIGCONT) 15 | } 16 | -------------------------------------------------------------------------------- /internal/llmgo/saslimpl.go: -------------------------------------------------------------------------------- 1 | //+build sasl 2 | 3 | package mgo 4 | 5 | import ( 6 | "github.com/mongodb-labs/mongoreplay/internal/llmgo/internal/sasl" 7 | ) 8 | 9 | func saslNew(cred Credential, host string) (saslStepper, error) { 10 | return sasl.New(cred.Username, cred.Password, cred.Mechanism, cred.Service, host) 11 | } 12 | -------------------------------------------------------------------------------- /internal/legacy/util/util.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | // Package util provides commonly used utility functions. 8 | package util 9 | -------------------------------------------------------------------------------- /internal/legacy/json/consts.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | const ( 10 | ArrayStart = '[' 11 | ArraySep = ',' 12 | ArrayEnd = ']' 13 | ) 14 | -------------------------------------------------------------------------------- /mongoreplay/testPcap/make_WorkloadWithEOF.js: -------------------------------------------------------------------------------- 1 | var db1 = (new Mongo("mongodb://localhost:20000")).getDB("mongoreplay"); 2 | 3 | db1.test.insert({dummy:true}); 4 | for(var i =0; i < 10; i ++ ){ 5 | db1.test.find().limit(10).toArray() 6 | } 7 | 8 | db1= (new Mongo("mongodb://localhost:20000")).getDB("mongoreplay"); 9 | 10 | db1.test.insert({dummy:true}); 11 | for(var i =0; i < 10; i ++ ){ 12 | db1.test.find().limit(10).toArray() 13 | }build/mongotape/ 14 | -------------------------------------------------------------------------------- /internal/llmgo/internal/sasl/sasl_windows.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "sspi_windows.h" 4 | 5 | SECURITY_STATUS SEC_ENTRY sspi_acquire_credentials_handle(CredHandle* cred_handle, char* username, char* password, char* domain); 6 | int sspi_step(CredHandle* cred_handle, int has_context, CtxtHandle* context, PVOID* buffer, ULONG* buffer_length, char* target); 7 | int sspi_send_client_authz_id(CtxtHandle* context, PVOID* buffer, ULONG* buffer_length, char* user_plus_realm); 8 | -------------------------------------------------------------------------------- /internal/legacy/failpoint/failpoints.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package failpoint 8 | 9 | // Supported failpoint names 10 | const ( 11 | PauseBeforeDumping = "PauseBeforeDumping" 12 | SlowBSONDump = "SlowBSONDump" 13 | ) 14 | -------------------------------------------------------------------------------- /internal/legacy/options/options_fp.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | // +build failpoints 8 | 9 | package options 10 | 11 | // EnableFailpoints does nothing if we've compiled with failpoints enabled 12 | func EnableFailpoints(opts *ToolOptions) { 13 | } 14 | -------------------------------------------------------------------------------- /internal/llmgo/bson/specdata/update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ ! -d specifications ]; then 6 | git clone -b bson git@github.com:jyemin/specifications 7 | fi 8 | 9 | TESTFILE="../specdata_test.go" 10 | 11 | cat < $TESTFILE 12 | package bson_test 13 | 14 | var specTests = []string{ 15 | END 16 | 17 | for file in specifications/source/bson/tests/*.yml; do 18 | ( 19 | echo '`' 20 | cat $file 21 | echo -n '`,' 22 | ) >> $TESTFILE 23 | done 24 | 25 | echo '}' >> $TESTFILE 26 | 27 | gofmt -w $TESTFILE 28 | -------------------------------------------------------------------------------- /internal/legacy/options/options_fp_disabled.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | // +build !failpoints 8 | 9 | package options 10 | 11 | // EnableFailpoints removes the failpoints options 12 | func EnableFailpoints(opts *ToolOptions) { 13 | opt := opts.FindOptionByLongName("failpoints") 14 | opt.LongName = "" 15 | } 16 | -------------------------------------------------------------------------------- /internal/legacy/failpoint/failpoint_disabled.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | // +build !failpoints 8 | 9 | package failpoint 10 | 11 | func ParseFailpoints(_ string) { 12 | } 13 | 14 | func Get(fp string) (string, bool) { 15 | return "", false 16 | } 17 | 18 | func Enabled(fp string) bool { 19 | return false 20 | } 21 | -------------------------------------------------------------------------------- /internal/legacy/options/options_openssl.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | // +build ssl,!openssl_pre_1.0 8 | 9 | package options 10 | 11 | import "github.com/10gen/openssl" 12 | 13 | func init() { 14 | versionInfos = append(versionInfos, versionInfo{ 15 | key: "OpenSSL version", 16 | value: openssl.Version, 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /internal/legacy/util/exit_code.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package util 8 | 9 | import ( 10 | "errors" 11 | ) 12 | 13 | const ( 14 | ExitError int = 1 15 | ExitClean int = 0 16 | ExitBadOptions int = 3 17 | ExitKill int = 4 18 | // Go reserves exit code 2 for its own use 19 | ) 20 | 21 | var ( 22 | ErrTerminated = errors.New("received termination signal") 23 | ) 24 | -------------------------------------------------------------------------------- /internal/legacy/json/nan.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | // Transition functions for recognizing NaN. 10 | // Adapted from encoding/json/scanner.go. 11 | 12 | // stateUpperNa is the state after reading `Na`. 13 | func stateUpperNa(s *scanner, c int) int { 14 | if c == 'N' { 15 | s.step = stateEndValue 16 | return scanContinue 17 | } 18 | return s.error(c, "in literal NaN (expecting 'N')") 19 | } 20 | -------------------------------------------------------------------------------- /internal/legacy/json/hex.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | // Transition function for recognizing hexadecimal numbers. 10 | // Adapted from encoding/json/scanner.go. 11 | 12 | // stateHex is the state after reading `0x` or `0X`. 13 | func stateHex(s *scanner, c int) int { 14 | if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { 15 | s.step = stateHex 16 | return scanContinue 17 | } 18 | return stateEndValue(s, c) 19 | } 20 | -------------------------------------------------------------------------------- /internal/legacy/json/infinity.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | // Transition functions for recognizing Infinity. 10 | // Adapted from encoding/json/scanner.go. 11 | 12 | // stateI is the state after reading `In`. 13 | func stateIn(s *scanner, c int) int { 14 | if c == 'f' { 15 | s.step = generateState("Infinity", []byte("inity"), stateEndValue) 16 | return scanContinue 17 | } 18 | return s.error(c, "in literal Infinity (expecting 'f')") 19 | } 20 | -------------------------------------------------------------------------------- /internal/legacy/json/undefined.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | // Transition functions for recognizing undefined. 10 | // Adapted from encoding/json/scanner.go. 11 | 12 | // stateU is the state after reading `u`. 13 | func stateU(s *scanner, c int) int { 14 | if c == 'n' { 15 | s.step = generateState("undefined", []byte("defined"), stateEndValue) 16 | return scanContinue 17 | } 18 | return s.error(c, "in literal undefined (expecting 'n')") 19 | } 20 | -------------------------------------------------------------------------------- /internal/legacy/json/maxkey.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | // Transition functions for recognizing MaxKey. 10 | // Adapted from encoding/json/scanner.go. 11 | 12 | // stateUpperMa is the state after reading `Ma`. 13 | func stateUpperMa(s *scanner, c int) int { 14 | if c == 'x' { 15 | s.step = generateState("MaxKey", []byte("Key"), stateOptionalConstructor) 16 | return scanContinue 17 | } 18 | return s.error(c, "in literal MaxKey (expecting 'x')") 19 | } 20 | -------------------------------------------------------------------------------- /internal/legacy/json/minkey.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | // Transition functions for recognizing MinKey. 10 | // Adapted from encoding/json/scanner.go. 11 | 12 | // stateUpperMi is the state after reading `Mi`. 13 | func stateUpperMi(s *scanner, c int) int { 14 | if c == 'n' { 15 | s.step = generateState("MinKey", []byte("Key"), stateOptionalConstructor) 16 | return scanContinue 17 | } 18 | return s.error(c, "in literal MinKey (expecting 'n')") 19 | } 20 | -------------------------------------------------------------------------------- /internal/llmgo/export_test.go: -------------------------------------------------------------------------------- 1 | package mgo 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | func HackPingDelay(newDelay time.Duration) (restore func()) { 8 | globalMutex.Lock() 9 | defer globalMutex.Unlock() 10 | 11 | oldDelay := pingDelay 12 | restore = func() { 13 | globalMutex.Lock() 14 | pingDelay = oldDelay 15 | globalMutex.Unlock() 16 | } 17 | pingDelay = newDelay 18 | return 19 | } 20 | 21 | func HackSyncSocketTimeout(newTimeout time.Duration) (restore func()) { 22 | globalMutex.Lock() 23 | defer globalMutex.Unlock() 24 | 25 | oldTimeout := syncSocketTimeout 26 | restore = func() { 27 | globalMutex.Lock() 28 | syncSocketTimeout = oldTimeout 29 | globalMutex.Unlock() 30 | } 31 | syncSocketTimeout = newTimeout 32 | return 33 | } 34 | -------------------------------------------------------------------------------- /internal/legacy/lldb/tlsgo/rootcerts.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2018-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | // Based on https://github.com/hashicorp/go-rootcerts by HashiCorp 8 | // See THIRD-PARTY-NOTICES for original license terms. 9 | 10 | // +build !darwin 11 | 12 | package tlsgo 13 | 14 | import ( 15 | "crypto/x509" 16 | ) 17 | 18 | // Stubbed for non-darwin systems. By returning nil, the Go library 19 | // will use its own code for finding system certs. 20 | func loadSystemCAs() (*x509.CertPool, error) { 21 | return nil, nil 22 | } 23 | -------------------------------------------------------------------------------- /internal/legacy/util/strings.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package util 8 | 9 | // Pluralize takes an amount and two strings denoting the singular 10 | // and plural noun the amount represents. If the amount is singular, 11 | // the singular form is returned; otherwise plural is returned. E.g. 12 | // Pluralize(X, "mouse", "mice") -> 0 mice, 1 mouse, 2 mice, ... 13 | func Pluralize(amount int, singular, plural string) string { 14 | if amount == 1 { 15 | return singular 16 | } 17 | return plural 18 | } 19 | -------------------------------------------------------------------------------- /internal/legacy/options/options_gssapi.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | // +build sasl 8 | 9 | package options 10 | 11 | func init() { 12 | ConnectionOptFunctions = append(ConnectionOptFunctions, registerGSSAPIOptions) 13 | } 14 | 15 | func registerGSSAPIOptions(self *ToolOptions) error { 16 | _, err := self.parser.AddGroup("kerberos options", "", self.Kerberos) 17 | if err != nil { 18 | return err 19 | } 20 | self.URI.AddKnownURIParameters(KnownURIOptionsKerberos) 21 | BuiltWithGSSAPI = true 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /internal/legacy/options/options_ssl.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | // +build ssl 8 | 9 | package options 10 | 11 | func init() { 12 | ConnectionOptFunctions = append(ConnectionOptFunctions, registerSSLOptions) 13 | BuiltWithSSL = true 14 | } 15 | 16 | func registerSSLOptions(self *ToolOptions) error { 17 | _, err := self.parser.AddGroup("ssl options", "", self.SSL) 18 | if err != nil { 19 | return err 20 | } 21 | if self.enabledOptions.URI { 22 | self.URI.AddKnownURIParameters(KnownURIOptionsSSL) 23 | } 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /internal/legacy/password/pass_util.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | // +build !solaris 8 | 9 | package password 10 | 11 | import ( 12 | "github.com/howeyc/gopass" 13 | "golang.org/x/crypto/ssh/terminal" 14 | "syscall" 15 | ) 16 | 17 | // This file contains all the calls needed to properly 18 | // handle password input from stdin/terminal on all 19 | // operating systems that aren't solaris 20 | 21 | func IsTerminal() bool { 22 | return terminal.IsTerminal(int(syscall.Stdin)) 23 | } 24 | 25 | func GetPass() string { 26 | pass, _ := gopass.GetPasswd() 27 | return string(pass) 28 | } 29 | -------------------------------------------------------------------------------- /mongoreplay/testPcap/make_SingleChannelNoDeadlock.js: -------------------------------------------------------------------------------- 1 | var toInsert = []; 2 | for (var j = 0; j < 10000; j++) { 3 | toInsert.push({ 4 | x: j 5 | }); 6 | } 7 | 8 | for (var i = 0; i < 100; i++) { 9 | db.test.insert(toInsert); 10 | } 11 | 12 | db1 = (new Mongo()).getDB("mongoreplay"); 13 | 14 | var c = db1.runCommand({ 15 | aggregate: "test", 16 | cursor: { 17 | batchSize: 5 18 | } 19 | }); 20 | 21 | cursorId = c.cursor.id; 22 | 23 | print("got cursor fron cxn1: ", cursorId); 24 | 25 | var cursor1 = db1.getMongo().cursorFromId("mongoreplay.test", cursorId); 26 | 27 | while (c.hasNext()) { 28 | printjson(c.next()); 29 | } 30 | 31 | for (var i = 0; i < 100; i++) { 32 | db1.test.insert({ 33 | y: i 34 | }); 35 | sleep(50); 36 | } 37 | var cursor2 = db1.test.find(); 38 | printjson(cursor2.next()); 39 | -------------------------------------------------------------------------------- /internal/legacy/lldb/db_tlsgo.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | // +build ssl,openssl_pre_1.0 8 | 9 | package lldb 10 | 11 | import ( 12 | "github.com/mongodb-labs/mongoreplay/internal/legacy/lldb/tlsgo" 13 | "github.com/mongodb-labs/mongoreplay/internal/legacy/options" 14 | ) 15 | 16 | func init() { 17 | GetConnectorFuncs = append(GetConnectorFuncs, getSSLConnector) 18 | } 19 | 20 | // return the SSL DB connector if using SSL, otherwise, return nil. 21 | func getSSLConnector(opts options.ToolOptions) DBConnector { 22 | if opts.SSL.UseSSL { 23 | return &tlsgo.TLSDBConnector{} 24 | } 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /internal/legacy/lldb/db_openssl.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | // +build ssl,!openssl_pre_1.0 8 | 9 | package lldb 10 | 11 | import ( 12 | "github.com/mongodb-labs/mongoreplay/internal/legacy/options" 13 | "github.com/mongodb-labs/mongoreplay/internal/legacy/lldb/openssl" 14 | ) 15 | 16 | func init() { 17 | GetConnectorFuncs = append(GetConnectorFuncs, getSSLConnector) 18 | } 19 | 20 | // return the SSL DB connector if using SSL, otherwise, return nil. 21 | func getSSLConnector(opts options.ToolOptions) DBConnector { 22 | if opts.SSL.UseSSL { 23 | return &openssl.SSLDBConnector{} 24 | } 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /internal/legacy/util/net.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package util 8 | 9 | import ( 10 | "net" 11 | "time" 12 | ) 13 | 14 | // EnableTCPKeepAlive enables TCP keepalive on the underlying TCP connection. 15 | func EnableTCPKeepAlive(conn net.Conn, keepAlivePeriod time.Duration) error { 16 | if keepAlivePeriod == 0 { 17 | return nil 18 | } 19 | if tcpconn, ok := conn.(*net.TCPConn); ok { 20 | err := tcpconn.SetKeepAlive(true) 21 | if err != nil { 22 | return err 23 | } 24 | err = tcpconn.SetKeepAlivePeriod(keepAlivePeriod) 25 | if err != nil { 26 | return err 27 | } 28 | } 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /internal/legacy/util/format_date.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package util 8 | 9 | import ( 10 | "time" 11 | ) 12 | 13 | var ( 14 | acceptedDateFormats = []string{ 15 | "2006-01-02T15:04:05.000Z", 16 | "2006-01-02T15:04:05Z", 17 | "2006-01-02T15:04Z", 18 | "2006-01-02T15:04:05.000-0700", 19 | "2006-01-02T15:04:05-0700", 20 | "2006-01-02T15:04-0700", 21 | "2006-01-02T15:04:05Z07:00", 22 | } 23 | ) 24 | 25 | func FormatDate(v string) (interface{}, error) { 26 | var date interface{} 27 | var err error 28 | 29 | for _, format := range acceptedDateFormats { 30 | date, err = time.Parse(format, v) 31 | if err == nil { 32 | return date, nil 33 | } 34 | } 35 | return date, err 36 | } 37 | -------------------------------------------------------------------------------- /mongoreplay/testPcap/make_MultiCursorWait.js: -------------------------------------------------------------------------------- 1 | db1 = (new Mongo("localhost:27017")).getDB("mongoreplay"); 2 | 3 | var toInsert = []; 4 | for (var j = 0; j < 10000; j++) { 5 | toInsert.push({ 6 | x: j 7 | }); 8 | } 9 | 10 | for (var i = 0; i < 100; i++) { 11 | db1.test.insert(toInsert); 12 | } 13 | db2 = (new Mongo("localhost:27017")).getDB("mongoreplay"); 14 | 15 | var agg1 = db2.runCommand({ 16 | aggregate: "test", 17 | cursor: { 18 | batchSize: 5 19 | } 20 | }); 21 | 22 | cursorId = agg1.cursor.id; 23 | 24 | print("got cursor fron cxn1: ", cursorId); 25 | 26 | var cursor1 = db2.getMongo().cursorFromId("mongoreplay.test", cursorId); 27 | 28 | while (cursor1.hasNext()) { 29 | printjson(cursor1.next()); 30 | } 31 | 32 | var agg2 = db1.runCommand({ 33 | aggregate: "test", 34 | cursor: { 35 | batchSize: 5 36 | } 37 | }); 38 | 39 | var cursor2 = db1.getMongo().cursorFromId("mongoreplay.test", agg2.cursor.id); 40 | -------------------------------------------------------------------------------- /internal/llmgo/txn/tarjan_test.go: -------------------------------------------------------------------------------- 1 | package txn 2 | 3 | import ( 4 | "fmt" 5 | "github.com/mongodb-labs/mongoreplay/internal/llmgo/bson" 6 | . "gopkg.in/check.v1" 7 | ) 8 | 9 | type TarjanSuite struct{} 10 | 11 | var _ = Suite(TarjanSuite{}) 12 | 13 | func bid(n int) bson.ObjectId { 14 | return bson.ObjectId(fmt.Sprintf("%024d", n)) 15 | } 16 | 17 | func bids(ns ...int) (ids []bson.ObjectId) { 18 | for _, n := range ns { 19 | ids = append(ids, bid(n)) 20 | } 21 | return 22 | } 23 | 24 | func (TarjanSuite) TestExample(c *C) { 25 | successors := map[bson.ObjectId][]bson.ObjectId{ 26 | bid(1): bids(2, 3), 27 | bid(2): bids(1, 5), 28 | bid(3): bids(4), 29 | bid(4): bids(3, 5), 30 | bid(5): bids(6), 31 | bid(6): bids(7), 32 | bid(7): bids(8), 33 | bid(8): bids(6, 9), 34 | bid(9): bids(), 35 | } 36 | 37 | c.Assert(tarjanSort(successors), DeepEquals, [][]bson.ObjectId{ 38 | bids(9), 39 | bids(6, 7, 8), 40 | bids(5), 41 | bids(3, 4), 42 | bids(1, 2), 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /mongoreplay/version.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package mongoreplay 8 | 9 | import ( 10 | "fmt" 11 | "runtime" 12 | ) 13 | 14 | // Print the tool version to stdout. Returns whether or not the version flag 15 | // is specified. 16 | func (o *VersionOptions) PrintVersion(versionStr, gitCommit string) bool { 17 | if o.Version { 18 | printVersionInfo(versionStr, gitCommit) 19 | } 20 | return o.Version 21 | } 22 | 23 | func printVersionInfo(versionStr, gitCommit string) { 24 | fmt.Printf("%v version: %v\n", "mongoreplay", versionStr) 25 | fmt.Printf("git version: %v\n", gitCommit) 26 | fmt.Printf("Go version: %v\n", runtime.Version()) 27 | fmt.Printf(" os: %v\n", runtime.GOOS) 28 | fmt.Printf(" arch: %v\n", runtime.GOARCH) 29 | fmt.Printf(" compiler: %v\n", runtime.Compiler) 30 | } 31 | -------------------------------------------------------------------------------- /internal/legacy/json/tags_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | // Based on github.com/golang/go by The Go Authors 8 | // See THIRD-PARTY-NOTICES for original license terms. 9 | 10 | package json 11 | 12 | import ( 13 | "github.com/mongodb-labs/mongoreplay/internal/legacy/testtype" 14 | "testing" 15 | ) 16 | 17 | func TestTagParsing(t *testing.T) { 18 | testtype.SkipUnlessTestType(t, testtype.UnitTestType) 19 | name, opts := parseTag("field,foobar,foo") 20 | if name != "field" { 21 | t.Fatalf("name = %q, want field", name) 22 | } 23 | for _, tt := range []struct { 24 | opt string 25 | want bool 26 | }{ 27 | {"foobar", true}, 28 | {"foo", true}, 29 | {"bar", false}, 30 | } { 31 | if opts.Contains(tt.opt) != tt.want { 32 | t.Errorf("Contains(%q) = %v", tt.opt, !tt.want) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /internal/legacy/lldb/kerberos/gssapi.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | // Package kerberos implements authentication to MongoDB using kerberos 8 | package kerberos 9 | 10 | // #cgo windows CFLAGS: -Ic:/sasl/include 11 | // #cgo windows LDFLAGS: -Lc:/sasl/lib 12 | 13 | import ( 14 | "github.com/mongodb-labs/mongoreplay/internal/legacy/options" 15 | mgo "github.com/mongodb-labs/mongoreplay/internal/llmgo" 16 | ) 17 | 18 | const authMechanism = "GSSAPI" 19 | 20 | func AddKerberosOpts(opts options.ToolOptions, dialInfo *mgo.DialInfo) { 21 | if dialInfo == nil { 22 | return 23 | } 24 | if opts.Kerberos == nil { 25 | return 26 | } 27 | if opts.Auth == nil || (opts.Auth.Mechanism != authMechanism && 28 | dialInfo.Mechanism != authMechanism) { 29 | return 30 | } 31 | dialInfo.Service = opts.Kerberos.Service 32 | dialInfo.ServiceHost = opts.Kerberos.ServiceHost 33 | dialInfo.Mechanism = authMechanism 34 | } 35 | -------------------------------------------------------------------------------- /internal/legacy/util/bool.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package util 8 | 9 | import ( 10 | "gopkg.in/mgo.v2/bson" 11 | "reflect" 12 | ) 13 | 14 | // IsTruthy returns true for values the server will interpret as "true". 15 | // True values include {}, [], "", true, and any numbers != 0 16 | func IsTruthy(val interface{}) bool { 17 | if val == nil { 18 | return false 19 | } 20 | if val == bson.Undefined { 21 | return false 22 | } 23 | 24 | v := reflect.ValueOf(val) 25 | switch v.Kind() { 26 | case reflect.Map, reflect.Slice, reflect.Array, reflect.String, reflect.Struct: 27 | return true 28 | default: 29 | z := reflect.Zero(v.Type()) 30 | return v.Interface() != z.Interface() 31 | } 32 | } 33 | 34 | // IsFalsy returns true for values the server will interpret as "false". 35 | // False values include numbers == 0, false, and nil 36 | func IsFalsy(val interface{}) bool { 37 | return !IsTruthy(val) 38 | } 39 | -------------------------------------------------------------------------------- /internal/legacy/lldb/openssl/openssl_fips.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | // +build ssl,!openssl_pre_1.0 8 | 9 | package openssl 10 | 11 | import ( 12 | "fmt" 13 | "github.com/mongodb-labs/mongoreplay/internal/legacy/options" 14 | 15 | "github.com/10gen/openssl" 16 | ) 17 | 18 | func init() { 19 | if openssl.FIPSModeDefined() { 20 | sslInitializationFunctions = append(sslInitializationFunctions, SetUpFIPSMode) 21 | } else { 22 | sslInitializationFunctions = append(sslInitializationFunctions, NoFIPSModeAvailable) 23 | } 24 | } 25 | 26 | func SetUpFIPSMode(opts options.ToolOptions) error { 27 | if err := openssl.FIPSModeSet(opts.SSLFipsMode); err != nil { 28 | return fmt.Errorf("couldn't set FIPS mode to %v: %v", opts.SSLFipsMode, err) 29 | } 30 | return nil 31 | } 32 | 33 | func NoFIPSModeAvailable(opts options.ToolOptions) error { 34 | if opts.SSLFipsMode { 35 | return fmt.Errorf("FIPS mode not supported") 36 | } 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /internal/legacy/json/unquoted.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | // Transition function for recognizing unquoted strings. 10 | // Adapted from encoding/json/scanner.go. 11 | 12 | func isBeginUnquotedString(c int) bool { 13 | return c == '$' || c == '_' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' 14 | } 15 | 16 | func isInUnquotedString(c int) bool { 17 | return isBeginUnquotedString(c) || '0' <= c && c <= '9' 18 | } 19 | 20 | func stateInUnquotedString(s *scanner, c int) int { 21 | if isInUnquotedString(c) { 22 | return scanContinue 23 | } 24 | return stateEndValue(s, c) 25 | } 26 | 27 | // Decoder function that immediately returns an already unquoted string. 28 | // Adapted from encoding/json/decode.go. 29 | func maybeUnquoteBytes(s []byte) ([]byte, bool) { 30 | if len(s) == 0 { 31 | return nil, false 32 | } 33 | if s[0] != '"' && s[len(s)-1] != '"' && s[0] != '\'' && s[len(s)-1] != '\'' { 34 | return s, true 35 | } 36 | return unquoteBytes(s) 37 | } 38 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mongodb-labs/mongoreplay 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/10gen/escaper v0.0.0-20160802155924-17fe61c658dc 7 | github.com/10gen/openssl v0.0.0-20190731121843-f50700cbcaf4 8 | github.com/golang/snappy v0.0.1 9 | github.com/google/gopacket v1.1.17 10 | github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c 11 | github.com/jessevdk/go-flags v1.4.0 12 | github.com/kr/pretty v0.2.0 // indirect 13 | github.com/patrickmn/go-cache v2.1.0+incompatible 14 | github.com/smartystreets/goconvey v1.6.4 15 | github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect 16 | github.com/xdg/stringprep v1.0.0 17 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 18 | golang.org/x/text v0.3.2 // indirect 19 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 20 | gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 21 | gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 22 | gopkg.in/yaml.v2 v2.2.8 23 | ) 24 | 25 | replace ( 26 | github.com/golang/snappy => github.com/golang/snappy v0.0.0-20160529050041-d9eb7a3d35ec 27 | github.com/spacemonkeygo/spacelog => github.com/spacemonkeygo/spacelog v0.0.0-20160606222522-f936fb050dc6 28 | golang.org/x/text => github.com/golang/text v0.3.0 29 | gopkg.in/mgo.v2 => github.com/10gen/mgo v0.0.0-20181212170345-8c133fd1d0fc 30 | ) 31 | -------------------------------------------------------------------------------- /internal/legacy/failpoint/failpoint.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | // +build failpoints 8 | 9 | // Package failpoint implements triggers for custom debugging behavior 10 | package failpoint 11 | 12 | import ( 13 | "strings" 14 | ) 15 | 16 | var values map[string]string 17 | 18 | func init() { 19 | values = make(map[string]string) 20 | } 21 | 22 | // ParseFailpoints registers a comma-separated list of failpoint=value pairs 23 | func ParseFailpoints(arg string) { 24 | args := strings.Split(arg, ",") 25 | for _, fp := range args { 26 | if sep := strings.Index(fp, "="); sep != -1 { 27 | key := fp[:sep] 28 | val := fp[sep+1:] 29 | values[key] = val 30 | continue 31 | } 32 | values[fp] = "" 33 | } 34 | } 35 | 36 | // Get returns the value of the given failpoint and true, if it exists, and 37 | // false otherwise 38 | func Get(fp string) (string, bool) { 39 | val, ok := values[fp] 40 | return val, ok 41 | } 42 | 43 | // Enabled returns true iff the given failpoint has been turned on 44 | func Enabled(fp string) bool { 45 | _, ok := Get(fp) 46 | return ok 47 | } 48 | -------------------------------------------------------------------------------- /internal/llmgo/doc.go: -------------------------------------------------------------------------------- 1 | // Package mgo offers a rich MongoDB driver for Go. 2 | // 3 | // Details about the mgo project (pronounced as "mango") are found 4 | // in its web page: 5 | // 6 | // http://labix.org/mgo 7 | // 8 | // Usage of the driver revolves around the concept of sessions. To 9 | // get started, obtain a session using the Dial function: 10 | // 11 | // session, err := mgo.Dial(url) 12 | // 13 | // This will establish one or more connections with the cluster of 14 | // servers defined by the url parameter. From then on, the cluster 15 | // may be queried with multiple consistency rules (see SetMode) and 16 | // documents retrieved with statements such as: 17 | // 18 | // c := session.DB(database).C(collection) 19 | // err := c.Find(query).One(&result) 20 | // 21 | // New sessions are typically created by calling session.Copy on the 22 | // initial session obtained at dial time. These new sessions will share 23 | // the same cluster information and connection pool, and may be easily 24 | // handed into other methods and functions for organizing logic. 25 | // Every session created must have its Close method called at the end 26 | // of its life time, so its resources may be put back in the pool or 27 | // collected, depending on the case. 28 | // 29 | // For more details, see the documentation for the types and methods. 30 | // 31 | package mgo 32 | -------------------------------------------------------------------------------- /internal/legacy/failpoint/failpoint_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | // +build failpoints 8 | 9 | package failpoint 10 | 11 | import ( 12 | "github.com/mongodb-labs/mongoreplay/internal/legacy/testtype" 13 | "testing" 14 | 15 | . "github.com/smartystreets/goconvey/convey" 16 | ) 17 | 18 | func TestFailpointParsing(t *testing.T) { 19 | testtype.SkipUnlessTestType(t, testtype.UnitTestType) 20 | 21 | Convey("With test args", t, func() { 22 | args := "foo=bar,baz,biz=,=a" 23 | ParseFailpoints(args) 24 | 25 | So(Enabled("foo"), ShouldBeTrue) 26 | So(Enabled("baz"), ShouldBeTrue) 27 | So(Enabled("biz"), ShouldBeTrue) 28 | So(Enabled(""), ShouldBeTrue) 29 | So(Enabled("bar"), ShouldBeFalse) 30 | 31 | var val string 32 | var ok bool 33 | val, ok = Get("foo") 34 | So(val, ShouldEqual, "bar") 35 | So(ok, ShouldBeTrue) 36 | val, ok = Get("baz") 37 | So(val, ShouldEqual, "") 38 | So(ok, ShouldBeTrue) 39 | val, ok = Get("biz") 40 | So(val, ShouldEqual, "") 41 | So(ok, ShouldBeTrue) 42 | val, ok = Get("") 43 | So(val, ShouldEqual, "a") 44 | So(ok, ShouldBeTrue) 45 | val, ok = Get("bar") 46 | So(ok, ShouldBeFalse) 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /internal/legacy/json/tags.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | // Based on github.com/golang/go by The Go Authors 8 | // See THIRD-PARTY-NOTICES for original license terms. 9 | 10 | package json 11 | 12 | import ( 13 | "strings" 14 | ) 15 | 16 | // tagOptions is the string following a comma in a struct field's "json" 17 | // tag, or the empty string. It does not include the leading comma. 18 | type tagOptions string 19 | 20 | // parseTag splits a struct field's json tag into its name and 21 | // comma-separated options. 22 | func parseTag(tag string) (string, tagOptions) { 23 | if idx := strings.Index(tag, ","); idx != -1 { 24 | return tag[:idx], tagOptions(tag[idx+1:]) 25 | } 26 | return tag, tagOptions("") 27 | } 28 | 29 | // Contains reports whether a comma-separated list of options 30 | // contains a particular substr flag. substr must be surrounded by a 31 | // string boundary or commas. 32 | func (o tagOptions) Contains(optionName string) bool { 33 | if len(o) == 0 { 34 | return false 35 | } 36 | s := string(o) 37 | for s != "" { 38 | var next string 39 | i := strings.Index(s, ",") 40 | if i >= 0 { 41 | s, next = s[:i], s[i+1:] 42 | } 43 | if s == optionName { 44 | return true 45 | } 46 | s = next 47 | } 48 | return false 49 | } 50 | -------------------------------------------------------------------------------- /internal/legacy/json/iso_date.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | "reflect" 12 | ) 13 | 14 | // Transition functions for recognizing ISODate. 15 | // Adapted from encoding/json/scanner.go. 16 | 17 | // stateIS is the state after reading `IS`. 18 | func stateIS(s *scanner, c int) int { 19 | if c == 'O' { 20 | s.step = stateISO 21 | return scanContinue 22 | } 23 | return s.error(c, "in literal ISODate (expecting 'O')") 24 | } 25 | 26 | // stateISO is the state after reading `ISO`. 27 | func stateISO(s *scanner, c int) int { 28 | if c == 'D' { 29 | s.step = stateD 30 | return scanContinue 31 | } 32 | return s.error(c, "in literal ISODate (expecting 'D')") 33 | } 34 | 35 | // Decodes a ISODate literal stored in the underlying byte data into v. 36 | func (d *decodeState) storeISODate(v reflect.Value) { 37 | op := d.scanWhile(scanSkipSpace) 38 | if op != scanBeginCtor { 39 | d.error(fmt.Errorf("expected beginning of constructor")) 40 | } 41 | args, err := d.ctor("ISODate", []reflect.Type{isoDateType}) 42 | if err != nil { 43 | d.error(err) 44 | } 45 | switch kind := v.Kind(); kind { 46 | case reflect.Interface: 47 | v.Set(args[0]) 48 | default: 49 | d.error(fmt.Errorf("cannot store %v value into %v type", isoDateType, kind)) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /runTests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | tags="" 3 | if [ ! -z "$1" ] 4 | then 5 | tags="$@" 6 | fi 7 | 8 | # make sure we're in the directory where the script lives 9 | SCRIPT_DIR="$(cd "$(dirname ${BASH_SOURCE[0]})" && pwd)" 10 | cd $SCRIPT_DIR 11 | OUTPUT_DIR="$SCRIPT_DIR/testing_output" 12 | mkdir -p "$OUTPUT_DIR" 13 | 14 | . ./set_goenv.sh 15 | set_goenv || exit 16 | 17 | # build binaries for any tests that expect them for blackbox testing 18 | ./build.sh $tags 19 | ec=0 20 | 21 | # Run all tests depending on what flags are set in the environment 22 | for i in internal/legacy/failpoint internal/legacy/json internal/legacy/log internal/legacy/options internal/legacy/util mongoreplay; do 23 | echo "Testing ${i}..." 24 | COMMON_SUBPKG=$(basename $i) 25 | COVERAGE_ARGS=""; 26 | if [ "$RUN_COVERAGE" == "true" ]; then 27 | export COVERAGE_ARGS="-coverprofile=coverage_$COMMON_SUBPKG.out" 28 | fi 29 | if [ "$ON_EVERGREEN" = "true" ]; then 30 | (cd $i && go test -v $(buildflags) -ldflags "$(print_ldflags)" $tags -tags "$(print_tags $TOOLS_BUILD_TAGS)" "$COVERAGE_ARGS" > "$OUTPUT_DIR/$COMMON_SUBPKG.suite") 31 | exitcode=$? 32 | cat "$OUTPUT_DIR/$COMMON_SUBPKG.suite" 33 | else 34 | (cd $i && go test -v $(buildflags) -ldflags "$(print_ldflags)" "$(print_tags $tags)" "$COVERAGE_ARGS" ) 35 | exitcode=$? 36 | fi 37 | if [ $exitcode -ne 0 ]; then 38 | echo "Error testing $i" 39 | ec=1 40 | fi 41 | done 42 | 43 | if [ -t /dev/stdin ]; then 44 | stty sane 45 | fi 46 | 47 | exit $ec 48 | -------------------------------------------------------------------------------- /internal/llmgo/testdb/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | start() { 4 | mkdir _testdb 5 | cd _testdb 6 | mkdir db1 db2 db3 rs1a rs1b rs1c rs2a rs2b rs2c rs3a rs3b rs3c rs4a cfg1 cfg2 cfg3 7 | cp ../testdb/supervisord.conf supervisord.conf 8 | cp ../testdb/server.pem server.pem 9 | echo keyfile > keyfile 10 | chmod 600 keyfile 11 | COUNT=$(grep '^\[program' supervisord.conf | wc -l | tr -d ' ') 12 | if ! mongod --help | grep -q -- --ssl; then 13 | COUNT=$(($COUNT - 1)) 14 | fi 15 | echo "Running supervisord..." 16 | supervisord || ( echo "Supervisord failed executing ($?)" && exit 1 ) 17 | echo "Supervisord is up, starting $COUNT processes..." 18 | for i in $(seq 30); do 19 | RUNNING=$(supervisorctl status | grep RUNNING | wc -l | tr -d ' ') 20 | echo "$RUNNING processes running..." 21 | if [ x$COUNT = x$RUNNING ]; then 22 | echo "Running setup.js with mongo..." 23 | mongo --nodb ../testdb/init.js 24 | exit 0 25 | fi 26 | sleep 1 27 | done 28 | echo "Failed to start all processes. Check out what's up at $PWD now!" 29 | exit 1 30 | } 31 | 32 | stop() { 33 | if [ -d _testdb ]; then 34 | echo "Shutting down test cluster..." 35 | (cd _testdb && supervisorctl shutdown) 36 | rm -rf _testdb 37 | fi 38 | } 39 | 40 | 41 | if [ ! -f suite_test.go ]; then 42 | echo "This script must be run from within the source directory." 43 | exit 1 44 | fi 45 | 46 | case "$1" in 47 | 48 | start) 49 | start $2 50 | ;; 51 | 52 | stop) 53 | stop $2 54 | ;; 55 | 56 | esac 57 | 58 | # vim:ts=4:sw=4:et 59 | -------------------------------------------------------------------------------- /mongoreplay/mongoreplay.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package mongoreplay 8 | 9 | import ( 10 | mgo "github.com/mongodb-labs/mongoreplay/internal/llmgo" 11 | ) 12 | 13 | // Options stores settings for any mongoreplay command 14 | type Options struct { 15 | Verbosity []bool `short:"v" long:"verbosity" description:"increase the detail regarding the tools performance on the input file that is output to logs (include multiple times for increased logging verbosity, e.g. -vvv)"` 16 | Debug []bool `short:"d" long:"debug" description:"increase the detail regarding the operations and errors of the tool that is output to the logs(include multiple times for increased debugging information, e.g. -ddd)"` 17 | Silent bool `short:"s" long:"silent" description:"silence all log output"` 18 | 19 | VersionStr string 20 | GitCommit string 21 | } 22 | 23 | // SetLogging sets the verbosity/debug level for log output. 24 | func (opts *Options) SetLogging() { 25 | v := len(opts.Verbosity) 26 | d := len(opts.Debug) 27 | if opts.Silent { 28 | v = -1 29 | d = -1 30 | } 31 | userInfoLogger.setVerbosity(v) 32 | toolDebugLogger.setVerbosity(d) 33 | if d > 0 || v > 0 { 34 | printVersionInfo(opts.VersionStr, opts.GitCommit) 35 | } 36 | if d == DebugHigh { 37 | mgo.SetLogger(toolDebugLogger) 38 | mgo.SetDebug(true) 39 | } 40 | } 41 | 42 | type VersionOptions struct { 43 | Version bool `long:"version" description:"display the version and exit"` 44 | } 45 | -------------------------------------------------------------------------------- /internal/llmgo/txn/chaos.go: -------------------------------------------------------------------------------- 1 | package txn 2 | 3 | import ( 4 | mrand "math/rand" 5 | "time" 6 | ) 7 | 8 | var chaosEnabled = false 9 | var chaosSetting Chaos 10 | 11 | // Chaos holds parameters for the failure injection mechanism. 12 | type Chaos struct { 13 | // KillChance is the 0.0 to 1.0 chance that a given checkpoint 14 | // within the algorithm will raise an interruption that will 15 | // stop the procedure. 16 | KillChance float64 17 | 18 | // SlowdownChance is the 0.0 to 1.0 chance that a given checkpoint 19 | // within the algorithm will be delayed by Slowdown before 20 | // continuing. 21 | SlowdownChance float64 22 | Slowdown time.Duration 23 | 24 | // If Breakpoint is set, the above settings will only affect the 25 | // named breakpoint. 26 | Breakpoint string 27 | } 28 | 29 | // SetChaos sets the failure injection parameters to c. 30 | func SetChaos(c Chaos) { 31 | chaosSetting = c 32 | chaosEnabled = c.KillChance > 0 || c.SlowdownChance > 0 33 | } 34 | 35 | func chaos(bpname string) { 36 | if !chaosEnabled { 37 | return 38 | } 39 | switch chaosSetting.Breakpoint { 40 | case "", bpname: 41 | kc := chaosSetting.KillChance 42 | if kc > 0 && mrand.Intn(1000) < int(kc*1000) { 43 | panic(chaosError{}) 44 | } 45 | if bpname == "insert" { 46 | return 47 | } 48 | sc := chaosSetting.SlowdownChance 49 | if sc > 0 && mrand.Intn(1000) < int(sc*1000) { 50 | time.Sleep(chaosSetting.Slowdown) 51 | } 52 | } 53 | } 54 | 55 | type chaosError struct{} 56 | 57 | func (f *flusher) handleChaos(err *error) { 58 | v := recover() 59 | if v == nil { 60 | return 61 | } 62 | if _, ok := v.(chaosError); ok { 63 | f.debugf("Killed by chaos!") 64 | *err = ErrChaos 65 | return 66 | } 67 | panic(v) 68 | } 69 | -------------------------------------------------------------------------------- /internal/legacy/util/format_date_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package util 8 | 9 | import ( 10 | "github.com/mongodb-labs/mongoreplay/internal/legacy/testtype" 11 | . "github.com/smartystreets/goconvey/convey" 12 | "testing" 13 | ) 14 | 15 | func TestFormatDate(t *testing.T) { 16 | testtype.SkipUnlessTestType(t, testtype.UnitTestType) 17 | 18 | Convey("will take valid format 2006-01-02T15:04:05.000Z", t, func() { 19 | _, err := FormatDate("2014-01-02T15:04:05.000Z") 20 | So(err, ShouldBeNil) 21 | }) 22 | 23 | Convey("will take valid format 2006-01-02T15:04:05Z", t, func() { 24 | _, err := FormatDate("2014-03-02T15:05:05Z") 25 | So(err, ShouldBeNil) 26 | }) 27 | 28 | Convey("will take valid format 2006-01-02T15:04Z", t, func() { 29 | _, err := FormatDate("2014-04-02T15:04Z") 30 | So(err, ShouldBeNil) 31 | }) 32 | 33 | Convey("will take valid format 2006-01-02T15:04-0700", t, func() { 34 | _, err := FormatDate("2014-04-02T15:04-0800") 35 | So(err, ShouldBeNil) 36 | }) 37 | 38 | Convey("will take valid format 2006-01-02T15:04:05.000-0700", t, func() { 39 | _, err := FormatDate("2014-04-02T15:04:05.000-0600") 40 | So(err, ShouldBeNil) 41 | }) 42 | 43 | Convey("will take valid format 2006-01-02T15:04:05-0700", t, func() { 44 | _, err := FormatDate("2014-04-02T15:04:05-0500") 45 | So(err, ShouldBeNil) 46 | }) 47 | 48 | Convey("will return an error for an invalid format", t, func() { 49 | _, err := FormatDate("invalid string format") 50 | So(err, ShouldNotBeNil) 51 | }) 52 | 53 | } 54 | -------------------------------------------------------------------------------- /mongoreplay/unknown_op.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package mongoreplay 8 | 9 | import ( 10 | "fmt" 11 | "io" 12 | 13 | mgo "github.com/mongodb-labs/mongoreplay/internal/llmgo" 14 | ) 15 | 16 | // UnknownOp is not a real mongo Op but represents an unrecognized or corrupted op 17 | type UnknownOp struct { 18 | Header MsgHeader 19 | Body []byte 20 | } 21 | 22 | // Meta returns metadata about the UnknownOp, for which there is none 23 | func (op *UnknownOp) Meta() OpMetadata { 24 | return OpMetadata{"", "", "", nil} 25 | } 26 | 27 | func (op *UnknownOp) String() string { 28 | return fmt.Sprintf("OpUnkown: %v", op.Header.OpCode) 29 | } 30 | 31 | // Abbreviated returns a serialization of the UnknownOp, abbreviated so it 32 | // doesn't exceed the given number of characters. 33 | func (op *UnknownOp) Abbreviated(chars int) string { 34 | return fmt.Sprintf("%v", op) 35 | } 36 | 37 | // OpCode returns the OpCode for an UnknownOp. 38 | func (op *UnknownOp) OpCode() OpCode { 39 | return op.Header.OpCode 40 | } 41 | 42 | // FromReader extracts data from a serialized UnknownOp into its concrete structure. 43 | func (op *UnknownOp) FromReader(r io.Reader) error { 44 | if op.Header.MessageLength < MsgHeaderLen { 45 | return nil 46 | } 47 | op.Body = make([]byte, op.Header.MessageLength-MsgHeaderLen) 48 | _, err := io.ReadFull(r, op.Body) 49 | return err 50 | } 51 | 52 | // Execute doesn't do anything for an UnknownOp 53 | func (op *UnknownOp) Execute(session *mgo.Session) (*ReplyOp, error) { 54 | return nil, nil 55 | } 56 | -------------------------------------------------------------------------------- /internal/legacy/lldb/tlsgo/rootcerts_darwin.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2018-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | // Based on https://github.com/hashicorp/go-rootcerts by HashiCorp 8 | // See THIRD-PARTY-NOTICES for original license terms. 9 | 10 | package tlsgo 11 | 12 | import ( 13 | "crypto/x509" 14 | "os/exec" 15 | "os/user" 16 | "path" 17 | ) 18 | 19 | // loadSystemCAs has special behavior on Darwin systems to work around 20 | // bugs loading certs from keychains. See this GitHub issues query: 21 | // https://github.com/golang/go/issues?utf8=%E2%9C%93&q=is%3Aissue+darwin+keychain 22 | func loadSystemCAs() (*x509.CertPool, error) { 23 | pool := x509.NewCertPool() 24 | 25 | for _, keychain := range certKeychains() { 26 | err := addCertsFromKeychain(pool, keychain) 27 | if err != nil { 28 | return nil, err 29 | } 30 | } 31 | 32 | return pool, nil 33 | } 34 | 35 | func addCertsFromKeychain(pool *x509.CertPool, keychain string) error { 36 | cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", keychain) 37 | data, err := cmd.Output() 38 | if err != nil { 39 | return err 40 | } 41 | 42 | pool.AppendCertsFromPEM(data) 43 | 44 | return nil 45 | } 46 | 47 | func certKeychains() []string { 48 | keychains := []string{ 49 | "/System/Library/Keychains/SystemRootCertificates.keychain", 50 | "/Library/Keychains/System.keychain", 51 | } 52 | user, err := user.Current() 53 | if err == nil { 54 | loginKeychain := path.Join(user.HomeDir, "Library", "Keychains", "login.keychain") 55 | keychains = append(keychains, loginKeychain) 56 | } 57 | return keychains 58 | } 59 | -------------------------------------------------------------------------------- /mongoreplay/execute_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package mongoreplay 8 | 9 | import ( 10 | "testing" 11 | 12 | mgo "github.com/mongodb-labs/mongoreplay/internal/llmgo" 13 | ) 14 | 15 | func TestCompleteReply(t *testing.T) { 16 | context := NewExecutionContext(&StatCollector{}, nil, &ExecutionOptions{}) 17 | 18 | // AddFromWire takes a recorded request and a live reply to the re-execution 19 | // of that reply 20 | reply1 := &ReplyOp{} 21 | reply1.ReplyOp = mgo.ReplyOp{ 22 | CursorId: 2500, 23 | } 24 | recordedOp1 := &RecordedOp{ 25 | DstEndpoint: "a", 26 | SrcEndpoint: "b", 27 | RawOp: RawOp{ 28 | Header: MsgHeader{ 29 | RequestID: 1000, 30 | }, 31 | }, 32 | Generation: 0, 33 | } 34 | context.AddFromWire(reply1, recordedOp1) 35 | 36 | // AddFromFile takes a recorded reply and the contained reply 37 | reply2 := &ReplyOp{} 38 | reply2.ReplyOp = mgo.ReplyOp{ 39 | CursorId: 1500, 40 | } 41 | 42 | recordedOp2 := &RecordedOp{ 43 | DstEndpoint: "b", 44 | SrcEndpoint: "a", 45 | RawOp: RawOp{ 46 | Header: MsgHeader{ 47 | ResponseTo: 1000, 48 | }, 49 | }, 50 | Generation: 0, 51 | } 52 | context.AddFromFile(reply2, recordedOp2) 53 | if len(context.CompleteReplies) != 1 { 54 | t.Error("replies not completed") 55 | } 56 | context.handleCompletedReplies() 57 | 58 | cursorIDLookup, ok := context.CursorIDMap.GetCursor(1500, -1) 59 | if !ok { 60 | t.Error("can't find cursorID in map") 61 | } 62 | if cursorIDLookup != 2500 { 63 | t.Errorf("looked up cursorID is wrong: %v, should be 2500", cursorIDLookup) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /sanity_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PORT=27017 4 | STARTMONGO=false 5 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | PCAPFILE="$SCRIPT_DIR/mongoreplay/mongoreplay_test.out" 7 | 8 | while test $# -gt 0; do 9 | case "$1" in 10 | -f|--file) 11 | shift 12 | PCAPFILE="$1" 13 | shift 14 | ;; 15 | -p|--port) 16 | shift 17 | PORT="$1" 18 | shift 19 | ;; 20 | -m|--start-mongo) 21 | shift 22 | STARTMONGO=true 23 | ;; 24 | *) 25 | echo "Unknown arg: $1" 26 | exit 1 27 | esac 28 | done 29 | 30 | command -v mongoreplay >/dev/null 31 | if [ $? != 0 ]; then 32 | echo "mongoreplay must be in PATH" 33 | exit 1 34 | fi 35 | 36 | set -e 37 | set -o verbose 38 | 39 | OUTFILE="$(echo $PCAPFILE | cut -f 1 -d '.').playback" 40 | mongoreplay record -f $PCAPFILE -p $OUTFILE 41 | 42 | if [ "$STARTMONGO" = true ]; then 43 | rm -rf "$SCRIPT_DIR/data" 44 | mkdir "$SCRIPT_DIR/data" 45 | echo "starting MONGOD" 46 | mongod --port=$PORT --dbpath="$SCRIPT_DIR/data" & 47 | MONGOPID=$! 48 | fi 49 | 50 | mongo --port=$PORT mongoplay_test --eval "db.setProfilingLevel(2);" 51 | mongo --port=$PORT mongoplay_test --eval "db.createCollection('sanity_check', {});" 52 | 53 | export MONGOREPLAY_HOST="mongodb://localhost:$PORT" 54 | mongoreplay play -p $OUTFILE 55 | mongo --port=$PORT mongoplay_test --eval "var profile_results = db.system.profile.find({'ns':'mongoplay_test.sanity_check'}); 56 | assert.gt(profile_results.size(), 0);" 57 | 58 | mongo --port=$PORT mongoplay_test --eval "var query_results = db.sanity_check.find({'test_success':1}); 59 | assert.gt(query_results.size(), 0);" 60 | 61 | # test that files are correctly gziped ( TOOLS-1503 ) 62 | mongoreplay record -f $PCAPFILE -p ${OUTFILE} --gzip 63 | gunzip -t ${OUTFILE} 64 | 65 | echo "Success!" 66 | 67 | if [ "$STARTMONGO" = true ]; then 68 | kill $MONGOPID 69 | fi 70 | 71 | 72 | -------------------------------------------------------------------------------- /internal/llmgo/internal/sasl/sasl.c: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static int mgo_sasl_simple(void *context, int id, const char **result, unsigned int *len) 9 | { 10 | if (!result) { 11 | return SASL_BADPARAM; 12 | } 13 | switch (id) { 14 | case SASL_CB_USER: 15 | *result = (char *)context; 16 | break; 17 | case SASL_CB_AUTHNAME: 18 | *result = (char *)context; 19 | break; 20 | case SASL_CB_LANGUAGE: 21 | *result = NULL; 22 | break; 23 | default: 24 | return SASL_BADPARAM; 25 | } 26 | if (len) { 27 | *len = *result ? strlen(*result) : 0; 28 | } 29 | return SASL_OK; 30 | } 31 | 32 | typedef int (*callback)(void); 33 | 34 | static int mgo_sasl_secret(sasl_conn_t *conn, void *context, int id, sasl_secret_t **result) 35 | { 36 | if (!conn || !result || id != SASL_CB_PASS) { 37 | return SASL_BADPARAM; 38 | } 39 | *result = (sasl_secret_t *)context; 40 | return SASL_OK; 41 | } 42 | 43 | sasl_callback_t *mgo_sasl_callbacks(const char *username, const char *password) 44 | { 45 | sasl_callback_t *cb = malloc(4 * sizeof(sasl_callback_t)); 46 | int n = 0; 47 | 48 | size_t len = strlen(password); 49 | sasl_secret_t *secret = (sasl_secret_t*)malloc(sizeof(sasl_secret_t) + len); 50 | if (!secret) { 51 | free(cb); 52 | return NULL; 53 | } 54 | strcpy((char *)secret->data, password); 55 | secret->len = len; 56 | 57 | cb[n].id = SASL_CB_PASS; 58 | cb[n].proc = (callback)&mgo_sasl_secret; 59 | cb[n].context = secret; 60 | n++; 61 | 62 | cb[n].id = SASL_CB_USER; 63 | cb[n].proc = (callback)&mgo_sasl_simple; 64 | cb[n].context = (char*)username; 65 | n++; 66 | 67 | cb[n].id = SASL_CB_AUTHNAME; 68 | cb[n].proc = (callback)&mgo_sasl_simple; 69 | cb[n].context = (char*)username; 70 | n++; 71 | 72 | cb[n].id = SASL_CB_LIST_END; 73 | cb[n].proc = NULL; 74 | cb[n].context = NULL; 75 | 76 | return cb; 77 | } 78 | -------------------------------------------------------------------------------- /mongoreplay/recorded_op.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package mongoreplay 8 | 9 | // RecordedOp stores an op in addition to record/playback -related metadata 10 | type RecordedOp struct { 11 | RawOp 12 | Seen *PreciseTime 13 | PlayAt *PreciseTime `bson:",omitempty"` 14 | EOF bool `bson:",omitempty"` 15 | SrcEndpoint string 16 | DstEndpoint string 17 | SeenConnectionNum int64 18 | PlayedConnectionNum int64 19 | PlayedAt *PreciseTime `bson:",omitempty"` 20 | Generation int 21 | Order int64 22 | } 23 | 24 | // ConnectionString gives a serialized representation of the endpoints 25 | func (op *RecordedOp) ConnectionString() string { 26 | return op.SrcEndpoint + "->" + op.DstEndpoint 27 | } 28 | 29 | // ReversedConnectionString gives a serialized representation of the endpoints, 30 | // in reversed order 31 | func (op *RecordedOp) ReversedConnectionString() string { 32 | return op.DstEndpoint + "->" + op.SrcEndpoint 33 | } 34 | 35 | type orderedOps []RecordedOp 36 | 37 | func (o orderedOps) Len() int { 38 | return len(o) 39 | } 40 | 41 | func (o orderedOps) Less(i, j int) bool { 42 | return o[i].Seen.Before(o[j].Seen.Time) 43 | } 44 | 45 | func (o orderedOps) Swap(i, j int) { 46 | o[i], o[j] = o[j], o[i] 47 | } 48 | 49 | func (o *orderedOps) Pop() interface{} { 50 | i := len(*o) - 1 51 | op := (*o)[i] 52 | *o = (*o)[:i] 53 | return op 54 | } 55 | 56 | func (o *orderedOps) Push(op interface{}) { 57 | *o = append(*o, op.(RecordedOp)) 58 | } 59 | 60 | type opKey struct { 61 | driverEndpoint, serverEndpoint string 62 | opID int32 63 | } 64 | -------------------------------------------------------------------------------- /internal/legacy/json/objectid.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | "reflect" 12 | ) 13 | 14 | // Transition functions for recognizing ObjectId. 15 | // Adapted from encoding/json/scanner.go. 16 | 17 | // stateO is the state after reading `O`. 18 | func stateO(s *scanner, c int) int { 19 | if c == 'b' { 20 | s.step = generateState("ObjectId", []byte("jectId"), stateConstructor) 21 | return scanContinue 22 | } 23 | return s.error(c, "in literal ObjectId (expecting 'b')") 24 | } 25 | 26 | // Decodes an ObjectId literal stored in the underlying byte data into v. 27 | func (d *decodeState) storeObjectId(v reflect.Value) { 28 | op := d.scanWhile(scanSkipSpace) 29 | if op != scanBeginCtor { 30 | d.error(fmt.Errorf("expected beginning of constructor")) 31 | } 32 | 33 | args, err := d.ctor("ObjectId", []reflect.Type{objectIdType}) 34 | if err != nil { 35 | d.error(err) 36 | } 37 | switch kind := v.Kind(); kind { 38 | case reflect.Interface: 39 | v.Set(args[0]) 40 | default: 41 | d.error(fmt.Errorf("cannot store %v value into %v type", objectIdType, kind)) 42 | } 43 | } 44 | 45 | // Returns an ObjectId literal from the underlying byte data. 46 | func (d *decodeState) getObjectId() interface{} { 47 | op := d.scanWhile(scanSkipSpace) 48 | if op != scanBeginCtor { 49 | d.error(fmt.Errorf("expected beginning of constructor")) 50 | } 51 | 52 | args := d.ctorInterface() 53 | if err := ctorNumArgsMismatch("ObjectId", 1, len(args)); err != nil { 54 | d.error(err) 55 | } 56 | arg0, ok := args[0].(string) 57 | if !ok { 58 | d.error(fmt.Errorf("expected string for first argument of ObjectId constructor")) 59 | } 60 | return ObjectId(arg0) 61 | } 62 | -------------------------------------------------------------------------------- /internal/legacy/util/slice.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package util 8 | 9 | import ( 10 | "fmt" 11 | "reflect" 12 | ) 13 | 14 | // generic function that returns true if elt is in slice. 15 | // panics if slice is not of Kind reflect.Slice 16 | func SliceContains(slice, elt interface{}) bool { 17 | if slice == nil { 18 | return false 19 | } 20 | v := reflect.ValueOf(slice) 21 | if v.Kind() != reflect.Slice { 22 | panic(fmt.Sprintf("Cannot call SliceContains on a non-slice %#v of "+ 23 | "kind %#v", slice, v.Kind().String())) 24 | } 25 | for i := 0; i < v.Len(); i++ { 26 | if reflect.DeepEqual(v.Index(i).Interface(), elt) { 27 | return true 28 | } 29 | } 30 | return false 31 | } 32 | 33 | // StringSliceContains reports whether str is in the slice. 34 | func StringSliceContains(slice []string, str string) bool { 35 | return StringSliceIndex(slice, str) != -1 36 | } 37 | 38 | // StringSliceContains returns the first index at which the given element 39 | // can be found in the slice, or -1 if it is not present. 40 | func StringSliceIndex(slice []string, str string) int { 41 | i := -1 42 | for j, v := range slice { 43 | if v == str { 44 | i = j 45 | break 46 | } 47 | } 48 | return i 49 | } 50 | 51 | // generic function that returns number of instances of 'elt' in 'slice'. 52 | // panics if slice is not of Kind reflect.Slice 53 | func SliceCount(slice, elt interface{}) int { 54 | v := reflect.ValueOf(slice) 55 | if v.Kind() != reflect.Slice { 56 | panic(fmt.Sprintf("Cannot call SliceCount on a non-slice %#v of kind "+ 57 | "%#v", slice, v.Kind().String())) 58 | } 59 | counter := 0 60 | for i := 0; i < v.Len(); i++ { 61 | if reflect.DeepEqual(v.Index(i).Interface(), elt) { 62 | counter++ 63 | } 64 | } 65 | return counter 66 | } 67 | -------------------------------------------------------------------------------- /internal/legacy/password/password.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | // Package password handles cleanly reading in a user's password from 8 | // the command line. This varies heavily between operating systems. 9 | package password 10 | 11 | import ( 12 | "fmt" 13 | "github.com/mongodb-labs/mongoreplay/internal/legacy/log" 14 | "os" 15 | ) 16 | 17 | // key constants 18 | const ( 19 | backspaceKey = 8 20 | deleteKey = 127 21 | eotKey = 3 22 | eofKey = 4 23 | newLineKey = 10 24 | carriageReturnKey = 13 25 | ) 26 | 27 | // Prompt displays a prompt asking for the password and returns the 28 | // password the user enters as a string. 29 | func Prompt() string { 30 | var pass string 31 | if IsTerminal() { 32 | log.Logv(log.DebugLow, "standard input is a terminal; reading password from terminal") 33 | fmt.Fprintf(os.Stderr, "Enter password:") 34 | pass = GetPass() 35 | } else { 36 | log.Logv(log.Always, "reading password from standard input") 37 | fmt.Fprintf(os.Stderr, "Enter password:") 38 | pass = readPassFromStdin() 39 | } 40 | fmt.Fprintln(os.Stderr) 41 | return pass 42 | } 43 | 44 | // readPassFromStdin pipes in a password from stdin if 45 | // we aren't using a terminal for standard input 46 | func readPassFromStdin() string { 47 | pass := []byte{} 48 | for { 49 | var chBuf [1]byte 50 | n, err := os.Stdin.Read(chBuf[:]) 51 | if err != nil { 52 | panic(err) 53 | } 54 | if n == 0 { 55 | break 56 | } 57 | ch := chBuf[0] 58 | if ch == backspaceKey || ch == deleteKey { 59 | if len(pass) > 0 { 60 | pass = pass[:len(pass)-1] 61 | } 62 | } else if ch == carriageReturnKey || ch == newLineKey || ch == eotKey || ch == eofKey { 63 | break 64 | } else if ch != 0 { 65 | pass = append(pass, ch) 66 | } 67 | } 68 | return string(pass) 69 | } 70 | -------------------------------------------------------------------------------- /mongoreplay/log_wrapper.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package mongoreplay 8 | 9 | import ( 10 | "log" 11 | "os" 12 | ) 13 | 14 | const ( 15 | // Always denotes that a log be performed without needing any verbosity 16 | Always = iota 17 | // Info denotes that a log be performed with verbosity level 1 (-v) 18 | Info 19 | // DebugLow denotes that a log be performed with verbosity level 2 (-vv) 20 | DebugLow 21 | // DebugHigh denotes that a log be performed with verbosity level 3 (-vvv) 22 | DebugHigh 23 | ) 24 | 25 | var logger *log.Logger 26 | var userInfoLogger *logWrapper 27 | var toolDebugLogger *logWrapper 28 | 29 | type logWrapper struct { 30 | out *log.Logger 31 | verbosity int 32 | } 33 | 34 | func init() { 35 | if logger == nil { 36 | logger = log.New(os.Stderr, "", log.Ldate|log.Ltime) 37 | } 38 | if userInfoLogger == nil { 39 | userInfoLogger = &logWrapper{logger, 0} 40 | } 41 | if toolDebugLogger == nil { 42 | toolDebugLogger = &logWrapper{logger, 0} 43 | } 44 | } 45 | 46 | func (lw *logWrapper) setVerbosity(verbosity int) { 47 | lw.verbosity = verbosity 48 | } 49 | 50 | func (lw *logWrapper) Logvf(minVerb int, format string, a ...interface{}) { 51 | if minVerb < 0 { 52 | panic("cannot set a minimum log verbosity that is less than 0") 53 | } 54 | 55 | if minVerb <= lw.verbosity { 56 | lw.out.Printf(format, a...) 57 | } 58 | } 59 | 60 | func (lw *logWrapper) Logv(minVerb int, msg string) { 61 | if minVerb < 0 { 62 | panic("cannot set a minimum log verbosity that is less than 0") 63 | } 64 | 65 | if minVerb <= lw.verbosity { 66 | lw.out.Print(msg) 67 | } 68 | } 69 | func (lw *logWrapper) isInVerbosity(minVerb int) bool { 70 | return minVerb <= lw.verbosity 71 | } 72 | 73 | func (lw *logWrapper) Output(calldepth int, s string) error { 74 | lw.Logv(DebugHigh, s) 75 | return nil 76 | } 77 | -------------------------------------------------------------------------------- /internal/llmgo/internal/scram/scram_test.go: -------------------------------------------------------------------------------- 1 | package scram_test 2 | 3 | import ( 4 | "crypto/sha1" 5 | "testing" 6 | 7 | . "gopkg.in/check.v1" 8 | "github.com/mongodb-labs/mongoreplay/internal/llmgo/internal/scram" 9 | "strings" 10 | ) 11 | 12 | var _ = Suite(&S{}) 13 | 14 | func Test(t *testing.T) { TestingT(t) } 15 | 16 | type S struct{} 17 | 18 | var tests = [][]string{{ 19 | "U: user pencil", 20 | "N: fyko+d2lbbFgONRv9qkxdawL", 21 | "C: n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL", 22 | "S: r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096", 23 | "C: c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=", 24 | "S: v=rmF9pqV8S7suAoZWja4dJRkFsKQ=", 25 | }, { 26 | "U: root fe8c89e308ec08763df36333cbf5d3a2", 27 | "N: OTcxNDk5NjM2MzE5", 28 | "C: n,,n=root,r=OTcxNDk5NjM2MzE5", 29 | "S: r=OTcxNDk5NjM2MzE581Ra3provgG0iDsMkDiIAlrh4532dDLp,s=XRDkVrFC9JuL7/F4tG0acQ==,i=10000", 30 | "C: c=biws,r=OTcxNDk5NjM2MzE581Ra3provgG0iDsMkDiIAlrh4532dDLp,p=6y1jp9R7ETyouTXS9fW9k5UHdBc=", 31 | "S: v=LBnd9dUJRxdqZiEq91NKP3z/bHA=", 32 | }} 33 | 34 | func (s *S) TestExamples(c *C) { 35 | for _, steps := range tests { 36 | if len(steps) < 2 || len(steps[0]) < 3 || !strings.HasPrefix(steps[0], "U: ") { 37 | c.Fatalf("Invalid test: %#v", steps) 38 | } 39 | auth := strings.Fields(steps[0][3:]) 40 | client := scram.NewClient(sha1.New, auth[0], auth[1]) 41 | first, done := true, false 42 | c.Logf("-----") 43 | c.Logf("%s", steps[0]) 44 | for _, step := range steps[1:] { 45 | c.Logf("%s", step) 46 | switch step[:3] { 47 | case "N: ": 48 | client.SetNonce([]byte(step[3:])) 49 | case "C: ": 50 | if first { 51 | first = false 52 | done = client.Step(nil) 53 | } 54 | c.Assert(done, Equals, false) 55 | c.Assert(client.Err(), IsNil) 56 | c.Assert(string(client.Out()), Equals, step[3:]) 57 | case "S: ": 58 | first = false 59 | done = client.Step([]byte(step[3:])) 60 | default: 61 | panic("invalid test line: " + step) 62 | } 63 | } 64 | c.Assert(done, Equals, true) 65 | c.Assert(client.Err(), IsNil) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /internal/llmgo/queue_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2015-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | // Based on gopkg.io/mgo.v2 by Gustavo Niemeyer. 8 | // See THIRD-PARTY-NOTICES for original license terms. 9 | 10 | package mgo 11 | 12 | import ( 13 | . "gopkg.in/check.v1" 14 | ) 15 | 16 | type QS struct{} 17 | 18 | var _ = Suite(&QS{}) 19 | 20 | func (s *QS) TestSequentialGrowth(c *C) { 21 | q := queue{} 22 | n := 2048 23 | for i := 0; i != n; i++ { 24 | q.Push(i) 25 | } 26 | for i := 0; i != n; i++ { 27 | c.Assert(q.Pop(), Equals, i) 28 | } 29 | } 30 | 31 | var queueTestLists = [][]int{ 32 | // {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 33 | {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 34 | 35 | // {8, 9, 10, 11, ... 2, 3, 4, 5, 6, 7} 36 | {0, 1, 2, 3, 4, 5, 6, 7, -1, -1, 8, 9, 10, 11}, 37 | 38 | // {8, 9, 10, 11, ... 2, 3, 4, 5, 6, 7} 39 | {0, 1, 2, 3, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11}, 40 | 41 | // {0, 1, 2, 3, 4, 5, 6, 7, 8} 42 | {0, 1, 2, 3, 4, 5, 6, 7, 8, 43 | -1, -1, -1, -1, -1, -1, -1, -1, -1, 44 | 0, 1, 2, 3, 4, 5, 6, 7, 8}, 45 | } 46 | 47 | func (s *QS) TestQueueTestLists(c *C) { 48 | test := []int{} 49 | testi := 0 50 | reset := func() { 51 | test = test[0:0] 52 | testi = 0 53 | } 54 | push := func(i int) { 55 | test = append(test, i) 56 | } 57 | pop := func() (i int) { 58 | if testi == len(test) { 59 | return -1 60 | } 61 | i = test[testi] 62 | testi++ 63 | return 64 | } 65 | 66 | for _, list := range queueTestLists { 67 | reset() 68 | q := queue{} 69 | for _, n := range list { 70 | if n == -1 { 71 | c.Assert(q.Pop(), Equals, pop(), Commentf("With list %#v", list)) 72 | } else { 73 | q.Push(n) 74 | push(n) 75 | } 76 | } 77 | 78 | for n := pop(); n != -1; n = pop() { 79 | c.Assert(q.Pop(), Equals, n, Commentf("With list %#v", list)) 80 | } 81 | 82 | c.Assert(q.Pop(), Equals, nil, Commentf("With list %#v", list)) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /internal/llmgo/queue.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2015-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | // Based on gopkg.io/mgo.v2 by Gustavo Niemeyer. 8 | // See THIRD-PARTY-NOTICES for original license terms. 9 | 10 | package mgo 11 | 12 | type queue struct { 13 | elems []interface{} 14 | nelems, popi, pushi int 15 | } 16 | 17 | func (q *queue) Len() int { 18 | return q.nelems 19 | } 20 | 21 | func (q *queue) Push(elem interface{}) { 22 | //debugf("Pushing(pushi=%d popi=%d cap=%d): %#v\n", 23 | // q.pushi, q.popi, len(q.elems), elem) 24 | if q.nelems == len(q.elems) { 25 | q.expand() 26 | } 27 | q.elems[q.pushi] = elem 28 | q.nelems++ 29 | q.pushi = (q.pushi + 1) % len(q.elems) 30 | //debugf(" Pushed(pushi=%d popi=%d cap=%d): %#v\n", 31 | // q.pushi, q.popi, len(q.elems), elem) 32 | } 33 | 34 | func (q *queue) Pop() (elem interface{}) { 35 | //debugf("Popping(pushi=%d popi=%d cap=%d)\n", 36 | // q.pushi, q.popi, len(q.elems)) 37 | if q.nelems == 0 { 38 | return nil 39 | } 40 | elem = q.elems[q.popi] 41 | q.elems[q.popi] = nil // Help GC. 42 | q.nelems-- 43 | q.popi = (q.popi + 1) % len(q.elems) 44 | //debugf(" Popped(pushi=%d popi=%d cap=%d): %#v\n", 45 | // q.pushi, q.popi, len(q.elems), elem) 46 | return elem 47 | } 48 | 49 | func (q *queue) expand() { 50 | curcap := len(q.elems) 51 | var newcap int 52 | if curcap == 0 { 53 | newcap = 8 54 | } else if curcap < 1024 { 55 | newcap = curcap * 2 56 | } else { 57 | newcap = curcap + (curcap / 4) 58 | } 59 | elems := make([]interface{}, newcap) 60 | 61 | if q.popi == 0 { 62 | copy(elems, q.elems) 63 | q.pushi = curcap 64 | } else { 65 | newpopi := newcap - (curcap - q.popi) 66 | copy(elems, q.elems[:q.popi]) 67 | copy(elems[newpopi:], q.elems[q.popi:]) 68 | q.popi = newpopi 69 | } 70 | for i := range q.elems { 71 | q.elems[i] = nil // Help GC. 72 | } 73 | q.elems = elems 74 | } 75 | -------------------------------------------------------------------------------- /internal/legacy/util/file.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package util 8 | 9 | import ( 10 | "bufio" 11 | "io" 12 | "net/url" 13 | "os" 14 | "path/filepath" 15 | ) 16 | 17 | // GetFieldsFromFile fetches the first line from the contents of the file 18 | // at "path" 19 | func GetFieldsFromFile(path string) ([]string, error) { 20 | fieldFileReader, err := os.Open(path) 21 | if err != nil { 22 | return nil, err 23 | } 24 | defer fieldFileReader.Close() 25 | 26 | var fields []string 27 | fieldScanner := bufio.NewScanner(fieldFileReader) 28 | for fieldScanner.Scan() { 29 | fields = append(fields, fieldScanner.Text()) 30 | } 31 | if err := fieldScanner.Err(); err != nil { 32 | return nil, err 33 | } 34 | return fields, nil 35 | } 36 | 37 | // ToUniversalPath returns the result of replacing each slash ('/') character 38 | // in "path" with an OS-specific separator character. Multiple slashes are 39 | // replaced by multiple separators 40 | func ToUniversalPath(path string) string { 41 | return filepath.FromSlash(path) 42 | } 43 | 44 | func EscapeCollectionName(collName string) string { 45 | return url.PathEscape(collName) 46 | } 47 | 48 | func UnescapeCollectionName(escapedCollName string) (string, error) { 49 | return url.PathUnescape(escapedCollName) 50 | } 51 | 52 | type WrappedReadCloser struct { 53 | io.ReadCloser 54 | Inner io.ReadCloser 55 | } 56 | 57 | func (wrc *WrappedReadCloser) Close() error { 58 | outerErr := wrc.ReadCloser.Close() 59 | innerErr := wrc.Inner.Close() 60 | if outerErr != nil { 61 | return outerErr 62 | } 63 | return innerErr 64 | } 65 | 66 | type WrappedWriteCloser struct { 67 | io.WriteCloser 68 | Inner io.WriteCloser 69 | } 70 | 71 | func (wwc *WrappedWriteCloser) Close() error { 72 | outerErr := wwc.WriteCloser.Close() 73 | innerErr := wwc.Inner.Close() 74 | if outerErr != nil { 75 | return outerErr 76 | } 77 | return innerErr 78 | } 79 | -------------------------------------------------------------------------------- /internal/legacy/util/bool_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package util 8 | 9 | import ( 10 | "github.com/mongodb-labs/mongoreplay/internal/legacy/testtype" 11 | . "github.com/smartystreets/goconvey/convey" 12 | "gopkg.in/mgo.v2/bson" 13 | "math" 14 | "testing" 15 | ) 16 | 17 | func TestJSTruthyValues(t *testing.T) { 18 | testtype.SkipUnlessTestType(t, testtype.UnitTestType) 19 | Convey("With some sample values", t, func() { 20 | Convey("known server code edge cases are correct", func() { 21 | Convey("true -> true", func() { 22 | So(IsTruthy(true), ShouldBeTrue) 23 | }) 24 | Convey("{} -> true", func() { 25 | var myMap map[string]interface{} 26 | So(IsTruthy(myMap), ShouldBeTrue) 27 | myMap = map[string]interface{}{"a": 1} 28 | So(IsTruthy(myMap), ShouldBeTrue) 29 | }) 30 | Convey("[] -> true", func() { 31 | var mySlice []byte 32 | So(IsTruthy(mySlice), ShouldBeTrue) 33 | mySlice = []byte{21, 12} 34 | So(IsTruthy(mySlice), ShouldBeTrue) 35 | }) 36 | Convey(`"" -> true`, func() { 37 | So(IsTruthy(""), ShouldBeTrue) 38 | }) 39 | Convey("false -> false", func() { 40 | So(IsTruthy(false), ShouldBeFalse) 41 | }) 42 | Convey("0 -> false", func() { 43 | So(IsTruthy(0), ShouldBeFalse) 44 | }) 45 | Convey("0.0 -> false", func() { 46 | So(IsTruthy(float64(0)), ShouldBeFalse) 47 | }) 48 | Convey("nil -> false", func() { 49 | So(IsTruthy(nil), ShouldBeFalse) 50 | }) 51 | Convey("undefined -> false", func() { 52 | So(IsTruthy(bson.Undefined), ShouldBeFalse) 53 | }) 54 | }) 55 | 56 | Convey("and an assortment of non-edge cases are correct", func() { 57 | So(IsTruthy([]int{1, 2, 3}), ShouldBeTrue) 58 | So(IsTruthy("true"), ShouldBeTrue) 59 | So(IsTruthy("false"), ShouldBeTrue) 60 | So(IsTruthy(25), ShouldBeTrue) 61 | So(IsTruthy(math.NaN()), ShouldBeTrue) 62 | So(IsTruthy(25.1), ShouldBeTrue) 63 | So(IsTruthy(struct{ A int }{A: 12}), ShouldBeTrue) 64 | }) 65 | }) 66 | } 67 | -------------------------------------------------------------------------------- /internal/llmgo/testdb/dropall.js: -------------------------------------------------------------------------------- 1 | 2 | var ports = [40001, 40002, 40011, 40012, 40013, 40021, 40022, 40023, 40041, 40101, 40102, 40103, 40201, 40202, 40203] 3 | var auth = [40002, 40103, 40203, 40031] 4 | var db1 = new Mongo("localhost:40001") 5 | 6 | if (db1.getDB("admin").serverBuildInfo().OpenSSLVersion) { 7 | ports.push(40003) 8 | auth.push(40003) 9 | } 10 | 11 | for (var i in ports) { 12 | var port = ports[i] 13 | var server = "localhost:" + port 14 | var mongo = new Mongo("localhost:" + port) 15 | var admin = mongo.getDB("admin") 16 | 17 | for (var j in auth) { 18 | if (auth[j] == port) { 19 | admin.auth("root", "rapadura") 20 | admin.system.users.find().forEach(function(u) { 21 | if (u.user == "root" || u.user == "reader") { 22 | return; 23 | } 24 | if (typeof admin.dropUser == "function") { 25 | mongo.getDB(u.db).dropUser(u.user); 26 | } else { 27 | admin.removeUser(u.user); 28 | } 29 | }) 30 | break 31 | } 32 | } 33 | var result = admin.runCommand({"listDatabases": 1}) 34 | for (var j = 0; j != 100; j++) { 35 | if (typeof result.databases != "undefined" || notMaster(result)) { 36 | break 37 | } 38 | result = admin.runCommand({"listDatabases": 1}) 39 | } 40 | if (notMaster(result)) { 41 | continue 42 | } 43 | if (typeof result.databases == "undefined") { 44 | print("Could not list databases. Command result:") 45 | print(JSON.stringify(result)) 46 | quit(12) 47 | } 48 | var dbs = result.databases 49 | for (var j = 0; j != dbs.length; j++) { 50 | var db = dbs[j] 51 | switch (db.name) { 52 | case "admin": 53 | case "local": 54 | case "config": 55 | break 56 | default: 57 | mongo.getDB(db.name).dropDatabase() 58 | } 59 | } 60 | } 61 | 62 | function notMaster(result) { 63 | return typeof result.errmsg != "undefined" && (result.errmsg.indexOf("not master") >= 0 || result.errmsg.indexOf("no master found")) 64 | } 65 | 66 | // vim:ts=4:sw=4:et 67 | -------------------------------------------------------------------------------- /internal/legacy/testtype/types.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package testtype 8 | 9 | import ( 10 | "os" 11 | "testing" 12 | ) 13 | 14 | const ( 15 | // Integration tests require a mongod running on localhost:33333. If your 16 | // mongod uses SSL you need to specify the "ssl" type below, and ditto for 17 | // if your mongod requires auth. 18 | // First checks for a URI for a Mongod in the env variable TOOLS_TESTING_MONGOD. If it does not find it, looks on localhost:33333 19 | IntegrationTestType = "TOOLS_TESTING_INTEGRATION" 20 | 21 | // Unit tests don't require a real mongod. They may still do file I/O. 22 | UnitTestType = "TOOLS_TESTING_UNIT" 23 | 24 | // Kerberos tests are a special type of integration test that test tools 25 | // with Kerberos authentication against the drivers Kerberos testing cluster 26 | // because setting up a KDC every time is too brittle and expensive. 27 | // (See https://wiki.mongodb.com/display/DH/Testing+Kerberos) 28 | KerberosTestType = "TOOLS_TESTING_KERBEROS" 29 | 30 | // "TOOLS_TESTING_SSL" and "TOOLS_TESTING_AUTH" are used to configure integration tests to run against 31 | // different mongod configurations. "TOOLS_TESTING_SSL" will configure the integration tests 32 | // to expect an SSL-enabled mongod on localhost:33333. "TOOLS_TESTING_AUTH" will do the same 33 | // for an auth-enabled mongod on localhost:33333. 34 | SSLTestType = "TOOLS_TESTING_SSL" 35 | AuthTestType = "TOOLS_TESTING_AUTH" 36 | 37 | // For now mongoreplay tests are unique, and will have to be explicitly run. 38 | MongoReplayTestType = "TOOLS_TESTING_REPLAY" 39 | ) 40 | 41 | func HasTestType(testType string) bool { 42 | envVal := os.Getenv(testType) 43 | return envVal == "true" 44 | } 45 | 46 | // Skip the test if the specified type is not being run. 47 | func SkipUnlessTestType(t *testing.T, testType string) { 48 | if !HasTestType(testType) { 49 | t.SkipNow() 50 | } 51 | } 52 | 53 | func SkipUnlessBenchmarkType(b *testing.B, testType string) { 54 | if !HasTestType(testType) { 55 | b.SkipNow() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /internal/legacy/json/boolean.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | "reflect" 12 | ) 13 | 14 | // Transition functions for recognizing Boolean. 15 | // Adapted from encoding/json/scanner.go. 16 | 17 | // stateBo is the state after reading `Bo`. 18 | func stateBo(s *scanner, c int) int { 19 | if c == 'o' { 20 | s.step = generateState("Boolean", []byte("lean"), stateConstructor) 21 | return scanContinue 22 | } 23 | return s.error(c, "in literal Boolean (expecting 'o')") 24 | } 25 | 26 | // Decodes a Boolean literal stored in the underlying byte data into v. 27 | func (d *decodeState) storeBoolean(v reflect.Value) { 28 | res := d.getBoolean() 29 | switch kind := v.Kind(); kind { 30 | case reflect.Interface, reflect.Bool: 31 | v.Set(reflect.ValueOf(res)) 32 | default: 33 | d.error(fmt.Errorf("cannot store bool value into %v type", kind)) 34 | } 35 | } 36 | 37 | // Returns a Boolean literal from the underlying byte data. 38 | func (d *decodeState) getBoolean() interface{} { 39 | op := d.scanWhile(scanSkipSpace) 40 | if op != scanBeginCtor { 41 | d.error(fmt.Errorf("expected beginning of constructor")) 42 | } 43 | 44 | // Prevent d.convertNumber() from parsing the argument as a float64. 45 | useNumber := d.useNumber 46 | d.useNumber = true 47 | 48 | args := d.ctorInterface() 49 | if len(args) == 0 { 50 | return false 51 | } 52 | 53 | // Ignore all but the first argument. 54 | switch v := args[0].(type) { 55 | case bool: 56 | return v 57 | case Number: 58 | d.useNumber = useNumber 59 | 60 | // First try Int64 so hex numbers work, then if that fails try Float64. 61 | num, err := v.Int64() 62 | if err == nil { 63 | return (num != 0) 64 | } 65 | 66 | numF, err := v.Float64() 67 | if err != nil { 68 | d.error(fmt.Errorf("expected float64 for numeric argument of Boolean constructor, got err: %v", err)) 69 | } 70 | return (numF != 0) 71 | case string: 72 | return (v != "") 73 | case Undefined, nil: 74 | return false 75 | // Parameter values of any other types should yield true. 76 | default: 77 | return true 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /internal/legacy/json/bindata.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | "reflect" 12 | ) 13 | 14 | // Transition functions for recognizing BinData. 15 | // Adapted from encoding/json/scanner.go. 16 | 17 | // stateBi is the state after reading `Bi`. 18 | func stateBi(s *scanner, c int) int { 19 | if c == 'n' { 20 | s.step = generateState("BinData", []byte("Data"), stateConstructor) 21 | return scanContinue 22 | } 23 | return s.error(c, "in literal BinData (expecting 'n')") 24 | } 25 | 26 | // Decodes a BinData literal stored in the underlying byte data into v. 27 | func (d *decodeState) storeBinData(v reflect.Value) { 28 | op := d.scanWhile(scanSkipSpace) 29 | if op != scanBeginCtor { 30 | d.error(fmt.Errorf("expected beginning of constructor")) 31 | } 32 | 33 | args, err := d.ctor("BinData", []reflect.Type{byteType, stringType}) 34 | if err != nil { 35 | d.error(err) 36 | } 37 | switch kind := v.Kind(); kind { 38 | case reflect.Interface: 39 | arg0 := byte(args[0].Uint()) 40 | arg1 := args[1].String() 41 | v.Set(reflect.ValueOf(BinData{arg0, arg1})) 42 | default: 43 | d.error(fmt.Errorf("cannot store %v value into %v type", binDataType, kind)) 44 | } 45 | } 46 | 47 | // Returns a BinData literal from the underlying byte data. 48 | func (d *decodeState) getBinData() interface{} { 49 | op := d.scanWhile(scanSkipSpace) 50 | if op != scanBeginCtor { 51 | d.error(fmt.Errorf("expected beginning of constructor")) 52 | } 53 | 54 | // Prevent d.convertNumber() from parsing the argument as a float64. 55 | useNumber := d.useNumber 56 | d.useNumber = true 57 | 58 | args := d.ctorInterface() 59 | if err := ctorNumArgsMismatch("BinData", 2, len(args)); err != nil { 60 | d.error(err) 61 | } 62 | arg0, err := args[0].(Number).Uint8() 63 | if err != nil { 64 | d.error(fmt.Errorf("expected byte for first argument of BinData constructor")) 65 | } 66 | arg1, ok := args[1].(string) 67 | if !ok { 68 | d.error(fmt.Errorf("expected string for second argument of BinData constructor")) 69 | } 70 | 71 | d.useNumber = useNumber 72 | return BinData{arg0, arg1} 73 | } 74 | -------------------------------------------------------------------------------- /internal/legacy/json/csv_format.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "encoding/base64" 11 | "fmt" 12 | "time" 13 | ) 14 | 15 | const CSV_DATE_FORMAT = "2006-01-02T15:04:05.000Z" 16 | 17 | func (b BinData) String() string { 18 | data, err := base64.StdEncoding.DecodeString(b.Base64) 19 | if err != nil { 20 | return "" // XXX: panic? 21 | } 22 | if b.Type == 0x02 { 23 | data = data[4:] // skip the first 4 bytes 24 | } 25 | return fmt.Sprintf("%X", data) // use uppercase hexadecimal 26 | } 27 | 28 | func (js JavaScript) String() string { 29 | return js.Code 30 | } 31 | 32 | func (d Date) String() string { 33 | if d.isFormatable() { 34 | n := int64(d) 35 | t := time.Unix(n/1e3, n%1e3*1e6) 36 | return t.UTC().Format(JSON_DATE_FORMAT) 37 | } 38 | // date.MarshalJSON always returns a nil err. 39 | data, _ := d.MarshalJSON() 40 | return string(data) 41 | } 42 | 43 | func (d DBRef) String() string { 44 | return fmt.Sprintf(`{ "$ref": "%v", "$id": %v, "$db": "%v" }`, 45 | d.Collection, d.Id, d.Database) 46 | } 47 | 48 | func (d DBPointer) String() string { 49 | return fmt.Sprintf(`{ "$ref": "%v", "$id": %v }`, 50 | d.Namespace, d.Id) 51 | } 52 | 53 | func (f Float) String() string { 54 | return fmt.Sprintf("%v", float64(f)) 55 | } 56 | 57 | func (_ MinKey) String() string { 58 | return "$MinKey" 59 | } 60 | 61 | func (_ MaxKey) String() string { 62 | return "$MaxKey" 63 | } 64 | 65 | func (n NumberInt) String() string { 66 | return fmt.Sprintf("%v", int32(n)) 67 | } 68 | 69 | func (n NumberLong) String() string { 70 | return fmt.Sprintf("%v", int64(n)) 71 | } 72 | 73 | // Assumes that o represents a valid ObjectId 74 | // (composed of 24 hexadecimal characters). 75 | func (o ObjectId) String() string { 76 | return fmt.Sprintf("ObjectId(%v)", string(o)) 77 | } 78 | 79 | func (r RegExp) String() string { 80 | return fmt.Sprintf("/%v/%v", r.Pattern, r.Options) 81 | } 82 | 83 | func (t Timestamp) String() string { 84 | return fmt.Sprintf(`{ "$timestamp": { "t": %v, "i": %v } }`, 85 | t.Seconds, t.Increment) 86 | } 87 | 88 | func (_ Undefined) String() string { 89 | return `{ "$undefined": true }` 90 | } 91 | -------------------------------------------------------------------------------- /internal/legacy/json/timestamp.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | "reflect" 12 | ) 13 | 14 | // Transition functions for recognizing Timestamp. 15 | // Adapted from encoding/json/scanner.go. 16 | 17 | // stateUpperT is the state after reading `T`. 18 | func stateUpperT(s *scanner, c int) int { 19 | if c == 'i' { 20 | s.step = generateState("Timestamp", []byte("mestamp"), stateConstructor) 21 | return scanContinue 22 | } 23 | return s.error(c, "in literal Timestamp (expecting 'i')") 24 | } 25 | 26 | // Decodes a Timestamp literal stored in the underlying byte data into v. 27 | func (d *decodeState) storeTimestamp(v reflect.Value) { 28 | op := d.scanWhile(scanSkipSpace) 29 | if op != scanBeginCtor { 30 | d.error(fmt.Errorf("expected beginning of constructor")) 31 | } 32 | 33 | args, err := d.ctor("Timestamp", []reflect.Type{uint32Type, uint32Type}) 34 | if err != nil { 35 | d.error(err) 36 | } 37 | switch kind := v.Kind(); kind { 38 | case reflect.Interface: 39 | arg0 := uint32(args[0].Uint()) 40 | arg1 := uint32(args[1].Uint()) 41 | v.Set(reflect.ValueOf(Timestamp{arg0, arg1})) 42 | default: 43 | d.error(fmt.Errorf("cannot store %v value into %v type", timestampType, kind)) 44 | } 45 | } 46 | 47 | // Returns a Timestamp literal from the underlying byte data. 48 | func (d *decodeState) getTimestamp() interface{} { 49 | op := d.scanWhile(scanSkipSpace) 50 | if op != scanBeginCtor { 51 | d.error(fmt.Errorf("expected beginning of constructor")) 52 | } 53 | 54 | // Prevent d.convertNumber() from parsing the arguments as float64s. 55 | useNumber := d.useNumber 56 | d.useNumber = true 57 | 58 | args := d.ctorInterface() 59 | if err := ctorNumArgsMismatch("Timestamp", 2, len(args)); err != nil { 60 | d.error(err) 61 | } 62 | arg0, err := args[0].(Number).Uint32() 63 | if err != nil { 64 | d.error(fmt.Errorf("expected uint32 for first argument of Timestamp constructor")) 65 | } 66 | arg1, err := args[1].(Number).Uint32() 67 | if err != nil { 68 | d.error(fmt.Errorf("expected uint32 for second argument of Timestamp constructor")) 69 | } 70 | 71 | d.useNumber = useNumber 72 | return Timestamp{arg0, arg1} 73 | } 74 | -------------------------------------------------------------------------------- /internal/llmgo/txn/mgo_test.go: -------------------------------------------------------------------------------- 1 | package txn_test 2 | 3 | import ( 4 | "bytes" 5 | "github.com/mongodb-labs/mongoreplay/internal/llmgo" 6 | . "gopkg.in/check.v1" 7 | "os/exec" 8 | "time" 9 | ) 10 | 11 | // ---------------------------------------------------------------------------- 12 | // The mgo test suite 13 | 14 | type MgoSuite struct { 15 | output bytes.Buffer 16 | server *exec.Cmd 17 | session *mgo.Session 18 | } 19 | 20 | var mgoaddr = "127.0.0.1:50017" 21 | 22 | func (s *MgoSuite) SetUpSuite(c *C) { 23 | //mgo.SetDebug(true) 24 | mgo.SetStats(true) 25 | dbdir := c.MkDir() 26 | args := []string{ 27 | "--dbpath", dbdir, 28 | "--bind_ip", "127.0.0.1", 29 | "--port", "50017", 30 | "--nssize", "1", 31 | "--noprealloc", 32 | "--smallfiles", 33 | "--nojournal", 34 | "-vvvvv", 35 | } 36 | s.server = exec.Command("mongod", args...) 37 | s.server.Stdout = &s.output 38 | s.server.Stderr = &s.output 39 | err := s.server.Start() 40 | if err != nil { 41 | panic(err) 42 | } 43 | } 44 | 45 | func (s *MgoSuite) TearDownSuite(c *C) { 46 | s.server.Process.Kill() 47 | s.server.Process.Wait() 48 | } 49 | 50 | func (s *MgoSuite) SetUpTest(c *C) { 51 | err := DropAll(mgoaddr) 52 | if err != nil { 53 | panic(err) 54 | } 55 | mgo.SetLogger(c) 56 | mgo.ResetStats() 57 | 58 | s.session, err = mgo.Dial(mgoaddr) 59 | c.Assert(err, IsNil) 60 | } 61 | 62 | func (s *MgoSuite) TearDownTest(c *C) { 63 | if s.session != nil { 64 | s.session.Close() 65 | } 66 | for i := 0; ; i++ { 67 | stats := mgo.GetStats() 68 | if stats.SocketsInUse == 0 && stats.SocketsAlive == 0 { 69 | break 70 | } 71 | if i == 20 { 72 | c.Fatal("Test left sockets in a dirty state") 73 | } 74 | c.Logf("Waiting for sockets to die: %d in use, %d alive", stats.SocketsInUse, stats.SocketsAlive) 75 | time.Sleep(500 * time.Millisecond) 76 | } 77 | } 78 | 79 | func DropAll(mongourl string) (err error) { 80 | session, err := mgo.Dial(mongourl) 81 | if err != nil { 82 | return err 83 | } 84 | defer session.Close() 85 | 86 | names, err := session.DatabaseNames() 87 | if err != nil { 88 | return err 89 | } 90 | for _, name := range names { 91 | switch name { 92 | case "admin", "local", "config": 93 | default: 94 | err = session.DB(name).DropDatabase() 95 | if err != nil { 96 | return err 97 | } 98 | } 99 | } 100 | return nil 101 | } 102 | -------------------------------------------------------------------------------- /internal/legacy/json/dbref.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | "reflect" 12 | ) 13 | 14 | // Transition functions for recognizing DBRef and Dbref. 15 | // Adapted from encoding/json/scanner.go. 16 | 17 | // stateDB is the state after reading `DB`. 18 | func stateDBR(s *scanner, c int) int { 19 | if c == 'e' { 20 | s.step = generateState("DBRef", []byte("f"), stateConstructor) 21 | return scanContinue 22 | } 23 | return s.error(c, "in literal DBRef (expecting 'e')") 24 | } 25 | 26 | // stateDb is the state after reading `Db`. 27 | func stateDb(s *scanner, c int) int { 28 | if c == 'r' { 29 | s.step = generateState("Dbref", []byte("ef"), stateConstructor) 30 | return scanContinue 31 | } 32 | return s.error(c, "in literal Dbref (expecting 'r')") 33 | } 34 | 35 | // Decodes a DBRef literal stored in the underlying byte data into v. 36 | func (d *decodeState) storeDBRef(v reflect.Value) { 37 | op := d.scanWhile(scanSkipSpace) 38 | if op != scanBeginCtor { 39 | d.error(fmt.Errorf("expected beginning of constructor")) 40 | } 41 | 42 | args := d.ctorInterface() 43 | if len(args) != 2 { 44 | d.error(fmt.Errorf("expected 2 arguments to DBRef constructor, but %v received", len(args))) 45 | } 46 | switch kind := v.Kind(); kind { 47 | case reflect.Interface: 48 | arg0, ok := args[0].(string) 49 | if !ok { 50 | d.error(fmt.Errorf("expected first argument to DBRef to be of type string")) 51 | } 52 | arg1 := args[1] 53 | v.Set(reflect.ValueOf(DBRef{arg0, arg1, ""})) 54 | default: 55 | d.error(fmt.Errorf("cannot store %v value into %v type", dbRefType, kind)) 56 | } 57 | } 58 | 59 | // Returns a DBRef literal from the underlying byte data. 60 | func (d *decodeState) getDBRef() interface{} { 61 | op := d.scanWhile(scanSkipSpace) 62 | if op != scanBeginCtor { 63 | d.error(fmt.Errorf("expected beginning of constructor")) 64 | } 65 | 66 | args := d.ctorInterface() 67 | if err := ctorNumArgsMismatch("DBRef", 2, len(args)); err != nil { 68 | d.error(err) 69 | } 70 | arg0, ok := args[0].(string) 71 | if !ok { 72 | d.error(fmt.Errorf("expected string for first argument of DBRef constructor")) 73 | } 74 | return DBRef{arg0, args[1], ""} 75 | } 76 | -------------------------------------------------------------------------------- /internal/llmgo/testdb/wait.js: -------------------------------------------------------------------------------- 1 | // We know the master of the first set (pri=1), but not of the second. 2 | var settings = {} 3 | var rs1cfg = {_id: "rs1", 4 | members: [{_id: 1, host: "127.0.0.1:40011", priority: 1}, 5 | {_id: 2, host: "127.0.0.1:40012", priority: 0}, 6 | {_id: 3, host: "127.0.0.1:40013", priority: 0}]} 7 | var rs2cfg = {_id: "rs2", 8 | members: [{_id: 1, host: "127.0.0.1:40021", priority: 1}, 9 | {_id: 2, host: "127.0.0.1:40022", priority: 1}, 10 | {_id: 3, host: "127.0.0.1:40023", priority: 0}]} 11 | var rs3cfg = {_id: "rs3", 12 | members: [{_id: 1, host: "127.0.0.1:40031", priority: 1}, 13 | {_id: 2, host: "127.0.0.1:40032", priority: 1}, 14 | {_id: 3, host: "127.0.0.1:40033", priority: 1}], 15 | settings: settings} 16 | 17 | for (var i = 0; i != 60; i++) { 18 | try { 19 | rs1a = new Mongo("127.0.0.1:40011").getDB("admin") 20 | rs2a = new Mongo("127.0.0.1:40021").getDB("admin") 21 | rs3a = new Mongo("127.0.0.1:40031").getDB("admin") 22 | rs3a.auth("root", "rapadura") 23 | db1 = new Mongo("127.0.0.1:40001").getDB("admin") 24 | db2 = new Mongo("127.0.0.1:40002").getDB("admin") 25 | break 26 | } catch(err) { 27 | print("Can't connect yet...") 28 | } 29 | sleep(1000) 30 | } 31 | 32 | function countHealthy(rs) { 33 | var status = rs.runCommand({replSetGetStatus: 1}) 34 | var count = 0 35 | var primary = 0 36 | if (typeof status.members != "undefined") { 37 | for (var i = 0; i != status.members.length; i++) { 38 | var m = status.members[i] 39 | if (m.health == 1 && (m.state == 1 || m.state == 2)) { 40 | count += 1 41 | if (m.state == 1) { 42 | primary = 1 43 | } 44 | } 45 | } 46 | } 47 | if (primary == 0) { 48 | count = 0 49 | } 50 | return count 51 | } 52 | 53 | var totalRSMembers = rs1cfg.members.length + rs2cfg.members.length + rs3cfg.members.length 54 | 55 | for (var i = 0; i != 90; i++) { 56 | var count = countHealthy(rs1a) + countHealthy(rs2a) + countHealthy(rs3a) 57 | print("Replica sets have", count, "healthy nodes.") 58 | if (count == totalRSMembers) { 59 | quit(0) 60 | } 61 | sleep(1000) 62 | } 63 | 64 | print("Replica sets didn't sync up properly.") 65 | quit(12) 66 | 67 | // vim:ts=4:sw=4:et 68 | -------------------------------------------------------------------------------- /internal/legacy/json/dbpointer.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | "gopkg.in/mgo.v2/bson" 12 | "reflect" 13 | ) 14 | 15 | // Transition functions for recognizing DBPointer. 16 | // Adapted from encoding/json/scanner.go. 17 | 18 | // stateDB is the state after reading `DB`. 19 | func stateDBP(s *scanner, c int) int { 20 | if c == 'o' { 21 | s.step = generateState("DBPointer", []byte("inter"), stateConstructor) 22 | return scanContinue 23 | } 24 | return s.error(c, "in literal DBPointer (expecting 'o')") 25 | } 26 | 27 | // Decodes a DBRef literal stored in the underlying byte data into v. 28 | func (d *decodeState) storeDBPointer(v reflect.Value) { 29 | op := d.scanWhile(scanSkipSpace) 30 | if op != scanBeginCtor { 31 | d.error(fmt.Errorf("expected beginning of constructor")) 32 | } 33 | 34 | args := d.ctorInterface() 35 | if len(args) != 2 { 36 | d.error(fmt.Errorf("expected 2 arguments to DBPointer constructor, but %v received", len(args))) 37 | } 38 | switch kind := v.Kind(); kind { 39 | case reflect.Interface: 40 | arg0, ok := args[0].(string) 41 | if !ok { 42 | d.error(fmt.Errorf("expected first argument to DBPointer to be of type string")) 43 | } 44 | arg1, ok := args[1].(ObjectId) 45 | if !ok { 46 | d.error(fmt.Errorf("expected second argument to DBPointer to be of type ObjectId, but ended up being %t", args[1])) 47 | } 48 | id := bson.ObjectIdHex(string(arg1)) 49 | v.Set(reflect.ValueOf(DBPointer{arg0, id})) 50 | default: 51 | d.error(fmt.Errorf("cannot store %v value into %v type", dbPointerType, kind)) 52 | } 53 | } 54 | 55 | // Returns a DBRef literal from the underlying byte data. 56 | func (d *decodeState) getDBPointer() interface{} { 57 | op := d.scanWhile(scanSkipSpace) 58 | if op != scanBeginCtor { 59 | d.error(fmt.Errorf("expected beginning of constructor")) 60 | } 61 | 62 | args := d.ctorInterface() 63 | if err := ctorNumArgsMismatch("DBPointer", 2, len(args)); err != nil { 64 | d.error(err) 65 | } 66 | arg0, ok := args[0].(string) 67 | if !ok { 68 | d.error(fmt.Errorf("expected string for first argument of DBPointer constructor")) 69 | } 70 | arg1, ok := args[1].(ObjectId) 71 | if !ok { 72 | d.error(fmt.Errorf("expected ObjectId for second argument of DBPointer constructor")) 73 | } 74 | id := bson.ObjectIdHex(string(arg1)) 75 | 76 | return DBPointer{arg0, id} 77 | } 78 | -------------------------------------------------------------------------------- /internal/legacy/json/date.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | "github.com/mongodb-labs/mongoreplay/internal/legacy/util" 12 | "reflect" 13 | ) 14 | 15 | // Transition functions for recognizing Date. 16 | // Adapted from encoding/json/scanner.go. 17 | 18 | // stateDa is the state after reading `Da`. 19 | func stateDa(s *scanner, c int) int { 20 | if c == 't' { 21 | s.step = stateDat 22 | return scanContinue 23 | } 24 | return s.error(c, "in literal Date (expecting 't')") 25 | } 26 | 27 | // stateDat is the state after reading `Dat`. 28 | func stateDat(s *scanner, c int) int { 29 | if c == 'e' { 30 | s.step = stateConstructor 31 | return scanContinue 32 | } 33 | return s.error(c, "in literal Date (expecting 'e')") 34 | } 35 | 36 | // Decodes a Date literal stored in the underlying byte data into v. 37 | func (d *decodeState) storeDate(v reflect.Value) { 38 | op := d.scanWhile(scanSkipSpace) 39 | if op != scanBeginCtor { 40 | d.error(fmt.Errorf("expected beginning of constructor")) 41 | } 42 | args, err := d.ctor("Date", []reflect.Type{dateType}) 43 | if err != nil { 44 | d.error(err) 45 | } 46 | switch kind := v.Kind(); kind { 47 | case reflect.Interface: 48 | v.Set(args[0]) 49 | default: 50 | d.error(fmt.Errorf("cannot store %v value into %v type", dateType, kind)) 51 | } 52 | } 53 | 54 | // Returns a Date literal from the underlying byte data. 55 | func (d *decodeState) getDate() interface{} { 56 | op := d.scanWhile(scanSkipSpace) 57 | if op != scanBeginCtor { 58 | d.error(fmt.Errorf("expected beginning of constructor")) 59 | } 60 | 61 | // Prevent d.convertNumber() from parsing the argument as a float64. 62 | useNumber := d.useNumber 63 | d.useNumber = true 64 | 65 | args := d.ctorInterface() 66 | if err := ctorNumArgsMismatch("Date", 1, len(args)); err != nil { 67 | d.error(err) 68 | } 69 | arg0num, isNumber := args[0].(Number) 70 | if !isNumber { 71 | // validate the date format of the string 72 | _, err := util.FormatDate(args[0].(string)) 73 | if err != nil { 74 | d.error(fmt.Errorf("unexpected ISODate format")) 75 | } 76 | d.useNumber = useNumber 77 | return ISODate(args[0].(string)) 78 | } 79 | arg0, err := arg0num.Int64() 80 | if err != nil { 81 | d.error(fmt.Errorf("expected int64 for first argument of Date constructor")) 82 | } 83 | 84 | d.useNumber = useNumber 85 | return Date(arg0) 86 | } 87 | -------------------------------------------------------------------------------- /internal/llmgo/txn/tarjan.go: -------------------------------------------------------------------------------- 1 | package txn 2 | 3 | import ( 4 | "github.com/mongodb-labs/mongoreplay/internal/llmgo/bson" 5 | "sort" 6 | ) 7 | 8 | func tarjanSort(successors map[bson.ObjectId][]bson.ObjectId) [][]bson.ObjectId { 9 | // http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm 10 | data := &tarjanData{ 11 | successors: successors, 12 | nodes: make([]tarjanNode, 0, len(successors)), 13 | index: make(map[bson.ObjectId]int, len(successors)), 14 | } 15 | 16 | for id := range successors { 17 | id := bson.ObjectId(string(id)) 18 | if _, seen := data.index[id]; !seen { 19 | data.strongConnect(id) 20 | } 21 | } 22 | 23 | // Sort connected components to stabilize the algorithm. 24 | for _, ids := range data.output { 25 | if len(ids) > 1 { 26 | sort.Sort(idList(ids)) 27 | } 28 | } 29 | return data.output 30 | } 31 | 32 | type tarjanData struct { 33 | successors map[bson.ObjectId][]bson.ObjectId 34 | output [][]bson.ObjectId 35 | 36 | nodes []tarjanNode 37 | stack []bson.ObjectId 38 | index map[bson.ObjectId]int 39 | } 40 | 41 | type tarjanNode struct { 42 | lowlink int 43 | stacked bool 44 | } 45 | 46 | type idList []bson.ObjectId 47 | 48 | func (l idList) Len() int { return len(l) } 49 | func (l idList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 50 | func (l idList) Less(i, j int) bool { return l[i] < l[j] } 51 | 52 | func (data *tarjanData) strongConnect(id bson.ObjectId) *tarjanNode { 53 | index := len(data.nodes) 54 | data.index[id] = index 55 | data.stack = append(data.stack, id) 56 | data.nodes = append(data.nodes, tarjanNode{index, true}) 57 | node := &data.nodes[index] 58 | 59 | for _, succid := range data.successors[id] { 60 | succindex, seen := data.index[succid] 61 | if !seen { 62 | succnode := data.strongConnect(succid) 63 | if succnode.lowlink < node.lowlink { 64 | node.lowlink = succnode.lowlink 65 | } 66 | } else if data.nodes[succindex].stacked { 67 | // Part of the current strongly-connected component. 68 | if succindex < node.lowlink { 69 | node.lowlink = succindex 70 | } 71 | } 72 | } 73 | 74 | if node.lowlink == index { 75 | // Root node; pop stack and output new 76 | // strongly-connected component. 77 | var scc []bson.ObjectId 78 | i := len(data.stack) - 1 79 | for { 80 | stackid := data.stack[i] 81 | stackindex := data.index[stackid] 82 | data.nodes[stackindex].stacked = false 83 | scc = append(scc, stackid) 84 | if stackindex == index { 85 | break 86 | } 87 | i-- 88 | } 89 | data.stack = data.stack[:i] 90 | data.output = append(data.output, scc) 91 | } 92 | 93 | return node 94 | } 95 | -------------------------------------------------------------------------------- /set_goenv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set_goenv() { 4 | # Error out if not in the same directory as this script 5 | if [ ! -f ./set_goenv.sh ]; then 6 | echo "Must be run from mongo-tools top-level directory. Aborting." 7 | return 1 8 | fi 9 | 10 | # Set OS-level default Go configuration 11 | UNAME_S=$(PATH="/usr/bin:/bin" uname -s) 12 | case $UNAME_S in 13 | CYGWIN*) 14 | PREF_GOROOT="c:/golang/go1.12" 15 | PREF_PATH="/cygdrive/c/golang/go1.12/bin:/cygdrive/c/mingw-w64/x86_64-4.9.1-posix-seh-rt_v3-rev1/mingw64/bin:$PATH" 16 | ;; 17 | *) 18 | PREF_GOROOT="/opt/golang/go1.12" 19 | # XXX might not need mongodbtoolchain anymore 20 | PREF_PATH="$PREF_GOROOT/bin:/opt/mongodbtoolchain/v3/bin/:$PATH" 21 | ;; 22 | esac 23 | 24 | # Set OS-level compilation flags 25 | case $UNAME_S in 26 | CYGWIN*) 27 | export CGO_CFLAGS="-D_WIN32_WINNT=0x0601 -DNTDDI_VERSION=0x06010000" 28 | export GOCACHE="C:/windows/temp" 29 | ;; 30 | Darwin) 31 | export CGO_CFLAGS="-mmacosx-version-min=10.11" 32 | export CGO_LDFLAGS="-mmacosx-version-min=10.11" 33 | ;; 34 | esac 35 | 36 | # If GOROOT is not set by the user, configure our preferred Go version and 37 | # associated path if available or error. 38 | if [ -z "$GOROOT" ]; then 39 | if [ -d "$PREF_GOROOT" ]; then 40 | export GOROOT="$PREF_GOROOT"; 41 | export PATH="$PREF_PATH"; 42 | else 43 | echo "GOROOT not set and preferred GOROOT '$PREF_GOROOT' doesn't exist. Aborting." 44 | return 1 45 | fi 46 | fi 47 | 48 | return 49 | } 50 | 51 | print_ldflags() { 52 | VersionStr="$(git describe --always)" 53 | GitCommit="$(git rev-parse HEAD)" 54 | importpath="main" 55 | echo "-X ${importpath}.VersionStr=${VersionStr} -X ${importpath}.GitCommit=${GitCommit}" 56 | } 57 | 58 | print_tags() { 59 | tags="" 60 | if [ ! -z "$1" ] 61 | then 62 | tags="$@" 63 | fi 64 | UNAME_S=$(PATH="/usr/bin:/bin" uname -s) 65 | case $UNAME_S in 66 | Darwin) 67 | if expr "$tags" : '.*ssl' > /dev/null ; then 68 | tags="$tags openssl_pre_1.0" 69 | fi 70 | ;; 71 | esac 72 | echo "$tags" 73 | } 74 | 75 | # On linux, we want to set buildmode=pie for ASLR support 76 | buildflags() { 77 | flags="" 78 | UNAME_S=$(PATH="/usr/bin:/bin" uname -s) 79 | case $UNAME_S in 80 | Linux) 81 | flags="-buildmode=pie" 82 | ;; 83 | esac 84 | echo "$flags" 85 | } 86 | -------------------------------------------------------------------------------- /main/mongoreplay.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package main 8 | 9 | import ( 10 | "github.com/jessevdk/go-flags" 11 | "github.com/mongodb-labs/mongoreplay/internal/legacy/options" 12 | "github.com/mongodb-labs/mongoreplay/mongoreplay" 13 | 14 | "fmt" 15 | "os" 16 | "runtime" 17 | ) 18 | 19 | const ( 20 | ExitOk = 0 21 | ExitError = 1 22 | ExitNonFatal = 3 23 | // Go reserves exit code 2 for its own use 24 | ) 25 | 26 | var ( 27 | VersionStr = "built-without-version-string" 28 | GitCommit = "build-without-git-commit" 29 | ) 30 | 31 | func main() { 32 | versionOpts := mongoreplay.VersionOptions{} 33 | versionFlagParser := flags.NewParser(&versionOpts, flags.Default) 34 | versionFlagParser.Options = flags.IgnoreUnknown 35 | _, err := versionFlagParser.Parse() 36 | if err != nil { 37 | os.Exit(ExitError) 38 | } 39 | 40 | if versionOpts.PrintVersion(VersionStr, GitCommit) { 41 | os.Exit(ExitOk) 42 | } 43 | 44 | if runtime.NumCPU() == 1 { 45 | fmt.Fprint(os.Stderr, "mongoreplay must be run with multiple threads") 46 | os.Exit(ExitError) 47 | } 48 | 49 | opts := mongoreplay.Options{VersionStr: VersionStr, GitCommit: GitCommit} 50 | 51 | var parser = flags.NewParser(&opts, flags.Default) 52 | 53 | playCmd := &mongoreplay.PlayCommand{GlobalOpts: &opts} 54 | playCmdParser, err := parser.AddCommand("play", "Play captured traffic against a mongodb instance", "", playCmd) 55 | if err != nil { 56 | panic(err) 57 | } 58 | if options.BuiltWithSSL { 59 | playCmd.SSLOpts = &options.SSL{} 60 | _, err := playCmdParser.AddGroup("ssl", "", playCmd.SSLOpts) 61 | if err != nil { 62 | panic(err) 63 | } 64 | } 65 | 66 | _, err = parser.AddCommand("record", "Convert network traffic into mongodb queries", "", 67 | &mongoreplay.RecordCommand{GlobalOpts: &opts}) 68 | if err != nil { 69 | panic(err) 70 | } 71 | 72 | _, err = parser.AddCommand("monitor", "Inspect live or pre-recorded mongodb traffic", "", 73 | &mongoreplay.MonitorCommand{GlobalOpts: &opts}) 74 | if err != nil { 75 | panic(err) 76 | } 77 | 78 | _, err = parser.AddCommand("filter", "Filter playback file", "", 79 | &mongoreplay.FilterCommand{GlobalOpts: &opts}) 80 | if err != nil { 81 | panic(err) 82 | } 83 | 84 | _, err = parser.Parse() 85 | 86 | if err != nil { 87 | switch err.(type) { 88 | case mongoreplay.ErrPacketsDropped: 89 | os.Exit(ExitNonFatal) 90 | default: 91 | os.Exit(ExitError) 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /mongoreplay/killcursors_op.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package mongoreplay 8 | 9 | import ( 10 | "fmt" 11 | 12 | "io" 13 | 14 | mgo "github.com/mongodb-labs/mongoreplay/internal/llmgo" 15 | ) 16 | 17 | // KillCursorsOp is used to close an active cursor in the database. This is necessary 18 | // to ensure that database resources are reclaimed at the end of the query. 19 | // http://docs.mongodb.org/meta-driver/latest/legacy/mongodb-wire-protocol/#op-kill-cursors 20 | type KillCursorsOp struct { 21 | Header MsgHeader 22 | mgo.KillCursorsOp 23 | } 24 | 25 | // Meta returns metadata about the KillCursorsOp, useful for analysis of traffic. 26 | func (op *KillCursorsOp) Meta() OpMetadata { 27 | return OpMetadata{"killcursors", "", "", op.CursorIds} 28 | } 29 | 30 | func (op *KillCursorsOp) String() string { 31 | return fmt.Sprintf("KillCursorsOp %v", op.CursorIds) 32 | } 33 | 34 | // Abbreviated returns a serialization of the KillCursorsOp, abbreviated so it 35 | // doesn't exceed the given number of characters. 36 | func (op *KillCursorsOp) Abbreviated(chars int) string { 37 | return fmt.Sprintf("%v", op) 38 | } 39 | 40 | // OpCode returns the OpCode for the KillCursorsOp. 41 | func (op *KillCursorsOp) OpCode() OpCode { 42 | return OpCodeKillCursors 43 | } 44 | 45 | func (op *KillCursorsOp) getCursorIDs() ([]int64, error) { 46 | return op.KillCursorsOp.CursorIds, nil 47 | } 48 | func (op *KillCursorsOp) setCursorIDs(cursorIDs []int64) error { 49 | op.KillCursorsOp.CursorIds = cursorIDs 50 | return nil 51 | } 52 | 53 | // FromReader extracts data from a serialized KillCursorsOp into its concrete 54 | // structure. 55 | func (op *KillCursorsOp) FromReader(r io.Reader) error { 56 | var b [8]byte 57 | _, err := io.ReadFull(r, b[:]) //skip ZERO and grab numberOfCursors 58 | if err != nil { 59 | return err 60 | } 61 | 62 | numCursors := uint32(getInt32(b[4:], 0)) 63 | var i uint32 64 | for i = 0; i < numCursors; i++ { 65 | if _, err := io.ReadFull(r, b[:]); err != nil { 66 | return nil 67 | } 68 | op.CursorIds = append(op.CursorIds, getInt64(b[:], 0)) 69 | } 70 | return nil 71 | } 72 | 73 | // Execute performs the KillCursorsOp on a given session, yielding the reply 74 | // when successful (and an error otherwise). 75 | func (op *KillCursorsOp) Execute(socket *mgo.MongoSocket) (Replyable, error) { 76 | if err := mgo.ExecOpWithoutReply(socket, &op.KillCursorsOp); err != nil { 77 | return nil, err 78 | } 79 | 80 | return nil, nil 81 | } 82 | -------------------------------------------------------------------------------- /internal/llmgo/txn/debug.go: -------------------------------------------------------------------------------- 1 | package txn 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "sort" 7 | "sync/atomic" 8 | 9 | "github.com/mongodb-labs/mongoreplay/internal/llmgo/bson" 10 | ) 11 | 12 | var ( 13 | debugEnabled bool 14 | logger log_Logger 15 | ) 16 | 17 | type log_Logger interface { 18 | Output(calldepth int, s string) error 19 | } 20 | 21 | // Specify the *log.Logger where logged messages should be sent to. 22 | func SetLogger(l log_Logger) { 23 | logger = l 24 | } 25 | 26 | // SetDebug enables or disables debugging. 27 | func SetDebug(debug bool) { 28 | debugEnabled = debug 29 | } 30 | 31 | var ErrChaos = fmt.Errorf("interrupted by chaos") 32 | 33 | var debugId uint32 34 | 35 | func debugPrefix() string { 36 | d := atomic.AddUint32(&debugId, 1) - 1 37 | s := make([]byte, 0, 10) 38 | for i := uint(0); i < 8; i++ { 39 | s = append(s, "abcdefghijklmnop"[(d>>(4*i))&0xf]) 40 | if d>>(4*(i+1)) == 0 { 41 | break 42 | } 43 | } 44 | s = append(s, ')', ' ') 45 | return string(s) 46 | } 47 | 48 | func logf(format string, args ...interface{}) { 49 | if logger != nil { 50 | logger.Output(2, fmt.Sprintf(format, argsForLog(args)...)) 51 | } 52 | } 53 | 54 | func debugf(format string, args ...interface{}) { 55 | if debugEnabled && logger != nil { 56 | logger.Output(2, fmt.Sprintf(format, argsForLog(args)...)) 57 | } 58 | } 59 | 60 | func argsForLog(args []interface{}) []interface{} { 61 | for i, arg := range args { 62 | switch v := arg.(type) { 63 | case bson.ObjectId: 64 | args[i] = v.Hex() 65 | case []bson.ObjectId: 66 | lst := make([]string, len(v)) 67 | for j, id := range v { 68 | lst[j] = id.Hex() 69 | } 70 | args[i] = lst 71 | case map[docKey][]bson.ObjectId: 72 | buf := &bytes.Buffer{} 73 | var dkeys docKeys 74 | for dkey := range v { 75 | dkeys = append(dkeys, dkey) 76 | } 77 | sort.Sort(dkeys) 78 | for i, dkey := range dkeys { 79 | if i > 0 { 80 | buf.WriteByte(' ') 81 | } 82 | buf.WriteString(fmt.Sprintf("%v: {", dkey)) 83 | for j, id := range v[dkey] { 84 | if j > 0 { 85 | buf.WriteByte(' ') 86 | } 87 | buf.WriteString(id.Hex()) 88 | } 89 | buf.WriteByte('}') 90 | } 91 | args[i] = buf.String() 92 | case map[docKey][]int64: 93 | buf := &bytes.Buffer{} 94 | var dkeys docKeys 95 | for dkey := range v { 96 | dkeys = append(dkeys, dkey) 97 | } 98 | sort.Sort(dkeys) 99 | for i, dkey := range dkeys { 100 | if i > 0 { 101 | buf.WriteByte(' ') 102 | } 103 | buf.WriteString(fmt.Sprintf("%v: %v", dkey, v[dkey])) 104 | } 105 | args[i] = buf.String() 106 | } 107 | } 108 | return args 109 | } 110 | -------------------------------------------------------------------------------- /mongoreplay/opcode.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package mongoreplay 8 | 9 | import "fmt" 10 | 11 | // OpCode allow identifying the type of operation: 12 | // http://docs.mongodb.org/meta-driver/latest/legacy/mongodb-wire-protocol/#request-opcodes 13 | type OpCode int32 14 | 15 | // String returns a human readable representation of the OpCode. 16 | func (c OpCode) String() string { 17 | switch c { 18 | case OpCodeReply: 19 | return "reply" 20 | case OpCodeMessage: 21 | return "message" 22 | case OpCodeUpdate: 23 | return "update" 24 | case OpCodeInsert: 25 | return "insert" 26 | case OpCodeReserved: 27 | return "reserved" 28 | case OpCodeQuery: 29 | return "query" 30 | case OpCodeGetMore: 31 | return "get_more" 32 | case OpCodeDelete: 33 | return "delete" 34 | case OpCodeKillCursors: 35 | return "kill_cursors" 36 | case OpCodeCommand: 37 | return "command" 38 | case OpCodeCommandReply: 39 | return "command_reply" 40 | default: 41 | return fmt.Sprintf("UNKNOWN(%d)", c) 42 | } 43 | } 44 | 45 | // The full set of known request op codes: 46 | // http://docs.mongodb.org/meta-driver/latest/legacy/mongodb-wire-protocol/#request-opcodes 47 | const ( 48 | OpCodeReply = OpCode(1) 49 | OpCodeUpdate = OpCode(2001) 50 | OpCodeInsert = OpCode(2002) 51 | OpCodeReserved = OpCode(2003) 52 | OpCodeQuery = OpCode(2004) 53 | OpCodeGetMore = OpCode(2005) 54 | OpCodeDelete = OpCode(2006) 55 | OpCodeKillCursors = OpCode(2007) 56 | OpCodeCommand = OpCode(2010) 57 | OpCodeCommandReply = OpCode(2011) 58 | OpCodeCompressed = OpCode(2012) 59 | OpCodeMessage = OpCode(2013) 60 | ) 61 | 62 | var goodOpCode = map[int32]bool{ 63 | 1: true, //OP_REPLY Reply to a client request. responseTo is set. 64 | 2001: true, //OP_UPDATE Update document. 65 | 2002: true, //OP_INSERT Insert new document. 66 | 2003: true, //RESERVED Formerly used for OP_GET_BY_OID. 67 | 2004: true, //OP_QUERY Query a collection. 68 | 2005: true, //OP_GET_MORE Get more data from a query. See Cursors. 69 | 2006: true, //OP_DELETE Delete documents. 70 | 2007: true, //OP_KILL_CURSORS Notify database that the client has finished with the cursor. 71 | 2010: true, //OP_COMMAND A new wire protocol message representing a command request 72 | 2011: true, //OP_COMMANDREPLY A new wire protocol message representing a command 73 | 2012: true, //OP_COMPRESSED Compressed op 74 | 2013: true, //OP_MSG New command/reply type 75 | } 76 | -------------------------------------------------------------------------------- /internal/legacy/util/math.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package util 8 | 9 | import ( 10 | "fmt" 11 | "reflect" 12 | ) 13 | 14 | // Return the max of two ints 15 | func MaxInt(a, b int) int { 16 | if a > b { 17 | return a 18 | } 19 | return b 20 | } 21 | 22 | // Numeric Conversion Tools 23 | 24 | type converterFunc func(interface{}) (interface{}, error) 25 | 26 | // this helper makes it simple to generate new numeric converters, 27 | // be sure to assign them on a package level instead of dynamically 28 | // within a function to avoid low performance 29 | func newNumberConverter(targetType reflect.Type) converterFunc { 30 | return func(number interface{}) (interface{}, error) { 31 | // to avoid panics on nil values 32 | if number == nil { 33 | return nil, fmt.Errorf("cannot convert nil value") 34 | } 35 | v := reflect.ValueOf(number) 36 | if !v.Type().ConvertibleTo(targetType) { 37 | return nil, fmt.Errorf("cannot convert %v to %v", v.Type(), targetType) 38 | } 39 | converted := v.Convert(targetType) 40 | return converted.Interface(), nil 41 | } 42 | } 43 | 44 | // making this package level so it is only evaluated once 45 | var uint32Converter = newNumberConverter(reflect.TypeOf(uint32(0))) 46 | 47 | // ToUInt32 is a function for converting any numeric type 48 | // into a uint32. This can easily result in a loss of information 49 | // due to truncation, so be careful. 50 | func ToUInt32(number interface{}) (uint32, error) { 51 | asInterface, err := uint32Converter(number) 52 | if err != nil { 53 | return 0, err 54 | } 55 | // no check for "ok" here, since we know it will work 56 | return asInterface.(uint32), nil 57 | } 58 | 59 | var intConverter = newNumberConverter(reflect.TypeOf(int(0))) 60 | 61 | // ToInt is a function for converting any numeric type 62 | // into an int. This can easily result in a loss of information 63 | // due to truncation of floats. 64 | func ToInt(number interface{}) (int, error) { 65 | asInterface, err := intConverter(number) 66 | if err != nil { 67 | return 0, err 68 | } 69 | // no check for "ok" here, since we know it will work 70 | return asInterface.(int), nil 71 | } 72 | 73 | var float64Converter = newNumberConverter(reflect.TypeOf(float64(0))) 74 | 75 | // ToFloat64 is a function for converting any numeric type 76 | // into a float64. 77 | func ToFloat64(number interface{}) (float64, error) { 78 | asInterface, err := float64Converter(number) 79 | if err != nil { 80 | return 0, err 81 | } 82 | // no check for "ok" here, since we know it will work 83 | return asInterface.(float64), nil 84 | } 85 | -------------------------------------------------------------------------------- /internal/llmgo/dbtest/dbserver_test.go: -------------------------------------------------------------------------------- 1 | package dbtest_test 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | "time" 7 | 8 | . "gopkg.in/check.v1" 9 | 10 | "github.com/mongodb-labs/mongoreplay/internal/llmgo" 11 | "github.com/mongodb-labs/mongoreplay/internal/llmgo/dbtest" 12 | ) 13 | 14 | type M map[string]interface{} 15 | 16 | func TestAll(t *testing.T) { 17 | TestingT(t) 18 | } 19 | 20 | type S struct { 21 | oldCheckSessions string 22 | } 23 | 24 | var _ = Suite(&S{}) 25 | 26 | func (s *S) SetUpTest(c *C) { 27 | s.oldCheckSessions = os.Getenv("CHECK_SESSIONS") 28 | os.Setenv("CHECK_SESSIONS", "") 29 | } 30 | 31 | func (s *S) TearDownTest(c *C) { 32 | os.Setenv("CHECK_SESSIONS", s.oldCheckSessions) 33 | } 34 | 35 | func (s *S) TestWipeData(c *C) { 36 | var server dbtest.DBServer 37 | server.SetPath(c.MkDir()) 38 | defer server.Stop() 39 | 40 | session := server.Session() 41 | err := session.DB("mydb").C("mycoll").Insert(M{"a": 1}) 42 | session.Close() 43 | c.Assert(err, IsNil) 44 | 45 | server.Wipe() 46 | 47 | session = server.Session() 48 | names, err := session.DatabaseNames() 49 | session.Close() 50 | c.Assert(err, IsNil) 51 | for _, name := range names { 52 | if name != "local" && name != "admin" { 53 | c.Fatalf("Wipe should have removed this database: %s", name) 54 | } 55 | } 56 | } 57 | 58 | func (s *S) TestStop(c *C) { 59 | var server dbtest.DBServer 60 | server.SetPath(c.MkDir()) 61 | defer server.Stop() 62 | 63 | // Server should not be running. 64 | process := server.ProcessTest() 65 | c.Assert(process, IsNil) 66 | 67 | session := server.Session() 68 | addr := session.LiveServers()[0] 69 | session.Close() 70 | 71 | // Server should be running now. 72 | process = server.ProcessTest() 73 | p, err := os.FindProcess(process.Pid) 74 | c.Assert(err, IsNil) 75 | p.Release() 76 | 77 | server.Stop() 78 | 79 | // Server should not be running anymore. 80 | session, err = mgo.DialWithTimeout(addr, 500*time.Millisecond) 81 | if session != nil { 82 | session.Close() 83 | c.Fatalf("Stop did not stop the server") 84 | } 85 | } 86 | 87 | func (s *S) TestCheckSessions(c *C) { 88 | var server dbtest.DBServer 89 | server.SetPath(c.MkDir()) 90 | defer server.Stop() 91 | 92 | session := server.Session() 93 | defer session.Close() 94 | c.Assert(server.Wipe, PanicMatches, "There are mgo sessions still alive.") 95 | } 96 | 97 | func (s *S) TestCheckSessionsDisabled(c *C) { 98 | var server dbtest.DBServer 99 | server.SetPath(c.MkDir()) 100 | defer server.Stop() 101 | 102 | os.Setenv("CHECK_SESSIONS", "0") 103 | 104 | // Should not panic, although it looks to Wipe like this session will leak. 105 | session := server.Session() 106 | defer session.Close() 107 | server.Wipe() 108 | } 109 | -------------------------------------------------------------------------------- /internal/llmgo/testserver/testserver_test.go: -------------------------------------------------------------------------------- 1 | package testserver_test 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | "time" 7 | 8 | . "gopkg.in/check.v1" 9 | 10 | "github.com/mongodb-labs/mongoreplay/internal/llmgo" 11 | "github.com/mongodb-labs/mongoreplay/internal/llmgo/testserver" 12 | ) 13 | 14 | type M map[string]interface{} 15 | 16 | func TestAll(t *testing.T) { 17 | TestingT(t) 18 | } 19 | 20 | type S struct { 21 | oldCheckSessions string 22 | } 23 | 24 | var _ = Suite(&S{}) 25 | 26 | func (s *S) SetUpTest(c *C) { 27 | s.oldCheckSessions = os.Getenv("CHECK_SESSIONS") 28 | os.Setenv("CHECK_SESSIONS", "") 29 | } 30 | 31 | func (s *S) TearDownTest(c *C) { 32 | os.Setenv("CHECK_SESSIONS", s.oldCheckSessions) 33 | } 34 | 35 | func (s *S) TestWipeData(c *C) { 36 | var server testserver.TestServer 37 | server.SetPath(c.MkDir()) 38 | defer server.Stop() 39 | 40 | session := server.Session() 41 | err := session.DB("mydb").C("mycoll").Insert(M{"a": 1}) 42 | session.Close() 43 | c.Assert(err, IsNil) 44 | 45 | server.Wipe() 46 | 47 | session = server.Session() 48 | names, err := session.DatabaseNames() 49 | session.Close() 50 | c.Assert(err, IsNil) 51 | for _, name := range names { 52 | if name != "local" && name != "admin" { 53 | c.Fatalf("Wipe should have removed this database: %s", name) 54 | } 55 | } 56 | } 57 | 58 | func (s *S) TestStop(c *C) { 59 | var server testserver.TestServer 60 | server.SetPath(c.MkDir()) 61 | defer server.Stop() 62 | 63 | // Server should not be running. 64 | process := server.ProcessTest() 65 | c.Assert(process, IsNil) 66 | 67 | session := server.Session() 68 | addr := session.LiveServers()[0] 69 | session.Close() 70 | 71 | // Server should be running now. 72 | process = server.ProcessTest() 73 | p, err := os.FindProcess(process.Pid) 74 | c.Assert(err, IsNil) 75 | p.Release() 76 | 77 | server.Stop() 78 | 79 | // Server should not be running anymore. 80 | session, err = mgo.DialWithTimeout(addr, 500*time.Millisecond) 81 | if session != nil { 82 | session.Close() 83 | c.Fatalf("Stop did not stop the server") 84 | } 85 | } 86 | 87 | func (s *S) TestCheckSessions(c *C) { 88 | var server testserver.TestServer 89 | server.SetPath(c.MkDir()) 90 | defer server.Stop() 91 | 92 | session := server.Session() 93 | defer session.Close() 94 | c.Assert(server.Wipe, PanicMatches, "There are mgo sessions still alive.") 95 | } 96 | 97 | func (s *S) TestCheckSessionsDisabled(c *C) { 98 | var server testserver.TestServer 99 | server.SetPath(c.MkDir()) 100 | defer server.Stop() 101 | 102 | os.Setenv("CHECK_SESSIONS", "0") 103 | 104 | // Should not panic, although it looks to Wipe like this session will leak. 105 | session := server.Session() 106 | defer session.Close() 107 | server.Wipe() 108 | } 109 | -------------------------------------------------------------------------------- /internal/legacy/json/objectid_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | "github.com/mongodb-labs/mongoreplay/internal/legacy/testtype" 12 | . "github.com/smartystreets/goconvey/convey" 13 | "testing" 14 | ) 15 | 16 | func TestObjectIdValue(t *testing.T) { 17 | testtype.SkipUnlessTestType(t, testtype.UnitTestType) 18 | 19 | Convey("When unmarshalling JSON with ObjectId values", t, func() { 20 | 21 | Convey("works for a single key", func() { 22 | var jsonMap map[string]interface{} 23 | 24 | key := "key" 25 | value := `ObjectId("123")` 26 | data := fmt.Sprintf(`{"%v":%v}`, key, value) 27 | 28 | err := Unmarshal([]byte(data), &jsonMap) 29 | So(err, ShouldBeNil) 30 | 31 | jsonValue, ok := jsonMap[key].(ObjectId) 32 | So(ok, ShouldBeTrue) 33 | So(jsonValue, ShouldEqual, ObjectId("123")) 34 | }) 35 | 36 | Convey("works for multiple keys", func() { 37 | var jsonMap map[string]interface{} 38 | 39 | key1, key2, key3 := "key1", "key2", "key3" 40 | value1, value2, value3 := `ObjectId("123")`, `ObjectId("456")`, `ObjectId("789")` 41 | data := fmt.Sprintf(`{"%v":%v,"%v":%v,"%v":%v}`, 42 | key1, value1, key2, value2, key3, value3) 43 | 44 | err := Unmarshal([]byte(data), &jsonMap) 45 | So(err, ShouldBeNil) 46 | 47 | jsonValue1, ok := jsonMap[key1].(ObjectId) 48 | So(ok, ShouldBeTrue) 49 | So(jsonValue1, ShouldEqual, ObjectId("123")) 50 | 51 | jsonValue2, ok := jsonMap[key2].(ObjectId) 52 | So(ok, ShouldBeTrue) 53 | So(jsonValue2, ShouldEqual, ObjectId("456")) 54 | 55 | jsonValue3, ok := jsonMap[key3].(ObjectId) 56 | So(ok, ShouldBeTrue) 57 | So(jsonValue3, ShouldEqual, ObjectId("789")) 58 | }) 59 | 60 | Convey("works in an array", func() { 61 | var jsonMap map[string]interface{} 62 | 63 | key := "key" 64 | value := `ObjectId("000")` 65 | data := fmt.Sprintf(`{"%v":[%v,%v,%v]}`, 66 | key, value, value, value) 67 | 68 | err := Unmarshal([]byte(data), &jsonMap) 69 | So(err, ShouldBeNil) 70 | 71 | jsonArray, ok := jsonMap[key].([]interface{}) 72 | So(ok, ShouldBeTrue) 73 | 74 | for _, _jsonValue := range jsonArray { 75 | jsonValue, ok := _jsonValue.(ObjectId) 76 | So(ok, ShouldBeTrue) 77 | So(jsonValue, ShouldEqual, ObjectId("000")) 78 | } 79 | }) 80 | 81 | Convey("cannot use number as argument", func() { 82 | var jsonMap map[string]interface{} 83 | 84 | key := "key" 85 | value := `ObjectId(123)` 86 | data := fmt.Sprintf(`{"%v":%v}`, key, value) 87 | 88 | err := Unmarshal([]byte(data), &jsonMap) 89 | So(err, ShouldNotBeNil) 90 | }) 91 | }) 92 | } 93 | -------------------------------------------------------------------------------- /internal/legacy/lldb/connector.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package lldb 8 | 9 | import ( 10 | "github.com/mongodb-labs/mongoreplay/internal/legacy/lldb/kerberos" 11 | "github.com/mongodb-labs/mongoreplay/internal/legacy/options" 12 | "github.com/mongodb-labs/mongoreplay/internal/legacy/util" 13 | "net" 14 | "time" 15 | 16 | mgo "github.com/mongodb-labs/mongoreplay/internal/llmgo" 17 | ) 18 | 19 | // Interface type for connecting to the database. 20 | type DBConnector interface { 21 | // configure, based on the options passed in 22 | Configure(options.ToolOptions) error 23 | 24 | // dial the database and get a fresh new session 25 | GetNewSession() (*mgo.Session, error) 26 | } 27 | 28 | // Basic connector for dialing the database, with no authentication. 29 | type VanillaDBConnector struct { 30 | dialInfo *mgo.DialInfo 31 | } 32 | 33 | // Configure sets up the db connector using the options in opts. It parses the 34 | // connection string and then sets up the dial information using the default 35 | // dial timeout. 36 | func (self *VanillaDBConnector) Configure(opts options.ToolOptions) error { 37 | timeout := time.Duration(opts.Timeout) * time.Second 38 | 39 | // create the dialer func that will be used to connect 40 | dialer := func(addr *mgo.ServerAddr) (net.Conn, error) { 41 | conn, err := net.DialTimeout("tcp", addr.String(), timeout) 42 | if err != nil { 43 | return nil, err 44 | } 45 | // enable TCP keepalive 46 | err = util.EnableTCPKeepAlive(conn, time.Duration(opts.TCPKeepAliveSeconds)*time.Second) 47 | if err != nil { 48 | return nil, err 49 | } 50 | return conn, nil 51 | } 52 | 53 | // set up the dial info 54 | self.dialInfo = &mgo.DialInfo{ 55 | Direct: opts.Direct, 56 | ReplicaSetName: opts.ReplicaSetName, 57 | Username: opts.Auth.Username, 58 | Password: opts.Auth.Password, 59 | Source: opts.GetAuthenticationDatabase(), 60 | Mechanism: opts.Auth.Mechanism, 61 | DialServer: dialer, 62 | Timeout: timeout, 63 | } 64 | 65 | // create or fetch the addresses to be used to connect 66 | if opts.URI != nil && opts.URI.ConnectionString != "" { 67 | self.dialInfo.Addrs = opts.URI.GetConnectionAddrs() 68 | } else { 69 | self.dialInfo.Addrs = util.CreateConnectionAddrs(opts.Host, opts.Port) 70 | } 71 | 72 | kerberos.AddKerberosOpts(opts, self.dialInfo) 73 | 74 | return nil 75 | } 76 | 77 | // GetNewSession connects to the server and returns the established session and any 78 | // error encountered. 79 | func (self *VanillaDBConnector) GetNewSession() (*mgo.Session, error) { 80 | return mgo.DialWithInfo(self.dialInfo) 81 | } 82 | -------------------------------------------------------------------------------- /internal/legacy/json/nan_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | "github.com/mongodb-labs/mongoreplay/internal/legacy/testtype" 12 | . "github.com/smartystreets/goconvey/convey" 13 | "math" 14 | "testing" 15 | ) 16 | 17 | func TestNaNValue(t *testing.T) { 18 | testtype.SkipUnlessTestType(t, testtype.UnitTestType) 19 | 20 | Convey("When unmarshalling JSON with NaN values", t, func() { 21 | 22 | Convey("works for a single key", func() { 23 | var jsonMap map[string]interface{} 24 | 25 | key := "key" 26 | value := "NaN" 27 | data := fmt.Sprintf(`{"%v":%v}`, key, value) 28 | 29 | err := Unmarshal([]byte(data), &jsonMap) 30 | So(err, ShouldBeNil) 31 | 32 | jsonValue, ok := jsonMap[key].(float64) 33 | So(ok, ShouldBeTrue) 34 | So(math.IsNaN(jsonValue), ShouldBeTrue) 35 | }) 36 | 37 | Convey("works for multiple keys", func() { 38 | var jsonMap map[string]interface{} 39 | 40 | key1, key2, key3 := "key1", "key2", "key3" 41 | value := "NaN" 42 | data := fmt.Sprintf(`{"%v":%v,"%v":%v,"%v":%v}`, 43 | key1, value, key2, value, key3, value) 44 | 45 | err := Unmarshal([]byte(data), &jsonMap) 46 | So(err, ShouldBeNil) 47 | 48 | jsonValue1, ok := jsonMap[key1].(float64) 49 | So(ok, ShouldBeTrue) 50 | So(math.IsNaN(jsonValue1), ShouldBeTrue) 51 | 52 | jsonValue2, ok := jsonMap[key2].(float64) 53 | So(ok, ShouldBeTrue) 54 | So(math.IsNaN(jsonValue2), ShouldBeTrue) 55 | 56 | jsonValue3, ok := jsonMap[key3].(float64) 57 | So(ok, ShouldBeTrue) 58 | So(math.IsNaN(jsonValue3), ShouldBeTrue) 59 | }) 60 | 61 | Convey("works in an array", func() { 62 | var jsonMap map[string]interface{} 63 | 64 | key := "key" 65 | value := "NaN" 66 | data := fmt.Sprintf(`{"%v":[%v,%v,%v]}`, 67 | key, value, value, value) 68 | 69 | err := Unmarshal([]byte(data), &jsonMap) 70 | So(err, ShouldBeNil) 71 | 72 | jsonArray, ok := jsonMap[key].([]interface{}) 73 | So(ok, ShouldBeTrue) 74 | 75 | for _, _jsonValue := range jsonArray { 76 | jsonValue, ok := _jsonValue.(float64) 77 | So(ok, ShouldBeTrue) 78 | So(math.IsNaN(jsonValue), ShouldBeTrue) 79 | } 80 | }) 81 | 82 | Convey("cannot have a sign ('+' or '-')", func() { 83 | var jsonMap map[string]interface{} 84 | 85 | key := "key" 86 | value := "NaN" 87 | data := fmt.Sprintf(`{"%v":+%v}`, key, value) 88 | 89 | err := Unmarshal([]byte(data), &jsonMap) 90 | So(err, ShouldNotBeNil) 91 | 92 | data = fmt.Sprintf(`{"%v":-%v}`, key, value) 93 | 94 | err = Unmarshal([]byte(data), &jsonMap) 95 | So(err, ShouldNotBeNil) 96 | }) 97 | }) 98 | } 99 | -------------------------------------------------------------------------------- /internal/legacy/json/helpers.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import "fmt" 10 | 11 | // Returns true if the byte array represents the null literal, 12 | // and false otherwise. Assumes that `nu` is sufficient to distinguish 13 | // between these cases. 14 | func isNull(s []byte) bool { 15 | return len(s) > 1 && s[0] == 'n' && s[1] == 'u' 16 | } 17 | 18 | // Returns true if the byte array represents some kind of number literal, 19 | // e.g. +123, -0.456, NaN, or Infinity, and false otherwise. Assumes that 20 | // the first character is sufficient to distinguish between these cases 21 | // with the exception of `N` where the second letter must be checked. 22 | func isNumber(s []byte) bool { 23 | if len(s) == 0 { 24 | return false 25 | } 26 | if len(s) > 1 && (s[0] == 'N' && s[1] == 'a') || (s[0] == 'I' && s[1] == 'n') { // NaN 27 | return true 28 | } 29 | return s[0] == '+' || s[0] == '-' || s[0] == '.' || (s[0] >= '0' && s[0] <= '9') 30 | } 31 | 32 | // Returns true if the string represents the start of a hexadecimal 33 | // literal, e.g. 0X123, -0x456, +0x789. 34 | func isHexPrefix(s string) bool { 35 | if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') { 36 | return true 37 | } 38 | return (s[0] == '+' || s[0] == '-') && isHexPrefix(s[1:]) 39 | } 40 | 41 | // Returns the accept state (transition function) if x is empty. 42 | // Otherwise returns a function that upon matching the first element 43 | // of x will generate another function to match the second, etc. 44 | // (or accept if no remaining elements). 45 | func generateState(name string, x []byte, accept func(*scanner, int) int) func(*scanner, int) int { 46 | if len(x) == 0 { 47 | return accept 48 | } 49 | 50 | return func(s *scanner, c int) int { 51 | if c == int(x[0]) { 52 | s.step = generateState(name, x[1:], accept) 53 | return scanContinue 54 | } 55 | return s.error(c, fmt.Sprintf("in literal %v (expecting '%v')", name, string(x[0]))) 56 | } 57 | } 58 | 59 | // stateOptionalConstructor is the state where there is the possibility of entering an empty constructor. 60 | func stateOptionalConstructor(s *scanner, c int) int { 61 | if c <= ' ' && isSpace(rune(c)) { 62 | return scanContinue 63 | } 64 | if c == '(' { 65 | s.step = stateInParen 66 | return scanContinue 67 | } 68 | return stateEndValue(s, c) 69 | } 70 | 71 | // stateInParen is the state when inside a `(` waiting for a `)` 72 | func stateInParen(s *scanner, c int) int { 73 | if c <= ' ' && isSpace(rune(c)) { 74 | return scanContinue 75 | } 76 | if c == ')' { 77 | s.step = stateEndValue 78 | return scanContinue 79 | } 80 | return s.error(c, "expecting ')' as next character") 81 | 82 | } 83 | -------------------------------------------------------------------------------- /internal/legacy/json/single_quoted.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | // Transition functions for recognizing single-quoted strings. 10 | // Adapted from encoding/json/scanner.go. 11 | 12 | // stateInSingleQuotedString is the state after reading `'`. 13 | func stateInSingleQuotedString(s *scanner, c int) int { 14 | if c == '\'' { 15 | s.step = stateEndValue 16 | return scanContinue 17 | } 18 | if c == '\\' { 19 | s.step = stateInSingleQuotedStringEsc 20 | return scanContinue 21 | } 22 | if c < 0x20 { 23 | return s.error(c, "in string literal") 24 | } 25 | return scanContinue 26 | } 27 | 28 | // stateInSingleQuotedStringEsc is the state after reading `'\` during a quoted string. 29 | func stateInSingleQuotedStringEsc(s *scanner, c int) int { 30 | switch c { 31 | case 'b', 'f', 'n', 'r', 't', '\\', '/', '\'': 32 | s.step = stateInSingleQuotedString 33 | return scanContinue 34 | } 35 | if c == 'u' { 36 | s.step = stateInSingleQuotedStringEscU 37 | return scanContinue 38 | } 39 | return s.error(c, "in string escape code") 40 | } 41 | 42 | // stateInSingleQuotedStringEscU is the state after reading `'\u` during a quoted string. 43 | func stateInSingleQuotedStringEscU(s *scanner, c int) int { 44 | if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { 45 | s.step = stateInSingleQuotedStringEscU1 46 | return scanContinue 47 | } 48 | // numbers 49 | return s.error(c, "in \\u hexadecimal character escape") 50 | } 51 | 52 | // stateInSingleQuotedStringEscU1 is the state after reading `'\u1` during a quoted string. 53 | func stateInSingleQuotedStringEscU1(s *scanner, c int) int { 54 | if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { 55 | s.step = stateInSingleQuotedStringEscU12 56 | return scanContinue 57 | } 58 | // numbers 59 | return s.error(c, "in \\u hexadecimal character escape") 60 | } 61 | 62 | // stateInSingleQuotedStringEscU12 is the state after reading `'\u12` during a quoted string. 63 | func stateInSingleQuotedStringEscU12(s *scanner, c int) int { 64 | if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { 65 | s.step = stateInSingleQuotedStringEscU123 66 | return scanContinue 67 | } 68 | // numbers 69 | return s.error(c, "in \\u hexadecimal character escape") 70 | } 71 | 72 | // stateInSingleQuotedStringEscU123 is the state after reading `'\u123` during a quoted string. 73 | func stateInSingleQuotedStringEscU123(s *scanner, c int) int { 74 | if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' { 75 | s.step = stateInSingleQuotedString 76 | return scanContinue 77 | } 78 | // numbers 79 | return s.error(c, "in \\u hexadecimal character escape") 80 | } 81 | -------------------------------------------------------------------------------- /internal/legacy/json/undefined_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | "github.com/mongodb-labs/mongoreplay/internal/legacy/testtype" 12 | . "github.com/smartystreets/goconvey/convey" 13 | "testing" 14 | ) 15 | 16 | func TestUndefinedValue(t *testing.T) { 17 | testtype.SkipUnlessTestType(t, testtype.UnitTestType) 18 | 19 | Convey("When unmarshalling JSON with undefined values", t, func() { 20 | 21 | Convey("works for a single key", func() { 22 | var jsonMap map[string]interface{} 23 | 24 | key := "key" 25 | value := "undefined" 26 | data := fmt.Sprintf(`{"%v":%v}`, key, value) 27 | 28 | err := Unmarshal([]byte(data), &jsonMap) 29 | So(err, ShouldBeNil) 30 | 31 | jsonValue, ok := jsonMap[key].(Undefined) 32 | So(ok, ShouldBeTrue) 33 | So(jsonValue, ShouldResemble, Undefined{}) 34 | }) 35 | 36 | Convey("works for multiple keys", func() { 37 | var jsonMap map[string]interface{} 38 | 39 | key1, key2, key3 := "key1", "key2", "key3" 40 | value := "undefined" 41 | data := fmt.Sprintf(`{"%v":%v,"%v":%v,"%v":%v}`, 42 | key1, value, key2, value, key3, value) 43 | 44 | err := Unmarshal([]byte(data), &jsonMap) 45 | So(err, ShouldBeNil) 46 | 47 | jsonValue1, ok := jsonMap[key1].(Undefined) 48 | So(ok, ShouldBeTrue) 49 | So(jsonValue1, ShouldResemble, Undefined{}) 50 | 51 | jsonValue2, ok := jsonMap[key2].(Undefined) 52 | So(ok, ShouldBeTrue) 53 | So(jsonValue2, ShouldResemble, Undefined{}) 54 | 55 | jsonValue3, ok := jsonMap[key3].(Undefined) 56 | So(ok, ShouldBeTrue) 57 | So(jsonValue3, ShouldResemble, Undefined{}) 58 | }) 59 | 60 | Convey("works in an array", func() { 61 | var jsonMap map[string]interface{} 62 | 63 | key := "key" 64 | value := "undefined" 65 | data := fmt.Sprintf(`{"%v":[%v,%v,%v]}`, 66 | key, value, value, value) 67 | 68 | err := Unmarshal([]byte(data), &jsonMap) 69 | So(err, ShouldBeNil) 70 | 71 | jsonArray, ok := jsonMap[key].([]interface{}) 72 | So(ok, ShouldBeTrue) 73 | 74 | for _, _jsonValue := range jsonArray { 75 | jsonValue, ok := _jsonValue.(Undefined) 76 | So(ok, ShouldBeTrue) 77 | So(jsonValue, ShouldResemble, Undefined{}) 78 | } 79 | }) 80 | 81 | Convey("cannot have a sign ('+' or '-')", func() { 82 | var jsonMap map[string]interface{} 83 | 84 | key := "key" 85 | value := "undefined" 86 | data := fmt.Sprintf(`{"%v":+%v}`, key, value) 87 | 88 | err := Unmarshal([]byte(data), &jsonMap) 89 | So(err, ShouldNotBeNil) 90 | 91 | data = fmt.Sprintf(`{"%v":-%v}`, key, value) 92 | 93 | err = Unmarshal([]byte(data), &jsonMap) 94 | So(err, ShouldNotBeNil) 95 | }) 96 | }) 97 | } 98 | -------------------------------------------------------------------------------- /internal/legacy/json/timestamp_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | "github.com/mongodb-labs/mongoreplay/internal/legacy/testtype" 12 | . "github.com/smartystreets/goconvey/convey" 13 | "testing" 14 | ) 15 | 16 | func TestTimestampValue(t *testing.T) { 17 | testtype.SkipUnlessTestType(t, testtype.UnitTestType) 18 | 19 | Convey("When unmarshalling JSON with Timestamp values", t, func() { 20 | 21 | Convey("works for a single key", func() { 22 | var jsonMap map[string]interface{} 23 | 24 | key := "key" 25 | value := "Timestamp(123, 321)" 26 | data := fmt.Sprintf(`{"%v":%v}`, key, value) 27 | 28 | err := Unmarshal([]byte(data), &jsonMap) 29 | So(err, ShouldBeNil) 30 | 31 | jsonValue, ok := jsonMap[key].(Timestamp) 32 | So(ok, ShouldBeTrue) 33 | So(jsonValue, ShouldResemble, Timestamp{123, 321}) 34 | }) 35 | 36 | Convey("works for multiple keys", func() { 37 | var jsonMap map[string]interface{} 38 | 39 | key1, key2, key3 := "key1", "key2", "key3" 40 | value1, value2, value3 := "Timestamp(123, 321)", 41 | "Timestamp(456, 654)", "Timestamp(789, 987)" 42 | data := fmt.Sprintf(`{"%v":%v,"%v":%v,"%v":%v}`, 43 | key1, value1, key2, value2, key3, value3) 44 | 45 | err := Unmarshal([]byte(data), &jsonMap) 46 | So(err, ShouldBeNil) 47 | 48 | jsonValue1, ok := jsonMap[key1].(Timestamp) 49 | So(ok, ShouldBeTrue) 50 | So(jsonValue1, ShouldResemble, Timestamp{123, 321}) 51 | 52 | jsonValue2, ok := jsonMap[key2].(Timestamp) 53 | So(ok, ShouldBeTrue) 54 | So(jsonValue2, ShouldResemble, Timestamp{456, 654}) 55 | 56 | jsonValue3, ok := jsonMap[key3].(Timestamp) 57 | So(ok, ShouldBeTrue) 58 | So(jsonValue3, ShouldResemble, Timestamp{789, 987}) 59 | }) 60 | 61 | Convey("works in an array", func() { 62 | var jsonMap map[string]interface{} 63 | 64 | key := "key" 65 | value := "Timestamp(42, 10)" 66 | data := fmt.Sprintf(`{"%v":[%v,%v,%v]}`, 67 | key, value, value, value) 68 | 69 | err := Unmarshal([]byte(data), &jsonMap) 70 | So(err, ShouldBeNil) 71 | 72 | jsonArray, ok := jsonMap[key].([]interface{}) 73 | So(ok, ShouldBeTrue) 74 | 75 | for _, _jsonValue := range jsonArray { 76 | jsonValue, ok := _jsonValue.(Timestamp) 77 | So(ok, ShouldBeTrue) 78 | So(jsonValue, ShouldResemble, Timestamp{42, 10}) 79 | } 80 | }) 81 | 82 | Convey("cannot use string as argument", func() { 83 | var jsonMap map[string]interface{} 84 | 85 | key := "key" 86 | value := `Timestamp("123", "321")` 87 | data := fmt.Sprintf(`{"%v":%v}`, key, value) 88 | 89 | err := Unmarshal([]byte(data), &jsonMap) 90 | So(err, ShouldNotBeNil) 91 | }) 92 | }) 93 | } 94 | -------------------------------------------------------------------------------- /internal/llmgo/THIRD-PARTY-NOTICES: -------------------------------------------------------------------------------- 1 | mgo - MongoDB driver for Go 2 | 3 | Copyright (c) 2010-2013 - Gustavo Niemeyer 4 | 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | -------------------------------------------------------------------------- 28 | BSON library for Go 29 | 30 | Copyright (c) 2010-2012 - Gustavo Niemeyer 31 | 32 | All rights reserved. 33 | 34 | Redistribution and use in source and binary forms, with or without 35 | modification, are permitted provided that the following conditions are met: 36 | 37 | 1. Redistributions of source code must retain the above copyright notice, this 38 | list of conditions and the following disclaimer. 39 | 2. Redistributions in binary form must reproduce the above copyright notice, 40 | this list of conditions and the following disclaimer in the documentation 41 | and/or other materials provided with the distribution. 42 | 43 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 44 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 45 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 46 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 47 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 48 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 49 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 50 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 51 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 52 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 53 | -------------------------------------------------------------------------------- /internal/legacy/util/math_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package util 8 | 9 | import ( 10 | "github.com/mongodb-labs/mongoreplay/internal/legacy/testtype" 11 | "reflect" 12 | "testing" 13 | 14 | . "github.com/smartystreets/goconvey/convey" 15 | ) 16 | 17 | func TestMaxInt(t *testing.T) { 18 | 19 | testtype.SkipUnlessTestType(t, testtype.UnitTestType) 20 | 21 | Convey("When finding the maximum of two ints", t, func() { 22 | 23 | Convey("the larger int should be returned", func() { 24 | 25 | So(MaxInt(1, 2), ShouldEqual, 2) 26 | So(MaxInt(2, 1), ShouldEqual, 2) 27 | 28 | }) 29 | 30 | }) 31 | } 32 | 33 | func TestNumberConverter(t *testing.T) { 34 | 35 | testtype.SkipUnlessTestType(t, testtype.UnitTestType) 36 | 37 | Convey("With a number converter for float32", t, func() { 38 | floatConverter := newNumberConverter(reflect.TypeOf(float32(0))) 39 | 40 | Convey("numeric values should be convertable", func() { 41 | out, err := floatConverter(21) 42 | So(err, ShouldEqual, nil) 43 | So(out, ShouldEqual, 21.0) 44 | out, err = floatConverter(uint64(21)) 45 | So(err, ShouldEqual, nil) 46 | So(out, ShouldEqual, 21.0) 47 | out, err = floatConverter(float64(27.52)) 48 | So(err, ShouldEqual, nil) 49 | So(out, ShouldEqual, 27.52) 50 | }) 51 | 52 | Convey("non-numeric values should fail", func() { 53 | _, err := floatConverter("I AM A STRING") 54 | So(err, ShouldNotBeNil) 55 | _, err = floatConverter(struct{ int }{12}) 56 | So(err, ShouldNotBeNil) 57 | _, err = floatConverter(nil) 58 | So(err, ShouldNotBeNil) 59 | }) 60 | }) 61 | } 62 | 63 | func TestUInt32Converter(t *testing.T) { 64 | 65 | testtype.SkipUnlessTestType(t, testtype.UnitTestType) 66 | 67 | Convey("With a series of test values, conversions should pass", t, func() { 68 | out, err := ToUInt32(int64(99)) 69 | So(err, ShouldEqual, nil) 70 | So(out, ShouldEqual, uint32(99)) 71 | out, err = ToUInt32(int32(99)) 72 | So(err, ShouldEqual, nil) 73 | So(out, ShouldEqual, uint32(99)) 74 | out, err = ToUInt32(float32(99)) 75 | So(err, ShouldEqual, nil) 76 | So(out, ShouldEqual, uint32(99)) 77 | out, err = ToUInt32(float64(99)) 78 | So(err, ShouldEqual, nil) 79 | So(out, ShouldEqual, uint32(99)) 80 | out, err = ToUInt32(uint64(99)) 81 | So(err, ShouldEqual, nil) 82 | So(out, ShouldEqual, uint32(99)) 83 | out, err = ToUInt32(uint32(99)) 84 | So(err, ShouldEqual, nil) 85 | So(out, ShouldEqual, uint32(99)) 86 | 87 | Convey("but non-numeric inputs will fail", func() { 88 | _, err = ToUInt32(nil) 89 | So(err, ShouldNotBeNil) 90 | _, err = ToUInt32("string") 91 | So(err, ShouldNotBeNil) 92 | _, err = ToUInt32([]byte{1, 2, 3, 4}) 93 | So(err, ShouldNotBeNil) 94 | }) 95 | }) 96 | } 97 | -------------------------------------------------------------------------------- /internal/legacy/json/new.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | "reflect" 12 | ) 13 | 14 | // Transition functions for recognizing new. 15 | // Adapted from encoding/json/scanner.go. 16 | 17 | // stateNe is the state after reading `ne`. 18 | func stateNe(s *scanner, c int) int { 19 | if c == 'w' { 20 | s.step = stateNew 21 | return scanContinue 22 | } 23 | return s.error(c, "in literal new (expecting 'w')") 24 | } 25 | 26 | // stateNew is the state after reading `new`. 27 | // Ensures that there is a space after the new keyword. 28 | func stateNew(s *scanner, c int) int { 29 | if c <= ' ' && isSpace(rune(c)) { 30 | s.step = stateBeginObjectValue 31 | return scanContinue 32 | } 33 | return s.error(c, "expected space after new keyword") 34 | } 35 | 36 | // stateBeginObjectValue is the state after reading `new`. 37 | func stateBeginObjectValue(s *scanner, c int) int { 38 | if c <= ' ' && isSpace(rune(c)) { 39 | return scanSkipSpace 40 | } 41 | switch c { 42 | case 'B': // beginning of BinData or Boolean 43 | s.step = stateB 44 | case 'D': // beginning of Date 45 | s.step = stateD 46 | case 'N': // beginning of NumberInt or NumberLong 47 | s.step = stateNumberUpperN 48 | case 'O': // beginning of ObjectId 49 | s.step = stateO 50 | case 'R': // beginning of RegExp 51 | s.step = stateR 52 | case 'T': // beginning of Timestamp 53 | s.step = stateUpperT 54 | default: 55 | return s.error(c, "looking for beginning of value") 56 | } 57 | 58 | return scanBeginLiteral 59 | } 60 | 61 | // stateNumberUpperN is the state after reading `N`. 62 | func stateNumberUpperN(s *scanner, c int) int { 63 | if c == 'u' { 64 | s.step = stateUpperNu 65 | return scanContinue 66 | } 67 | return s.error(c, "in literal NumberInt or NumberLong (expecting 'u')") 68 | } 69 | 70 | // Decodes a literal stored in the underlying byte data into v. 71 | func (d *decodeState) storeNewLiteral(v reflect.Value, fromQuoted bool) { 72 | op := d.scanWhile(scanSkipSpace) 73 | if op != scanBeginLiteral { 74 | d.error(fmt.Errorf("expected beginning of constructor")) 75 | } 76 | 77 | // Read constructor identifier 78 | start := d.off - 1 79 | op = d.scanWhile(scanContinue) 80 | if op != scanBeginCtor { 81 | d.error(fmt.Errorf("expected beginning of constructor")) 82 | } 83 | 84 | // Back up so d.ctor can have the byte we just read. 85 | d.off-- 86 | d.scan.undo(op) 87 | 88 | d.literalStore(d.data[start:d.off-1], v, fromQuoted) 89 | } 90 | 91 | // Returns a literal from the underlying byte data. 92 | func (d *decodeState) getNewLiteral() interface{} { 93 | op := d.scanWhile(scanSkipSpace) 94 | if op != scanBeginLiteral { 95 | d.error(fmt.Errorf("expected beginning of constructor")) 96 | } 97 | return d.literalInterface() 98 | } 99 | -------------------------------------------------------------------------------- /internal/legacy/json/bindata_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | "github.com/mongodb-labs/mongoreplay/internal/legacy/testtype" 12 | . "github.com/smartystreets/goconvey/convey" 13 | "testing" 14 | ) 15 | 16 | func TestBinDataValue(t *testing.T) { 17 | testtype.SkipUnlessTestType(t, testtype.UnitTestType) 18 | 19 | Convey("When unmarshalling JSON with BinData values", t, func() { 20 | 21 | Convey("works for a single key", func() { 22 | var jsonMap map[string]interface{} 23 | 24 | key := "key" 25 | value := `BinData(1, "xyz")` 26 | data := fmt.Sprintf(`{"%v":%v}`, key, value) 27 | 28 | err := Unmarshal([]byte(data), &jsonMap) 29 | So(err, ShouldBeNil) 30 | 31 | jsonValue, ok := jsonMap[key].(BinData) 32 | So(ok, ShouldBeTrue) 33 | So(jsonValue, ShouldResemble, BinData{1, "xyz"}) 34 | }) 35 | 36 | Convey("works for multiple keys", func() { 37 | var jsonMap map[string]interface{} 38 | 39 | key1, key2, key3 := "key1", "key2", "key3" 40 | value1, value2, value3 := `BinData(1, "abc")`, 41 | `BinData(2, "def")`, `BinData(3, "ghi")` 42 | data := fmt.Sprintf(`{"%v":%v,"%v":%v,"%v":%v}`, 43 | key1, value1, key2, value2, key3, value3) 44 | 45 | err := Unmarshal([]byte(data), &jsonMap) 46 | So(err, ShouldBeNil) 47 | 48 | jsonValue1, ok := jsonMap[key1].(BinData) 49 | So(ok, ShouldBeTrue) 50 | So(jsonValue1, ShouldResemble, BinData{1, "abc"}) 51 | 52 | jsonValue2, ok := jsonMap[key2].(BinData) 53 | So(ok, ShouldBeTrue) 54 | So(jsonValue2, ShouldResemble, BinData{2, "def"}) 55 | 56 | jsonValue3, ok := jsonMap[key3].(BinData) 57 | So(ok, ShouldBeTrue) 58 | So(jsonValue3, ShouldResemble, BinData{3, "ghi"}) 59 | }) 60 | 61 | Convey("works in an array", func() { 62 | var jsonMap map[string]interface{} 63 | 64 | key := "key" 65 | value := `BinData(42, "10")` 66 | data := fmt.Sprintf(`{"%v":[%v,%v,%v]}`, 67 | key, value, value, value) 68 | 69 | err := Unmarshal([]byte(data), &jsonMap) 70 | So(err, ShouldBeNil) 71 | 72 | jsonArray, ok := jsonMap[key].([]interface{}) 73 | So(ok, ShouldBeTrue) 74 | 75 | for _, _jsonValue := range jsonArray { 76 | jsonValue, ok := _jsonValue.(BinData) 77 | So(ok, ShouldBeTrue) 78 | So(jsonValue, ShouldResemble, BinData{42, "10"}) 79 | } 80 | }) 81 | 82 | Convey("can specify type argument using hexadecimal", func() { 83 | var jsonMap map[string]interface{} 84 | 85 | key := "key" 86 | value := `BinData(0x5f, "xyz")` 87 | data := fmt.Sprintf(`{"%v":%v}`, key, value) 88 | 89 | err := Unmarshal([]byte(data), &jsonMap) 90 | So(err, ShouldBeNil) 91 | 92 | jsonValue, ok := jsonMap[key].(BinData) 93 | So(ok, ShouldBeTrue) 94 | So(jsonValue, ShouldResemble, BinData{0x5f, "xyz"}) 95 | }) 96 | }) 97 | } 98 | -------------------------------------------------------------------------------- /internal/llmgo/stats.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2015-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | // Based on gopkg.io/mgo.v2 by Gustavo Niemeyer. 8 | // See THIRD-PARTY-NOTICES for original license terms. 9 | 10 | package mgo 11 | 12 | import ( 13 | "sync" 14 | ) 15 | 16 | var stats *Stats 17 | var statsMutex sync.Mutex 18 | 19 | func SetStats(enabled bool) { 20 | statsMutex.Lock() 21 | if enabled { 22 | if stats == nil { 23 | stats = &Stats{} 24 | } 25 | } else { 26 | stats = nil 27 | } 28 | statsMutex.Unlock() 29 | } 30 | 31 | func GetStats() (snapshot Stats) { 32 | statsMutex.Lock() 33 | snapshot = *stats 34 | statsMutex.Unlock() 35 | return 36 | } 37 | 38 | func ResetStats() { 39 | statsMutex.Lock() 40 | debug("Resetting stats") 41 | old := stats 42 | stats = &Stats{} 43 | // These are absolute values: 44 | stats.Clusters = old.Clusters 45 | stats.SocketsInUse = old.SocketsInUse 46 | stats.SocketsAlive = old.SocketsAlive 47 | stats.SocketRefs = old.SocketRefs 48 | statsMutex.Unlock() 49 | return 50 | } 51 | 52 | type Stats struct { 53 | Clusters int 54 | MasterConns int 55 | SlaveConns int 56 | SentOps int 57 | ReceivedOps int 58 | ReceivedDocs int 59 | SocketsAlive int 60 | SocketsInUse int 61 | SocketRefs int 62 | } 63 | 64 | func (stats *Stats) cluster(delta int) { 65 | if stats != nil { 66 | statsMutex.Lock() 67 | stats.Clusters += delta 68 | statsMutex.Unlock() 69 | } 70 | } 71 | 72 | func (stats *Stats) conn(delta int, master bool) { 73 | if stats != nil { 74 | statsMutex.Lock() 75 | if master { 76 | stats.MasterConns += delta 77 | } else { 78 | stats.SlaveConns += delta 79 | } 80 | statsMutex.Unlock() 81 | } 82 | } 83 | 84 | func (stats *Stats) sentOps(delta int) { 85 | if stats != nil { 86 | statsMutex.Lock() 87 | stats.SentOps += delta 88 | statsMutex.Unlock() 89 | } 90 | } 91 | 92 | func (stats *Stats) receivedOps(delta int) { 93 | if stats != nil { 94 | statsMutex.Lock() 95 | stats.ReceivedOps += delta 96 | statsMutex.Unlock() 97 | } 98 | } 99 | 100 | func (stats *Stats) receivedDocs(delta int) { 101 | if stats != nil { 102 | statsMutex.Lock() 103 | stats.ReceivedDocs += delta 104 | statsMutex.Unlock() 105 | } 106 | } 107 | 108 | func (stats *Stats) socketsInUse(delta int) { 109 | if stats != nil { 110 | statsMutex.Lock() 111 | stats.SocketsInUse += delta 112 | statsMutex.Unlock() 113 | } 114 | } 115 | 116 | func (stats *Stats) socketsAlive(delta int) { 117 | if stats != nil { 118 | statsMutex.Lock() 119 | stats.SocketsAlive += delta 120 | statsMutex.Unlock() 121 | } 122 | } 123 | 124 | func (stats *Stats) socketRefs(delta int) { 125 | if stats != nil { 126 | statsMutex.Lock() 127 | stats.SocketRefs += delta 128 | statsMutex.Unlock() 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mongoreplay 2 | Mongoreplay is no longer supported or maintained. 3 | 4 | ## Purpose 5 | 6 | `mongoreplay` is a traffic capture and replay tool for MongoDB. It can be used to inspect commands being sent to a MongoDB instance, record them, and replay them back onto another host at a later time. 7 | ## Use cases 8 | - Preview how well your database cluster would perform a production workload under a different environment (storage engine, index, hardware, OS, etc.) 9 | - Reproduce and investigate bugs by recording and replaying the operations that trigger them 10 | - Inspect the details of what an application is doing to a mongo cluster (i.e. a more flexible version of [mongosniff](https://docs.mongodb.org/manual/reference/program/mongosniff/)) 11 | 12 | ## Quickstart 13 | 14 | ### Building Tools 15 | To build the tools, you need to have Go version 1.9 and up. `go get` will not work; you 16 | need to clone the repository to build it. 17 | 18 | ``` 19 | git clone https://github.com/mongodb-labs/mongoreplay.git 20 | cd mongodb-labs/mongoreplay.git 21 | ``` 22 | 23 | To use build/test scripts in the repo, you *MUST* set GOROOT to your Go root directory. 24 | 25 | ``` 26 | export GOROOT=/usr/local/go 27 | ``` 28 | 29 | ### Quick build 30 | 31 | The `build.sh` script builds all the tools, placing them in the `bin` 32 | directory. Pass any build tags (like `ssl` or `sasl`) as additional command 33 | line arguments. 34 | 35 | ``` 36 | ./build.sh 37 | ./build.sh ssl 38 | ./build.sh ssl sasl 39 | ``` 40 | 41 | ### Manual Build 42 | 43 | Source `set_goenv.sh` and run the `set_goenv` function to setup your GOPATH and 44 | architecture-specific configuration flags: 45 | 46 | ``` 47 | . ./set_goenv.sh 48 | set_goenv 49 | ``` 50 | 51 | Set the environment variable to use local vendor folder 52 | Pass tags to the `go build` command as needed in order to build the tools with 53 | support for SSL and/or SASL. For example: 54 | 55 | ``` 56 | mkdir bin 57 | export GO111MODULE=on 58 | export GOSUMDB=off 59 | export GOFLAGS=-mod=vendor 60 | 61 | go build -o bin/mongoreplay main/mongoreplay.go 62 | go build -o bin/mongoreplay -tags ssl main/mongoreplay.go 63 | go build -o bin/mongoreplay -tags "ssl sasl" main/mongoreplay.go 64 | ``` 65 | 66 | ### Use Mongoreplay 67 | Please follow the instructions in https://docs.mongodb.com/manual/reference/program/mongoreplay/ 68 | 69 | ## Testing 70 | 71 | To run unit and integration tests: 72 | 73 | ``` 74 | ./runTests.sh 75 | ``` 76 | If TOOLS_TESTING_UNIT is set to "true" in the environment, unit tests will run. Delete the environment variable will disable unittest test. 77 | If TOOLS_TESTING_INTEGRATION is set to "true" in the environment, integration tests will run. Delete the environment variable will disable integration test. 78 | 79 | Integration tests require a `mongod` (running on port 33333) while unit tests do not. 80 | 81 | To run the tests inside pcap_test.go, you need to download the testing pcap files from Amazon S3 to mongoreplay/testPcap 82 | bucket: boxes.10gen.com 83 | path: build/mongotape/ 84 | If the pcap files are not available, the tests inside pcap_test will be skpped. 85 | -------------------------------------------------------------------------------- /internal/legacy/json/float_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | "github.com/mongodb-labs/mongoreplay/internal/legacy/testtype" 12 | . "github.com/smartystreets/goconvey/convey" 13 | "testing" 14 | ) 15 | 16 | func TestNumberFloatValue(t *testing.T) { 17 | testtype.SkipUnlessTestType(t, testtype.UnitTestType) 18 | 19 | Convey("When unmarshaling JSON with float values", t, func() { 20 | 21 | Convey("converts to a JSON NumberFloat value", func() { 22 | var jsonMap map[string]interface{} 23 | 24 | key := "key" 25 | value := "5.5" 26 | data := fmt.Sprintf(`{"%v":%v}`, key, value) 27 | 28 | err := Unmarshal([]byte(data), &jsonMap) 29 | So(err, ShouldBeNil) 30 | 31 | jsonValue, ok := jsonMap[key].(float64) 32 | So(ok, ShouldBeTrue) 33 | So(jsonValue, ShouldEqual, NumberFloat(5.5)) 34 | 35 | }) 36 | }) 37 | 38 | Convey("When unmarshaling and marshaling NumberFloat values", t, func() { 39 | key := "key" 40 | 41 | Convey("maintains decimal point with trailing zero", func() { 42 | var jsonMap map[string]interface{} 43 | 44 | value := "5.0" 45 | data := fmt.Sprintf(`{"%v":%v}`, key, value) 46 | 47 | err := Unmarshal([]byte(data), &jsonMap) 48 | So(err, ShouldBeNil) 49 | 50 | jsonValue, ok := jsonMap[key].(float64) 51 | So(ok, ShouldBeTrue) 52 | So(jsonValue, ShouldEqual, NumberFloat(5.0)) 53 | 54 | numFloat := NumberFloat(jsonValue) 55 | byteValue, err := numFloat.MarshalJSON() 56 | So(err, ShouldBeNil) 57 | So(string(byteValue), ShouldEqual, "5.0") 58 | 59 | }) 60 | 61 | Convey("maintains precision with large decimals", func() { 62 | var jsonMap map[string]interface{} 63 | 64 | value := "5.52342123" 65 | data := fmt.Sprintf(`{"%v":%v}`, key, value) 66 | 67 | err := Unmarshal([]byte(data), &jsonMap) 68 | So(err, ShouldBeNil) 69 | 70 | jsonValue, ok := jsonMap[key].(float64) 71 | So(ok, ShouldBeTrue) 72 | So(jsonValue, ShouldEqual, NumberFloat(5.52342123)) 73 | 74 | numFloat := NumberFloat(jsonValue) 75 | byteValue, err := numFloat.MarshalJSON() 76 | So(err, ShouldBeNil) 77 | So(string(byteValue), ShouldEqual, "5.52342123") 78 | 79 | }) 80 | 81 | Convey("maintains exponent values", func() { 82 | var jsonMap map[string]interface{} 83 | 84 | value := "5e+32" 85 | data := fmt.Sprintf(`{"%v":%v}`, key, value) 86 | 87 | err := Unmarshal([]byte(data), &jsonMap) 88 | So(err, ShouldBeNil) 89 | 90 | jsonValue, ok := jsonMap[key].(float64) 91 | So(ok, ShouldBeTrue) 92 | So(jsonValue, ShouldEqual, NumberFloat(5e32)) 93 | 94 | numFloat := NumberFloat(jsonValue) 95 | byteValue, err := numFloat.MarshalJSON() 96 | So(err, ShouldBeNil) 97 | So(string(byteValue), ShouldEqual, "5e+32") 98 | 99 | }) 100 | }) 101 | } 102 | -------------------------------------------------------------------------------- /internal/llmgo/testdb/server.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD9PlbW9OwAX7aB 3 | Nc/UkrKCMztP/YFceIlzoNEpWOWyFO09i4LeulN10Obp3zp3XstYSj5PZsJPgzNk 4 | mFIYC6f2l4W96F0SVEyvnvGzuPlXVBiPBp0xMGQtC4ogCDpwhI3Uo9TOlRNQqxYi 5 | xvH3uwDS3TCIQ+J9E5vud9IwhVCx3P9z0uVjZQ1gj7kaJTzyIMaDbCt2xrdT6XYb 6 | YpLH/24TdzmIWSLpt16q4uJaYFnqvF+hot7iCTUg2OJ8qyw2yfaLe4niLhOavc9R 7 | ziTHHSYwq5Yrcd2VCwyq2mr74dCYdK+w+tuByOX0fI8mIcOygn7g7ltu1wTnWhBs 8 | uHVtkCFjAgMBAAECggEASRAfRc1L+Z+jrAu2doIMdnwJdL6S//bW0UFolyFKw+I9 9 | wC/sBg6D3c3zkS4SVDZJPKPO7mGbVg1oWnGH3eAfCYoV0ACmOY+QwGp/GXcYmRVu 10 | MHWcDIEFpelaZHt7QNM9iEfsMd3YwMFblZUIYozVZADk66uKQMPTjS2Muur7qRSi 11 | wuVfSmsVZ5afH3B1Tr96BbmPsHrXLjvNpjO44k2wrnnSPQjUL7+YiZPvtnNW8Fby 12 | yuo2uoAyjg3+68PYZftOvvNneMsv1uyGlUs6Bk+DVWaqofIztWFdFZyXbHnK2PTk 13 | eGQt5EsL+RwIck5eoqd5vSE+KyzhhydL0zcpngVQoQKBgQD/Yelvholbz5NQtSy3 14 | ZoiW1y7hL1BKzvVNHuAMKJ5WOnj5szhjhKxt/wZ+hk0qcAmlV9WAPbf4izbEwPRC 15 | tnMBQzf1uBxqqbLL6WZ4YAyGrcX3UrT7GXsGfVT4zJjz7oYSw8aPircecw5V4exB 16 | xa4NF+ki8IycXSkHwvW2R56fRwKBgQD92xpxXtte/rUnmENbQmr0aKg7JEfMoih6 17 | MdX+f6mfgjMmqj+L4jPTI8/ql8HEy13SQS1534aDSHO+nBqBK5aHUCRMIgSLnTP9 18 | Xyx9Ngg03SZIkPfykqxQmnZgWkTPMhYS+K1Ao9FGVs8W5jVi7veyAdhHptAcxhP3 19 | IuxvrxVTBQKBgQCluMPiu0snaOwP04HRAZhhSgIB3tIbuXE1OnPpb/JPwmH+p25Q 20 | Jig+uN9d+4jXoRyhTv4c2fAoOS6xPwVCxWKbzyLhMTg/fx+ncy4rryhxvRJaDDGl 21 | QEO1Ul9xlFMs9/vI8YJIY5uxBrimwpStmbn4hSukoLSeQ1X802bfglpMwQKBgD8z 22 | GTY4Y20XBIrDAaHquy32EEwJEEcF6AXj+l7N8bDgfVOW9xMgUb6zH8RL29Xeu5Do 23 | 4SWCXL66fvZpbr/R1jwB28eIgJExpgvicfUKSqi+lhVi4hfmJDg8/FOopZDf61b1 24 | ykxZfHSCkDQnRAtJaylKBEpyYUWImtfgPfTgJfLxAoGAc8A/Tl2h/DsdTA+cA5d7 25 | 1e0l64m13ObruSWRczyru4hy8Yq6E/K2rOFw8cYCcFpy24NqNlk+2iXPLRpWm2zt 26 | 9R497zAPvhK/bfPXjvm0j/VjB44lvRTC9hby/RRMHy9UJk4o/UQaD+1IodxZovvk 27 | SruEA1+5bfBRMW0P+h7Qfe4= 28 | -----END PRIVATE KEY----- 29 | -----BEGIN CERTIFICATE----- 30 | MIIDjTCCAnWgAwIBAgIJAMW+wDfcdzC+MA0GCSqGSIb3DQEBCwUAMFwxCzAJBgNV 31 | BAYTAkdPMQwwCgYDVQQIDANNR08xDDAKBgNVBAcMA01HTzEMMAoGA1UECgwDTUdP 32 | MQ8wDQYDVQQLDAZTZXJ2ZXIxEjAQBgNVBAMMCWxvY2FsaG9zdDAgFw0xNTA5Mjkw 33 | ODM0MTBaGA8yMTE1MDkwNTA4MzQxMFowXDELMAkGA1UEBhMCR08xDDAKBgNVBAgM 34 | A01HTzEMMAoGA1UEBwwDTUdPMQwwCgYDVQQKDANNR08xDzANBgNVBAsMBlNlcnZl 35 | cjESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 36 | CgKCAQEA/T5W1vTsAF+2gTXP1JKygjM7T/2BXHiJc6DRKVjlshTtPYuC3rpTddDm 37 | 6d86d17LWEo+T2bCT4MzZJhSGAun9peFvehdElRMr57xs7j5V1QYjwadMTBkLQuK 38 | IAg6cISN1KPUzpUTUKsWIsbx97sA0t0wiEPifROb7nfSMIVQsdz/c9LlY2UNYI+5 39 | GiU88iDGg2wrdsa3U+l2G2KSx/9uE3c5iFki6bdequLiWmBZ6rxfoaLe4gk1INji 40 | fKssNsn2i3uJ4i4Tmr3PUc4kxx0mMKuWK3HdlQsMqtpq++HQmHSvsPrbgcjl9HyP 41 | JiHDsoJ+4O5bbtcE51oQbLh1bZAhYwIDAQABo1AwTjAdBgNVHQ4EFgQUhku/u9Kd 42 | OAc1L0OR649vCCuQT+0wHwYDVR0jBBgwFoAUhku/u9KdOAc1L0OR649vCCuQT+0w 43 | DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAw7Bgw3hlWXWSZjLhnSOu 44 | 2mW/UJ2Sj31unHngmgtXwW/04cyzoULb+qmzPe/Z06QMgGIsku1jFBcu0JabQtUG 45 | TyalpfW77tfnvz238CYdImYwE9ZcIGuZGfhs6ySFN9XpW43B8YM7R8wTNPvOcSPw 46 | nfjqU6kueN4TTspQg9cKhDss5DcMTIdgJgLbITXhIsrCu6GlKOgtX3HrdMGpQX7s 47 | UoMXtZVG8pK32vxKWGTZ6DPqESeKjjq74NbYnB3H5U/kDU2dt7LF90C/Umdr9y+C 48 | W2OJb1WBrf6RTcbt8D6d7P9kOfLPOtyn/cbaA/pfXBMQMHqr7XNXzjnaNU+jB7hL 49 | yQ== 50 | -----END CERTIFICATE----- 51 | -------------------------------------------------------------------------------- /internal/legacy/json/dbpointer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | "github.com/mongodb-labs/mongoreplay/internal/legacy/testtype" 12 | . "github.com/smartystreets/goconvey/convey" 13 | "gopkg.in/mgo.v2/bson" 14 | "testing" 15 | ) 16 | 17 | func TestDBPointerValue(t *testing.T) { 18 | testtype.SkipUnlessTestType(t, testtype.UnitTestType) 19 | 20 | Convey("Unmarshalling JSON with DBPointer values", t, func() { 21 | key := "key" 22 | value := `DBPointer("ref", ObjectId("552ffe9f5739878e73d116a9"))` 23 | 24 | Convey("works for a single key", func() { 25 | var jsonMap map[string]interface{} 26 | 27 | data := fmt.Sprintf(`{"%v":%v}`, key, value) 28 | 29 | err := Unmarshal([]byte(data), &jsonMap) 30 | So(err, ShouldBeNil) 31 | 32 | jsonValue, ok := jsonMap[key].(DBPointer) 33 | So(ok, ShouldBeTrue) 34 | So(jsonValue, ShouldResemble, DBPointer{"ref", bson.ObjectIdHex("552ffe9f5739878e73d116a9")}) 35 | }) 36 | 37 | Convey("works for multiple keys", func() { 38 | var jsonMap map[string]interface{} 39 | 40 | key1, key2, key3 := "key1", "key2", "key3" 41 | value2 := `DBPointer("ref2", ObjectId("552ffed95739878e73d116aa"))` 42 | value3 := `DBPointer("ref3", ObjectId("552fff215739878e73d116ab"))` 43 | data := fmt.Sprintf(`{"%v":%v,"%v":%v,"%v":%v}`, 44 | key1, value, key2, value2, key3, value3) 45 | 46 | err := Unmarshal([]byte(data), &jsonMap) 47 | So(err, ShouldBeNil) 48 | 49 | jsonValue1, ok := jsonMap[key1].(DBPointer) 50 | So(ok, ShouldBeTrue) 51 | 52 | So(jsonValue1, ShouldResemble, DBPointer{"ref", bson.ObjectIdHex("552ffe9f5739878e73d116a9")}) 53 | 54 | jsonValue2, ok := jsonMap[key2].(DBPointer) 55 | So(ok, ShouldBeTrue) 56 | So(jsonValue2, ShouldResemble, DBPointer{"ref2", bson.ObjectIdHex("552ffed95739878e73d116aa")}) 57 | 58 | jsonValue3, ok := jsonMap[key3].(DBPointer) 59 | So(ok, ShouldBeTrue) 60 | So(jsonValue3, ShouldResemble, DBPointer{"ref3", bson.ObjectIdHex("552fff215739878e73d116ab")}) 61 | }) 62 | 63 | Convey("works in an array", func() { 64 | var jsonMap map[string]interface{} 65 | 66 | data := fmt.Sprintf(`{"%v":[%v,%v,%v]}`, 67 | key, value, value, value) 68 | 69 | err := Unmarshal([]byte(data), &jsonMap) 70 | So(err, ShouldBeNil) 71 | jsonArray, ok := jsonMap[key].([]interface{}) 72 | So(ok, ShouldBeTrue) 73 | 74 | for _, _jsonValue := range jsonArray { 75 | jsonValue, ok := _jsonValue.(DBPointer) 76 | So(ok, ShouldBeTrue) 77 | So(jsonValue, ShouldResemble, DBPointer{"ref", bson.ObjectIdHex("552ffe9f5739878e73d116a9")}) 78 | } 79 | }) 80 | 81 | Convey("will not accept an $id type that is not an ObjectId", func() { 82 | value := `DBPointer("ref", 4)` 83 | var jsonMap map[string]interface{} 84 | 85 | data := fmt.Sprintf(`{"%v":%v}`, key, value) 86 | 87 | err := Unmarshal([]byte(data), &jsonMap) 88 | So(err, ShouldNotBeNil) 89 | }) 90 | 91 | }) 92 | } 93 | -------------------------------------------------------------------------------- /mongoreplay/message.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package mongoreplay 8 | 9 | import ( 10 | "fmt" 11 | 12 | "io" 13 | ) 14 | 15 | // MsgHeaderLen is the message header length in bytes 16 | const MsgHeaderLen = 16 17 | 18 | // MaxMessageSize is the maximum message size as defined in the server 19 | const MaxMessageSize = 48 * 1000 * 1000 20 | 21 | // MsgHeader is the mongo MessageHeader 22 | type MsgHeader struct { 23 | // MessageLength is the total message size, including this header 24 | MessageLength int32 25 | // RequestID is the identifier for this message 26 | RequestID int32 27 | // ResponseTo is the RequestID of the message being responded to; 28 | // used in DB responses 29 | ResponseTo int32 30 | // OpCode is the request type, see consts above. 31 | OpCode OpCode 32 | } 33 | 34 | // ReadHeader creates a new MsgHeader given a reader at the beginning of a 35 | // message. 36 | func ReadHeader(r io.Reader) (*MsgHeader, error) { 37 | var d [MsgHeaderLen]byte 38 | b := d[:] 39 | if _, err := io.ReadFull(r, b); err != nil { 40 | return nil, err 41 | } 42 | h := MsgHeader{} 43 | h.FromWire(b) 44 | return &h, nil 45 | } 46 | 47 | // ToWire converts the MsgHeader to the wire protocol 48 | func (m MsgHeader) ToWire() []byte { 49 | var d [MsgHeaderLen]byte 50 | b := d[:] 51 | SetInt32(b, 0, m.MessageLength) 52 | SetInt32(b, 4, m.RequestID) 53 | SetInt32(b, 8, m.ResponseTo) 54 | SetInt32(b, 12, int32(m.OpCode)) 55 | return b 56 | } 57 | 58 | // FromWire reads the wirebytes into this object 59 | func (m *MsgHeader) FromWire(b []byte) { 60 | m.MessageLength = getInt32(b, 0) 61 | m.RequestID = getInt32(b, 4) 62 | m.ResponseTo = getInt32(b, 8) 63 | m.OpCode = OpCode(getInt32(b, 12)) 64 | } 65 | 66 | // WriteTo writes the MsgHeader into a writer. 67 | func (m *MsgHeader) WriteTo(w io.Writer) (int64, error) { 68 | b := m.ToWire() 69 | c, err := w.Write(b) 70 | n := int64(c) 71 | if err != nil { 72 | return n, err 73 | } 74 | if c != len(b) { 75 | return n, fmt.Errorf("attempted to write %d but wrote %d", len(b), n) 76 | } 77 | return n, nil 78 | } 79 | 80 | // LooksReal does a best effort to detect if a MsgHeader is valid 81 | func (m *MsgHeader) LooksReal() bool { 82 | // AFAIK, the smallest wire protocol message possible is a 24 byte 83 | // KILL_CURSORS_OP 84 | if m.MessageLength > MaxMessageSize || m.MessageLength < 24 { 85 | return false 86 | } 87 | if m.RequestID < 0 { 88 | return false 89 | } 90 | if m.ResponseTo < 0 { 91 | return false 92 | } 93 | return goodOpCode[int32(m.OpCode)] 94 | } 95 | 96 | // String returns a string representation of the message header. 97 | // Useful for debugging. 98 | func (m *MsgHeader) String() string { 99 | return fmt.Sprintf( 100 | "opCode:%s (%d) msgLen:%d reqID:%d respID:%d", 101 | m.OpCode, 102 | m.OpCode, 103 | m.MessageLength, 104 | m.RequestID, 105 | m.ResponseTo, 106 | ) 107 | } 108 | -------------------------------------------------------------------------------- /mongoreplay/delete_op.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package mongoreplay 8 | 9 | import ( 10 | "encoding/json" 11 | "fmt" 12 | mgo "github.com/mongodb-labs/mongoreplay/internal/llmgo" 13 | "github.com/mongodb-labs/mongoreplay/internal/llmgo/bson" 14 | 15 | "io" 16 | 17 | ) 18 | 19 | // DeleteOp is used to remove one or more documents from a collection. 20 | // http://docs.mongodb.org/meta-driver/latest/legacy/mongodb-wire-protocol/#op-delete 21 | type DeleteOp struct { 22 | Header MsgHeader 23 | mgo.DeleteOp 24 | } 25 | 26 | // Meta returns metadata about the operation, useful for analysis of traffic. 27 | func (op *DeleteOp) Meta() OpMetadata { 28 | return OpMetadata{ 29 | "Delete", 30 | op.Collection, 31 | "", 32 | op.Selector, 33 | } 34 | } 35 | 36 | func (op *DeleteOp) String() string { 37 | body, err := op.getOpBodyString() 38 | if err != nil { 39 | return fmt.Sprintf("%v", err) 40 | } 41 | return fmt.Sprintf("DeleteOp %v %v", op.Collection, body) 42 | } 43 | 44 | func (op *DeleteOp) getOpBodyString() (string, error) { 45 | jsonDoc, err := ConvertBSONValueToJSON(op.Selector) 46 | if err != nil { 47 | return "", fmt.Errorf("%#v - %v", op, err) 48 | } 49 | selectorAsJSON, _ := json.Marshal(jsonDoc) 50 | return string(selectorAsJSON), nil 51 | } 52 | 53 | // Abbreviated returns a serialization of the DeleteOp, abbreviated so it 54 | // doesn't exceed the given number of characters. 55 | func (op *DeleteOp) Abbreviated(chars int) string { 56 | body, err := op.getOpBodyString() 57 | if err != nil { 58 | return fmt.Sprintf("%v", err) 59 | } 60 | return fmt.Sprintf("DeleteOp %v %v", op.Collection, Abbreviate(body, chars)) 61 | } 62 | 63 | // OpCode returns the OpCode for DeleteOp. 64 | func (op *DeleteOp) OpCode() OpCode { 65 | return OpCodeDelete 66 | } 67 | 68 | // FromReader extracts data from a serialized DeleteOp into its concrete 69 | // structure. 70 | func (op *DeleteOp) FromReader(r io.Reader) error { 71 | var b [4]byte 72 | _, err := io.ReadFull(r, b[:]) //skip ZERO 73 | if err != nil { 74 | return err 75 | } 76 | name, err := readCStringFromReader(r) 77 | if err != nil { 78 | return err 79 | } 80 | op.Collection = string(name) 81 | _, err = io.ReadFull(r, b[:]) //Grab the flags 82 | if err != nil { 83 | return err 84 | } 85 | op.Flags = uint32(getInt32(b[:], 0)) 86 | 87 | selectorAsSlice, err := ReadDocument(r) 88 | if err != nil { 89 | return err 90 | } 91 | op.Selector = &bson.D{} 92 | err = bson.Unmarshal(selectorAsSlice, op.Selector) 93 | 94 | if err != nil { 95 | return err 96 | } 97 | return nil 98 | } 99 | 100 | // Execute performs the DeleteOp on a given session, yielding the reply when 101 | // successful (and an error otherwise). 102 | func (op *DeleteOp) Execute(socket *mgo.MongoSocket) (Replyable, error) { 103 | if err := mgo.ExecOpWithoutReply(socket, &op.DeleteOp); err != nil { 104 | return nil, err 105 | } 106 | return nil, nil 107 | } 108 | -------------------------------------------------------------------------------- /internal/legacy/json/infinity_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | "github.com/mongodb-labs/mongoreplay/internal/legacy/testtype" 12 | . "github.com/smartystreets/goconvey/convey" 13 | "math" 14 | "testing" 15 | ) 16 | 17 | func TestInfinityValue(t *testing.T) { 18 | testtype.SkipUnlessTestType(t, testtype.UnitTestType) 19 | 20 | Convey("When unmarshalling JSON with Infinity values", t, func() { 21 | 22 | Convey("works for a single key", func() { 23 | var jsonMap map[string]interface{} 24 | 25 | key := "key" 26 | value := "Infinity" 27 | data := fmt.Sprintf(`{"%v":%v}`, key, value) 28 | 29 | err := Unmarshal([]byte(data), &jsonMap) 30 | So(err, ShouldBeNil) 31 | 32 | jsonValue, ok := jsonMap[key].(float64) 33 | So(ok, ShouldBeTrue) 34 | So(math.IsInf(jsonValue, 1), ShouldBeTrue) 35 | }) 36 | 37 | Convey("works for multiple keys", func() { 38 | var jsonMap map[string]interface{} 39 | 40 | key1, key2, key3 := "key1", "key2", "key3" 41 | value := "Infinity" 42 | data := fmt.Sprintf(`{"%v":%v,"%v":%v,"%v":%v}`, 43 | key1, value, key2, value, key3, value) 44 | 45 | err := Unmarshal([]byte(data), &jsonMap) 46 | So(err, ShouldBeNil) 47 | 48 | jsonValue1, ok := jsonMap[key1].(float64) 49 | So(ok, ShouldBeTrue) 50 | So(math.IsInf(jsonValue1, 1), ShouldBeTrue) 51 | 52 | jsonValue2, ok := jsonMap[key2].(float64) 53 | So(ok, ShouldBeTrue) 54 | So(math.IsInf(jsonValue2, 1), ShouldBeTrue) 55 | 56 | jsonValue3, ok := jsonMap[key3].(float64) 57 | So(ok, ShouldBeTrue) 58 | So(math.IsInf(jsonValue3, 1), ShouldBeTrue) 59 | }) 60 | 61 | Convey("works in an array", func() { 62 | var jsonMap map[string]interface{} 63 | 64 | key := "key" 65 | value := "Infinity" 66 | data := fmt.Sprintf(`{"%v":[%v,%v,%v]}`, 67 | key, value, value, value) 68 | 69 | err := Unmarshal([]byte(data), &jsonMap) 70 | So(err, ShouldBeNil) 71 | 72 | jsonArray, ok := jsonMap[key].([]interface{}) 73 | So(ok, ShouldBeTrue) 74 | 75 | for _, _jsonValue := range jsonArray { 76 | jsonValue, ok := _jsonValue.(float64) 77 | So(ok, ShouldBeTrue) 78 | So(math.IsInf(jsonValue, 1), ShouldBeTrue) 79 | } 80 | }) 81 | 82 | Convey("can have a sign ('+' or '-')", func() { 83 | var jsonMap map[string]interface{} 84 | 85 | key := "key" 86 | value := "Infinity" 87 | data := fmt.Sprintf(`{"%v":+%v}`, key, value) 88 | 89 | err := Unmarshal([]byte(data), &jsonMap) 90 | So(err, ShouldBeNil) 91 | 92 | jsonValue, ok := jsonMap[key].(float64) 93 | So(ok, ShouldBeTrue) 94 | So(math.IsInf(jsonValue, 1), ShouldBeTrue) 95 | 96 | data = fmt.Sprintf(`{"%v":-%v}`, key, value) 97 | 98 | err = Unmarshal([]byte(data), &jsonMap) 99 | So(err, ShouldBeNil) 100 | 101 | jsonValue, ok = jsonMap[key].(float64) 102 | So(ok, ShouldBeTrue) 103 | So(math.IsInf(jsonValue, -1), ShouldBeTrue) 104 | }) 105 | }) 106 | } 107 | -------------------------------------------------------------------------------- /internal/legacy/json/date_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | "github.com/mongodb-labs/mongoreplay/internal/legacy/testtype" 12 | . "github.com/smartystreets/goconvey/convey" 13 | "testing" 14 | ) 15 | 16 | func TestDateValue(t *testing.T) { 17 | testtype.SkipUnlessTestType(t, testtype.UnitTestType) 18 | 19 | Convey("When unmarshalling JSON with Date values", t, func() { 20 | 21 | Convey("works for a single key", func() { 22 | var jsonMap map[string]interface{} 23 | 24 | key := "key" 25 | value := "Date(123)" 26 | data := fmt.Sprintf(`{"%v":%v}`, key, value) 27 | 28 | err := Unmarshal([]byte(data), &jsonMap) 29 | So(err, ShouldBeNil) 30 | 31 | jsonValue, ok := jsonMap[key].(Date) 32 | So(ok, ShouldBeTrue) 33 | So(jsonValue, ShouldEqual, Date(123)) 34 | }) 35 | 36 | Convey("works for multiple keys", func() { 37 | var jsonMap map[string]interface{} 38 | 39 | key1, key2, key3 := "key1", "key2", "key3" 40 | value1, value2, value3 := "Date(123)", "Date(456)", "Date(789)" 41 | data := fmt.Sprintf(`{"%v":%v,"%v":%v,"%v":%v}`, 42 | key1, value1, key2, value2, key3, value3) 43 | 44 | err := Unmarshal([]byte(data), &jsonMap) 45 | So(err, ShouldBeNil) 46 | 47 | jsonValue1, ok := jsonMap[key1].(Date) 48 | So(ok, ShouldBeTrue) 49 | So(jsonValue1, ShouldEqual, Date(123)) 50 | 51 | jsonValue2, ok := jsonMap[key2].(Date) 52 | So(ok, ShouldBeTrue) 53 | So(jsonValue2, ShouldEqual, Date(456)) 54 | 55 | jsonValue3, ok := jsonMap[key3].(Date) 56 | So(ok, ShouldBeTrue) 57 | So(jsonValue3, ShouldEqual, Date(789)) 58 | }) 59 | 60 | Convey("works in an array", func() { 61 | var jsonMap map[string]interface{} 62 | 63 | key := "key" 64 | value := "Date(42)" 65 | data := fmt.Sprintf(`{"%v":[%v,%v,%v]}`, 66 | key, value, value, value) 67 | 68 | err := Unmarshal([]byte(data), &jsonMap) 69 | So(err, ShouldBeNil) 70 | 71 | jsonArray, ok := jsonMap[key].([]interface{}) 72 | So(ok, ShouldBeTrue) 73 | 74 | for _, _jsonValue := range jsonArray { 75 | jsonValue, ok := _jsonValue.(Date) 76 | So(ok, ShouldBeTrue) 77 | So(jsonValue, ShouldEqual, Date(42)) 78 | } 79 | }) 80 | 81 | Convey("cannot use string as argument", func() { 82 | var jsonMap map[string]interface{} 83 | 84 | key := "key" 85 | value := `Date("123")` 86 | data := fmt.Sprintf(`{"%v":%v}`, key, value) 87 | 88 | err := Unmarshal([]byte(data), &jsonMap) 89 | So(err, ShouldNotBeNil) 90 | }) 91 | 92 | Convey("can specify argument in hexadecimal", func() { 93 | var jsonMap map[string]interface{} 94 | 95 | key := "key" 96 | value := "Date(0x5f)" 97 | data := fmt.Sprintf(`{"%v":%v}`, key, value) 98 | 99 | err := Unmarshal([]byte(data), &jsonMap) 100 | So(err, ShouldBeNil) 101 | 102 | jsonValue, ok := jsonMap[key].(Date) 103 | So(ok, ShouldBeTrue) 104 | So(jsonValue, ShouldEqual, Date(0x5f)) 105 | }) 106 | }) 107 | } 108 | -------------------------------------------------------------------------------- /internal/legacy/json/frac_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | "github.com/mongodb-labs/mongoreplay/internal/legacy/testtype" 12 | . "github.com/smartystreets/goconvey/convey" 13 | "testing" 14 | ) 15 | 16 | func TestFractionalNumber(t *testing.T) { 17 | testtype.SkipUnlessTestType(t, testtype.UnitTestType) 18 | 19 | Convey("When unmarshalling JSON with fractional numeric values "+ 20 | "without a leading zero", t, func() { 21 | 22 | Convey("works for a single key", func() { 23 | var jsonMap map[string]interface{} 24 | 25 | key := "key" 26 | value := ".123" 27 | data := fmt.Sprintf(`{"%v":%v}`, key, value) 28 | 29 | err := Unmarshal([]byte(data), &jsonMap) 30 | So(err, ShouldBeNil) 31 | 32 | jsonValue, ok := jsonMap[key].(float64) 33 | So(ok, ShouldBeTrue) 34 | So(jsonValue, ShouldAlmostEqual, 0.123) 35 | }) 36 | 37 | Convey("works for multiple keys", func() { 38 | var jsonMap map[string]interface{} 39 | 40 | key1, key2, key3 := "key1", "key2", "key3" 41 | value1, value2, value3 := ".123", ".456", ".789" 42 | data := fmt.Sprintf(`{"%v":%v,"%v":%v,"%v":%v}`, 43 | key1, value1, key2, value2, key3, value3) 44 | 45 | err := Unmarshal([]byte(data), &jsonMap) 46 | So(err, ShouldBeNil) 47 | 48 | jsonValue1, ok := jsonMap[key1].(float64) 49 | So(ok, ShouldBeTrue) 50 | So(jsonValue1, ShouldAlmostEqual, 0.123) 51 | 52 | jsonValue2, ok := jsonMap[key2].(float64) 53 | So(ok, ShouldBeTrue) 54 | So(jsonValue2, ShouldAlmostEqual, 0.456) 55 | 56 | jsonValue3, ok := jsonMap[key3].(float64) 57 | So(ok, ShouldBeTrue) 58 | So(jsonValue3, ShouldAlmostEqual, 0.789) 59 | }) 60 | 61 | Convey("works in an array", func() { 62 | var jsonMap map[string]interface{} 63 | 64 | key := "key" 65 | value := ".42" 66 | data := fmt.Sprintf(`{"%v":[%v,%v,%v]}`, 67 | key, value, value, value) 68 | 69 | err := Unmarshal([]byte(data), &jsonMap) 70 | So(err, ShouldBeNil) 71 | 72 | jsonArray, ok := jsonMap[key].([]interface{}) 73 | So(ok, ShouldBeTrue) 74 | 75 | for _, _jsonValue := range jsonArray { 76 | jsonValue, ok := _jsonValue.(float64) 77 | So(ok, ShouldBeTrue) 78 | So(jsonValue, ShouldAlmostEqual, 0.42) 79 | } 80 | }) 81 | 82 | Convey("can have a sign ('+' or '-')", func() { 83 | var jsonMap map[string]interface{} 84 | 85 | key := "key" 86 | value := ".106" 87 | data := fmt.Sprintf(`{"%v":+%v}`, key, value) 88 | 89 | err := Unmarshal([]byte(data), &jsonMap) 90 | So(err, ShouldBeNil) 91 | 92 | jsonValue, ok := jsonMap[key].(float64) 93 | So(ok, ShouldBeTrue) 94 | So(jsonValue, ShouldAlmostEqual, 0.106) 95 | 96 | data = fmt.Sprintf(`{"%v":-%v}`, key, value) 97 | 98 | err = Unmarshal([]byte(data), &jsonMap) 99 | So(err, ShouldBeNil) 100 | 101 | jsonValue, ok = jsonMap[key].(float64) 102 | So(ok, ShouldBeTrue) 103 | So(jsonValue, ShouldAlmostEqual, -0.106) 104 | }) 105 | }) 106 | } 107 | -------------------------------------------------------------------------------- /internal/llmgo/internal/sasl/sspi_windows.h: -------------------------------------------------------------------------------- 1 | // Code adapted from the NodeJS kerberos library: 2 | // 3 | // https://github.com/christkv/kerberos/tree/master/lib/win32/kerberos_sspi.h 4 | // 5 | // Under the terms of the Apache License, Version 2.0: 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | #ifndef SSPI_WINDOWS_H 10 | #define SSPI_WINDOWS_H 11 | 12 | #define SECURITY_WIN32 1 13 | 14 | #include 15 | #include 16 | 17 | int load_secur32_dll(); 18 | 19 | SECURITY_STATUS SEC_ENTRY call_sspi_encrypt_message(PCtxtHandle phContext, unsigned long fQOP, PSecBufferDesc pMessage, unsigned long MessageSeqNo); 20 | 21 | typedef DWORD (WINAPI *encryptMessage_fn)(PCtxtHandle phContext, ULONG fQOP, PSecBufferDesc pMessage, ULONG MessageSeqNo); 22 | 23 | SECURITY_STATUS SEC_ENTRY call_sspi_acquire_credentials_handle( 24 | LPSTR pszPrincipal, // Name of principal 25 | LPSTR pszPackage, // Name of package 26 | unsigned long fCredentialUse, // Flags indicating use 27 | void *pvLogonId, // Pointer to logon ID 28 | void *pAuthData, // Package specific data 29 | SEC_GET_KEY_FN pGetKeyFn, // Pointer to GetKey() func 30 | void *pvGetKeyArgument, // Value to pass to GetKey() 31 | PCredHandle phCredential, // (out) Cred Handle 32 | PTimeStamp ptsExpiry // (out) Lifetime (optional) 33 | ); 34 | 35 | typedef DWORD (WINAPI *acquireCredentialsHandle_fn)( 36 | LPSTR pszPrincipal, LPSTR pszPackage, unsigned long fCredentialUse, 37 | void *pvLogonId, void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pvGetKeyArgument, 38 | PCredHandle phCredential, PTimeStamp ptsExpiry 39 | ); 40 | 41 | SECURITY_STATUS SEC_ENTRY call_sspi_initialize_security_context( 42 | PCredHandle phCredential, // Cred to base context 43 | PCtxtHandle phContext, // Existing context (OPT) 44 | LPSTR pszTargetName, // Name of target 45 | unsigned long fContextReq, // Context Requirements 46 | unsigned long Reserved1, // Reserved, MBZ 47 | unsigned long TargetDataRep, // Data rep of target 48 | PSecBufferDesc pInput, // Input Buffers 49 | unsigned long Reserved2, // Reserved, MBZ 50 | PCtxtHandle phNewContext, // (out) New Context handle 51 | PSecBufferDesc pOutput, // (inout) Output Buffers 52 | unsigned long *pfContextAttr, // (out) Context attrs 53 | PTimeStamp ptsExpiry // (out) Life span (OPT) 54 | ); 55 | 56 | typedef DWORD (WINAPI *initializeSecurityContext_fn)( 57 | PCredHandle phCredential, PCtxtHandle phContext, LPSTR pszTargetName, unsigned long fContextReq, 58 | unsigned long Reserved1, unsigned long TargetDataRep, PSecBufferDesc pInput, unsigned long Reserved2, 59 | PCtxtHandle phNewContext, PSecBufferDesc pOutput, unsigned long *pfContextAttr, PTimeStamp ptsExpiry); 60 | 61 | SECURITY_STATUS SEC_ENTRY call_sspi_query_context_attributes( 62 | PCtxtHandle phContext, // Context to query 63 | unsigned long ulAttribute, // Attribute to query 64 | void *pBuffer // Buffer for attributes 65 | ); 66 | 67 | typedef DWORD (WINAPI *queryContextAttributes_fn)( 68 | PCtxtHandle phContext, unsigned long ulAttribute, void *pBuffer); 69 | 70 | #endif // SSPI_WINDOWS_H 71 | -------------------------------------------------------------------------------- /mongoreplay/insert_op.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package mongoreplay 8 | 9 | import ( 10 | "encoding/json" 11 | "fmt" 12 | 13 | "io" 14 | 15 | mgo "github.com/mongodb-labs/mongoreplay/internal/llmgo" 16 | "github.com/mongodb-labs/mongoreplay/internal/llmgo/bson" 17 | ) 18 | 19 | // InsertOp is used to insert one or more documents into a collection. 20 | // http://docs.mongodb.org/meta-driver/latest/legacy/mongodb-wire-protocol/#op-insert 21 | type InsertOp struct { 22 | Header MsgHeader 23 | mgo.InsertOp 24 | } 25 | 26 | // Meta returns metadata about the InsertOp, useful for analysis of traffic. 27 | func (op *InsertOp) Meta() OpMetadata { 28 | return OpMetadata{"insert", op.Collection, "", op.Documents} 29 | } 30 | 31 | // OpCode returns the OpCode for the InsertOp. 32 | func (op *InsertOp) OpCode() OpCode { 33 | return OpCodeInsert 34 | } 35 | 36 | func (op *InsertOp) String() string { 37 | body, err := op.getOpBodyString() 38 | if err != nil { 39 | return fmt.Sprintf("%v", err) 40 | } 41 | return fmt.Sprintf("InsertOp %v %v", op.Collection, body) 42 | } 43 | 44 | func (op *InsertOp) getOpBodyString() (string, error) { 45 | docs := make([]string, 0, len(op.Documents)) 46 | for _, d := range op.Documents { 47 | jsonDoc, err := ConvertBSONValueToJSON(d) 48 | if err != nil { 49 | return "", fmt.Errorf("%#v - %v", op, err) 50 | } 51 | asJSON, _ := json.Marshal(jsonDoc) 52 | docs = append(docs, string(asJSON)) 53 | } 54 | return fmt.Sprintf("%v", docs), nil 55 | } 56 | 57 | // Abbreviated returns a serialization of the InsertOp, abbreviated so it 58 | // doesn't exceed the given number of characters. 59 | func (op *InsertOp) Abbreviated(chars int) string { 60 | body, err := op.getOpBodyString() 61 | if err != nil { 62 | return fmt.Sprintf("%v", err) 63 | } 64 | return fmt.Sprintf("InsertOp %v %v", op.Collection, Abbreviate(body, chars)) 65 | } 66 | 67 | // FromReader extracts data from a serialized InsertOp into its concrete 68 | // structure. 69 | func (op *InsertOp) FromReader(r io.Reader) error { 70 | var b [4]byte 71 | _, err := io.ReadFull(r, b[:]) 72 | if err != nil { 73 | return err 74 | } 75 | op.Flags = uint32(getInt32(b[:], 0)) 76 | name, err := readCStringFromReader(r) 77 | if err != nil { 78 | return err 79 | } 80 | op.Collection = string(name) 81 | op.Documents = make([]interface{}, 0) 82 | 83 | docLen := 0 84 | for len(name)+1+4+docLen < int(op.Header.MessageLength)-MsgHeaderLen { 85 | docAsSlice, err := ReadDocument(r) 86 | doc := &bson.D{} 87 | err = bson.Unmarshal(docAsSlice, doc) 88 | if err != nil { 89 | return err 90 | } 91 | docLen += len(docAsSlice) 92 | op.Documents = append(op.Documents, doc) 93 | } 94 | return nil 95 | } 96 | 97 | // Execute performs the InsertOp on a given socket, yielding the reply when 98 | // successful (and an error otherwise). 99 | func (op *InsertOp) Execute(socket *mgo.MongoSocket) (Replyable, error) { 100 | if err := mgo.ExecOpWithoutReply(socket, &op.InsertOp); err != nil { 101 | return nil, err 102 | } 103 | 104 | return nil, nil 105 | } 106 | -------------------------------------------------------------------------------- /internal/legacy/json/tagkey_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | // Based on github.com/golang/go by The Go Authors 8 | // See THIRD-PARTY-NOTICES for original license terms. 9 | 10 | package json 11 | 12 | import ( 13 | "github.com/mongodb-labs/mongoreplay/internal/legacy/testtype" 14 | "testing" 15 | ) 16 | 17 | type basicLatin2xTag struct { 18 | V string `json:"$%-/"` 19 | } 20 | 21 | type basicLatin3xTag struct { 22 | V string `json:"0123456789"` 23 | } 24 | 25 | type basicLatin4xTag struct { 26 | V string `json:"ABCDEFGHIJKLMO"` 27 | } 28 | 29 | type basicLatin5xTag struct { 30 | V string `json:"PQRSTUVWXYZ_"` 31 | } 32 | 33 | type basicLatin6xTag struct { 34 | V string `json:"abcdefghijklmno"` 35 | } 36 | 37 | type basicLatin7xTag struct { 38 | V string `json:"pqrstuvwxyz"` 39 | } 40 | 41 | type miscPlaneTag struct { 42 | V string `json:"色は匂へど"` 43 | } 44 | 45 | type percentSlashTag struct { 46 | V string `json:"text/html%"` // http://golang.org/issue/2718 47 | } 48 | 49 | type punctuationTag struct { 50 | V string `json:"!#$%&()*+-./:<=>?@[]^_{|}~"` // http://golang.org/issue/3546 51 | } 52 | 53 | type emptyTag struct { 54 | W string 55 | } 56 | 57 | type misnamedTag struct { 58 | X string `jsom:"Misnamed"` 59 | } 60 | 61 | type badCodeTag struct { 62 | Z string `json:" !\"#&'()*+,."` 63 | } 64 | 65 | type spaceTag struct { 66 | Q string `json:"With space"` 67 | } 68 | 69 | type unicodeTag struct { 70 | W string `json:"Ελλάδα"` 71 | } 72 | 73 | var structTagObjectKeyTests = []struct { 74 | raw interface{} 75 | value string 76 | key string 77 | }{ 78 | {basicLatin2xTag{"2x"}, "2x", "$%-/"}, 79 | {basicLatin3xTag{"3x"}, "3x", "0123456789"}, 80 | {basicLatin4xTag{"4x"}, "4x", "ABCDEFGHIJKLMO"}, 81 | {basicLatin5xTag{"5x"}, "5x", "PQRSTUVWXYZ_"}, 82 | {basicLatin6xTag{"6x"}, "6x", "abcdefghijklmno"}, 83 | {basicLatin7xTag{"7x"}, "7x", "pqrstuvwxyz"}, 84 | {miscPlaneTag{"いろはにほへと"}, "いろはにほへと", "色は匂へど"}, 85 | {emptyTag{"Pour Moi"}, "Pour Moi", "W"}, 86 | {misnamedTag{"Animal Kingdom"}, "Animal Kingdom", "X"}, 87 | {badCodeTag{"Reliable Man"}, "Reliable Man", "Z"}, 88 | {percentSlashTag{"brut"}, "brut", "text/html%"}, 89 | {punctuationTag{"Union Rags"}, "Union Rags", "!#$%&()*+-./:<=>?@[]^_{|}~"}, 90 | {spaceTag{"Perreddu"}, "Perreddu", "With space"}, 91 | {unicodeTag{"Loukanikos"}, "Loukanikos", "Ελλάδα"}, 92 | } 93 | 94 | func TestStructTagObjectKey(t *testing.T) { 95 | testtype.SkipUnlessTestType(t, testtype.UnitTestType) 96 | for _, tt := range structTagObjectKeyTests { 97 | b, err := Marshal(tt.raw) 98 | if err != nil { 99 | t.Fatalf("Marshal(%#q) failed: %v", tt.raw, err) 100 | } 101 | var f interface{} 102 | err = Unmarshal(b, &f) 103 | if err != nil { 104 | t.Fatalf("Unmarshal(%#q) failed: %v", b, err) 105 | } 106 | for i, v := range f.(map[string]interface{}) { 107 | switch i { 108 | case tt.key: 109 | if s, ok := v.(string); !ok || s != tt.value { 110 | t.Fatalf("Unexpected value: %#q, want %v", s, tt.value) 111 | } 112 | default: 113 | t.Fatalf("Unexpected key: %#q, from %#q", i, b) 114 | } 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /internal/llmgo/testdb/client.pem: -------------------------------------------------------------------------------- 1 | To regenerate the key: 2 | 3 | openssl req -newkey rsa:2048 -new -x509 -days 36500 -nodes -out server.crt -keyout server.key 4 | cat server.key server.crt > server.pem 5 | openssl genrsa -out client.key 2048 6 | openssl req -key client.key -new -out client.req 7 | openssl x509 -req -in client.req -CA server.crt -CAkey server.key -days 36500 -CAserial file.srl -out client.crt 8 | cat client.key client.crt > client.pem 9 | 10 | -----BEGIN RSA PRIVATE KEY----- 11 | MIIEogIBAAKCAQEAtFIkIZk/h+CCKq5/EjBEg873Jd68CJsFKESB5Zl5KLwiGQm7 12 | wQidZwLul+cyDfPRDzzo3za4GetesD4FVf2BEF6fg+/o0wLBObPCXqUVxXXnEXrJ 13 | r4f/tItg0riOEBbLslQDzNTtCAEORCoK9MHmWZrF+pYTw+LmHoVeA8QxNIv/GkwJ 14 | Q6DYEQgCa2BTIWq0Uw3WO20M3e2WGm/6Sv9w0pjisZfwBSfBJ5nI/cNW7L8tH4AI 15 | KBhAZwa7vND0RaRYqpO9kyZFzh8e83GBaXoLSj2wK3kwjKHWgp4z//37JAqeFya5 16 | Hx+ftNTXnl/69TnxG44BP8M88ZfDWlpzwpsTXwIDAQABAoIBADzCjOAxZkHfuZyu 17 | La0wTHXpkEfXdJ6ltagq5WY7P6MlOYwcRoK152vlhgXzZl9jL6ely4YjRwec0swq 18 | KdwezpV4fOGVPmuTuw45bx47HEnr/49ZQ4p9FgF9EYQPofbz53FQc/NaMACJcogv 19 | bn+osniw+VMFrOVNmGLiZ5p3Smk8zfXE7GRHO8CL5hpWLWO/aK236yytbfWOjM2f 20 | Pr76ICb26TPRNzYaYUEThU6DtgdLU8pLnJ6QKKaDsjn+zqQzRa+Nvc0c0K8gvWwA 21 | Afq7t0325+uMSwfpLgCOFldcaZQ5uvteJ0CAVRq1MvStnSHBmMzPlgS+NzsDm6lp 22 | QH5+rIkCgYEA5j3jrWsv7TueTNbk8Hr/Zwywc+fA2Ex0pBURBHlHyc6ahSXWSCqo 23 | DtvRGX0GDoK1lCfaIf1qb/DLlGaoHpkEeqcNhXQ+hHs+bZAxfbfBY9+ikit5ZTtl 24 | QN1tIlhaiyLDnwhkpi/hMw1tiouxJUf84Io61z0sCL4hyZSPCpjn0H0CgYEAyH6F 25 | Mwl+bCD3VDL/Dr5WSoOr2B/M3bF5SfvdStwy2IPcDJ716je1Ud/2qFCnKGgqvWhJ 26 | +HU15c7CjAWo7/pXq2/pEMD8fDKTYww4Hr4p6duEA7DpbOGkwcUX8u3eknxUWT9F 27 | jOSbTCvAxuDOC1K3AElyMxVVTNUrFFe8M84R9gsCgYBXmb6RkdG3WlKde7m5gaLB 28 | K4PLZabq5RQQBe/mmtpkfxYtiLrh1FEC7kG9h+MRDExX5V3KRugDVUOv3+shUSjy 29 | HbM4ToUm1NloyE78PTj4bfMl2CKlEJcyucy3H5S7kWuKi5/31wnA6d/+sa2huKUP 30 | Lai7kgu5+9VRJBPUfV7d5QKBgCnhk/13TDtWH5QtGu5/gBMMskbxTaA5xHZZ8H4E 31 | xXJJCRxx0Dje7jduK145itF8AQGT2W/XPC0HJciOHh4TE2EyfWMMjTF8dyFHmimB 32 | 28uIGWmT+Q7Pi9UWUMxkOAwtgIksGGE4F+CvexOQPjpLSwL6VKqrGCh2lwsm0J+Z 33 | ulLFAoGAKlC93c6XEj1A31c1+usdEhUe9BrmTqtSYLYpDNpeMLdZ3VctrAZuOQPZ 34 | 4A4gkkQkqqwZGBYYSEqwqiLU6MsBdHPPZ9u3JXLLOQuh1xGeaKylvHj7qx6iT0Xo 35 | I+FkJ6/3JeMgOina/+wlzD4oyQpqR4Mnh+TuLkDfQTgY+Lg0WPk= 36 | -----END RSA PRIVATE KEY----- 37 | -----BEGIN CERTIFICATE----- 38 | MIIDLjCCAhYCAQcwDQYJKoZIhvcNAQELBQAwXDELMAkGA1UEBhMCR08xDDAKBgNV 39 | BAgMA01HTzEMMAoGA1UEBwwDTUdPMQwwCgYDVQQKDANNR08xDzANBgNVBAsMBlNl 40 | cnZlcjESMBAGA1UEAwwJbG9jYWxob3N0MCAXDTE1MDkyOTA4NDAzMFoYDzIxMTUw 41 | OTA1MDg0MDMwWjBcMQswCQYDVQQGEwJHTzEMMAoGA1UECAwDTUdPMQwwCgYDVQQH 42 | DANNR08xDDAKBgNVBAoMA01HTzEPMA0GA1UECwwGQ2xpZW50MRIwEAYDVQQDDAls 43 | b2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0UiQhmT+H 44 | 4IIqrn8SMESDzvcl3rwImwUoRIHlmXkovCIZCbvBCJ1nAu6X5zIN89EPPOjfNrgZ 45 | 616wPgVV/YEQXp+D7+jTAsE5s8JepRXFdecResmvh/+0i2DSuI4QFsuyVAPM1O0I 46 | AQ5EKgr0weZZmsX6lhPD4uYehV4DxDE0i/8aTAlDoNgRCAJrYFMharRTDdY7bQzd 47 | 7ZYab/pK/3DSmOKxl/AFJ8Enmcj9w1bsvy0fgAgoGEBnBru80PRFpFiqk72TJkXO 48 | Hx7zcYFpegtKPbAreTCModaCnjP//fskCp4XJrkfH5+01NeeX/r1OfEbjgE/wzzx 49 | l8NaWnPCmxNfAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFwYpje3dCLDOIHYjd+5 50 | CpFOEb+bJsS4ryqm/NblTjIhCLo58hNpMsBqdJHRbHAFRCOE8fvY8yiWtdHeFZcW 51 | DgVRAXfHONLtN7faZaZQnhy/YzOhLfC/8dUMB0gQA8KXhBCPZqQmexE28AfkEO47 52 | PwICAxIWINfjm5VnFMkA3b7bDNLHon/pev2m7HqVQ3pRUJQNK3XgFOdDgRrnuXpR 53 | OKAfHORHVGTh1gf1DVwc0oM+0gnkSiJ1VG0n5pE3zhZ24fmZxu6JQ6X515W7APQI 54 | /nKVH+f1Fo+ustyTNLt8Bwxi1XmwT7IXwnkVSE9Ff6VejppXRF01V0aaWsa3kU3r 55 | z3A= 56 | -----END CERTIFICATE----- 57 | 58 | -------------------------------------------------------------------------------- /internal/llmgo/log.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2015-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | // Based on gopkg.io/mgo.v2 by Gustavo Niemeyer. 8 | // See THIRD-PARTY-NOTICES for original license terms. 9 | 10 | package mgo 11 | 12 | import ( 13 | "fmt" 14 | "sync" 15 | ) 16 | 17 | // --------------------------------------------------------------------------- 18 | // Logging integration. 19 | 20 | // Avoid importing the log type information unnecessarily. There's a small cost 21 | // associated with using an interface rather than the type. Depending on how 22 | // often the logger is plugged in, it would be worth using the type instead. 23 | type log_Logger interface { 24 | Output(calldepth int, s string) error 25 | } 26 | 27 | var ( 28 | globalLogger log_Logger 29 | globalDebug bool 30 | globalMutex sync.Mutex 31 | ) 32 | 33 | // RACE WARNING: There are known data races when logging, which are manually 34 | // silenced when the race detector is in use. These data races won't be 35 | // observed in typical use, because logging is supposed to be set up once when 36 | // the application starts. Having raceDetector as a constant, the compiler 37 | // should elide the locks altogether in actual use. 38 | 39 | // Specify the *log.Logger object where log messages should be sent to. 40 | func SetLogger(logger log_Logger) { 41 | if raceDetector { 42 | globalMutex.Lock() 43 | defer globalMutex.Unlock() 44 | } 45 | globalLogger = logger 46 | } 47 | 48 | // Enable the delivery of debug messages to the logger. Only meaningful 49 | // if a logger is also set. 50 | func SetDebug(debug bool) { 51 | if raceDetector { 52 | globalMutex.Lock() 53 | defer globalMutex.Unlock() 54 | } 55 | globalDebug = debug 56 | } 57 | 58 | func log(v ...interface{}) { 59 | if raceDetector { 60 | globalMutex.Lock() 61 | defer globalMutex.Unlock() 62 | } 63 | if globalLogger != nil { 64 | globalLogger.Output(2, fmt.Sprint(v...)) 65 | } 66 | } 67 | 68 | func logln(v ...interface{}) { 69 | if raceDetector { 70 | globalMutex.Lock() 71 | defer globalMutex.Unlock() 72 | } 73 | if globalLogger != nil { 74 | globalLogger.Output(2, fmt.Sprintln(v...)) 75 | } 76 | } 77 | 78 | func logf(format string, v ...interface{}) { 79 | if raceDetector { 80 | globalMutex.Lock() 81 | defer globalMutex.Unlock() 82 | } 83 | if globalLogger != nil { 84 | globalLogger.Output(2, fmt.Sprintf(format, v...)) 85 | } 86 | } 87 | 88 | func debug(v ...interface{}) { 89 | if raceDetector { 90 | globalMutex.Lock() 91 | defer globalMutex.Unlock() 92 | } 93 | if globalDebug && globalLogger != nil { 94 | globalLogger.Output(2, fmt.Sprint(v...)) 95 | } 96 | } 97 | 98 | func debugln(v ...interface{}) { 99 | if raceDetector { 100 | globalMutex.Lock() 101 | defer globalMutex.Unlock() 102 | } 103 | if globalDebug && globalLogger != nil { 104 | globalLogger.Output(2, fmt.Sprintln(v...)) 105 | } 106 | } 107 | 108 | func debugf(format string, v ...interface{}) { 109 | if raceDetector { 110 | globalMutex.Lock() 111 | defer globalMutex.Unlock() 112 | } 113 | if globalDebug && globalLogger != nil { 114 | globalLogger.Output(2, fmt.Sprintf(format, v...)) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /mongoreplay/connection_stub.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package mongoreplay 8 | 9 | import ( 10 | "bytes" 11 | "net" 12 | "sync" 13 | "time" 14 | 15 | mgo "github.com/mongodb-labs/mongoreplay/internal/llmgo" 16 | ) 17 | 18 | // SessionStub mocks an MongoSession by implementing the AcquireSocketPrivate 19 | // method. It allows for tests to pass around a struct with stubbed fields that 20 | // can then be read later for testing. 21 | type SessionStub struct { 22 | startup sync.Once 23 | mgo.MongoSession 24 | connection ConnStub 25 | socket *mgo.MongoSocket 26 | } 27 | 28 | // ConnStub mocks the connection used by an mgo session. It implements the 29 | // net.Conn interface so that it may be used as a connection for testing in 30 | // llmgo It contains a write buffer and a read buffer. It writes into the write 31 | // buffer, and reads from the read buffer so that its ends may be given in 32 | // reverse to another function. (i.e., another function can write to its read 33 | // buffer and it will receive this as incoming data) 34 | type ConnStub struct { 35 | closed bool 36 | readBuffer *bytes.Buffer 37 | writeBuffer *bytes.Buffer 38 | } 39 | 40 | func (conn *ConnStub) Read(b []byte) (n int, err error) { 41 | return conn.readBuffer.Read(b) 42 | } 43 | 44 | func (conn *ConnStub) Write(b []byte) (n int, err error) { 45 | return conn.writeBuffer.Write(b) 46 | } 47 | 48 | // Close doesn't actually do anything, and is here to implement net.Conn. 49 | func (conn *ConnStub) Close() error { 50 | return nil 51 | } 52 | 53 | // LocalAddr doesn't actually do anything, and is here to implement net.Conn. 54 | func (conn *ConnStub) LocalAddr() net.Addr { 55 | return nil 56 | } 57 | 58 | // RemoteAddr doesn't actually do anything, and is here to implement net.Conn. 59 | func (conn *ConnStub) RemoteAddr() net.Addr { 60 | return nil 61 | } 62 | 63 | // SetDeadline doesn't actually do anything, and is here to implement net.Conn. 64 | func (conn *ConnStub) SetDeadline(t time.Time) error { 65 | return nil 66 | } 67 | 68 | // SetReadDeadline doesn't actually do anything, and is here to implement net.Conn. 69 | func (conn *ConnStub) SetReadDeadline(t time.Time) error { 70 | return nil 71 | } 72 | 73 | // SetWriteDeadline doesn't actually do anything, and is here to implement net.Conn. 74 | func (conn *ConnStub) SetWriteDeadline(t time.Time) error { 75 | return nil 76 | } 77 | 78 | // newTwoSidedConn makes two ConnStub's which use the same buffers but in 79 | // opposite roles. The read end of one buffer is handed to the other connection 80 | // as the write end and vice versa 81 | func newTwoSidedConn() (conn1 ConnStub, conn2 ConnStub) { 82 | buffer1 := &bytes.Buffer{} 83 | buffer2 := &bytes.Buffer{} 84 | conn1 = ConnStub{false, buffer1, buffer2} 85 | conn2 = ConnStub{false, buffer2, buffer1} 86 | return conn1, conn2 87 | } 88 | 89 | // AcquireSocketPrivate is an implementation of MongoSession's function that 90 | // allows for the a stubbed connection to the passed to the other operations of 91 | // llmgo for testing 92 | func (session *SessionStub) AcquireSocketPrivate(slaveOk bool) (*mgo.MongoSocket, error) { 93 | session.startup.Do(func() { 94 | session.socket = mgo.NewDumbSocket(&session.connection) 95 | }) 96 | return session.socket, nil 97 | } 98 | -------------------------------------------------------------------------------- /mongoreplay/play_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) MongoDB, Inc. 2014-present. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | package mongoreplay 8 | 9 | import ( 10 | "bytes" 11 | "io" 12 | "testing" 13 | "time" 14 | ) 15 | 16 | func TestRepeatGeneration(t *testing.T) { 17 | recOp := &RecordedOp{ 18 | Seen: &PreciseTime{time.Now()}, 19 | } 20 | 21 | var buf bytes.Buffer 22 | wc := NopWriteCloser(&buf) 23 | file, err := playbackFileWriterFromWriteCloser(wc, "", PlaybackFileMetadata{}) 24 | if err != nil { 25 | t.Fatalf("error creating playback file %v", err) 26 | } 27 | 28 | err = bsonToWriter(file, recOp) 29 | if err != nil { 30 | t.Fatalf("error writing to bson file %v", err) 31 | } 32 | 33 | rs := bytes.NewReader(buf.Bytes()) 34 | playbackReader, err := playbackFileReaderFromReadSeeker(rs, "") 35 | if err != nil { 36 | t.Fatalf("unable to read from playback file %v", err) 37 | } 38 | 39 | repeat := 2 40 | opChan, errChan := playbackReader.OpChan(repeat) 41 | op1, ok := <-opChan 42 | if !ok { 43 | err, ok := <-errChan 44 | if ok { 45 | t.Logf("error: %v", err) 46 | } 47 | t.Fatalf("read of 0-generation op failed") 48 | } 49 | if op1.Generation != 0 { 50 | t.Errorf("generation of 0 generation op is %v", op1.Generation) 51 | } 52 | op2, ok := <-opChan 53 | if !ok { 54 | t.Fatalf("read of 1-generation op failed") 55 | } 56 | if op2.Generation != 1 { 57 | t.Errorf("generation of 1 generation op is %v", op2.Generation) 58 | } 59 | _, ok = <-opChan 60 | if ok { 61 | t.Errorf("Successfully read past end of op chan") 62 | } 63 | err = <-errChan 64 | if err != io.EOF { 65 | t.Errorf("should have eof at end, but got %v", err) 66 | } 67 | } 68 | 69 | func TestPlayOpEOF(t *testing.T) { 70 | ops := []RecordedOp{{ 71 | Seen: &PreciseTime{time.Now()}, 72 | }, { 73 | Seen: &PreciseTime{time.Now()}, 74 | EOF: true, 75 | }} 76 | var buf bytes.Buffer 77 | wc := NopWriteCloser(&buf) 78 | file, err := playbackFileWriterFromWriteCloser(wc, "", PlaybackFileMetadata{}) 79 | if err != nil { 80 | t.Fatalf("error creating playback file %v", err) 81 | } 82 | 83 | for _, op := range ops { 84 | err := bsonToWriter(file, op) 85 | if err != nil { 86 | t.Fatalf("unable to write to playback file %v", err) 87 | } 88 | } 89 | 90 | rs := bytes.NewReader(buf.Bytes()) 91 | playbackReader, err := playbackFileReaderFromReadSeeker(rs, "") 92 | if err != nil { 93 | t.Fatalf("unable to read from playback file %v", err) 94 | } 95 | 96 | repeat := 2 97 | opChan, errChan := playbackReader.OpChan(repeat) 98 | 99 | op1, ok := <-opChan 100 | if !ok { 101 | t.Fatalf("read of op1 failed") 102 | } 103 | if op1.EOF { 104 | t.Errorf("op1 should not be an EOF op") 105 | } 106 | op2, ok := <-opChan 107 | if !ok { 108 | t.Fatalf("read op2 failed") 109 | } 110 | if op2.EOF { 111 | t.Errorf("op2 should not be an EOF op") 112 | } 113 | op3, ok := <-opChan 114 | if !ok { 115 | t.Errorf("read of op3 failed") 116 | } 117 | if !op3.EOF { 118 | t.Errorf("op3 is not an EOF op") 119 | } 120 | 121 | _, ok = <-opChan 122 | if ok { 123 | t.Errorf("Successfully read past end of op chan") 124 | } 125 | err = <-errChan 126 | if err != io.EOF { 127 | t.Errorf("should have eof at end, but got %v", err) 128 | } 129 | } 130 | --------------------------------------------------------------------------------