Files
remark42/backend/app/store/engine/mongo/server.go
T
UmputunandGitHub de013c7d5a feature/mongo (#165)
* WIP: start mongo engine

* WIP: mongo accessor and admin

* integrate mongo store to main

* disable mongo tests by default, only in CI

* connection with constructor

* add mongo buffered writer

* buffered mongo writer

* fix nil responses on an empty list from mongo

* missing mongo index for scores

* cancelable store

* add gridfs implementation of avatar store

* fix race on mongo session copy

* gridfs avatars without tmp files

* move avatar store

* minor comments and refactoring for avatar store

* merged from current master

* simplify gridfs reader

* lint: fix minor warns

* test mongo against env defined url

* pass MONGO_REMARK_TEST to docker and travis

* set dockerfile env for mongo test url

* increase connect timeout in mongo tests

* pass MONGO_REMARK_TEST to drone build

* add MONGO_REMARK_TEST to branch stage of drone

* mass mongo test url via build_args_from_env

* populate mongo IP to docker build hosts

* test env

* pass mongo ip via .mongo

* remove .mongo temp from git

* add .mongo -> env to linter step

* allow more time to autoflush writer test

* default mongo tests to "mongo" if not in env

* merge fresh master into

* add test for mongo cleanup

* msg for a failed test

* lazy fix for failed test

* add an ability to skip all mongo tests

* add backend dev instructions

* remove unused code from mongo server

* move mongo testing to connection_test

* restore testing.go

* lint: minor warns for testing code
2018-07-13 12:27:11 -05:00

122 lines
3.6 KiB
Go

// Package mongo wraps mgo to provide easier way to construct mongo server (with auth).
// Connection provides With* func warapers to run query with session copy
package mongo
import (
"crypto/tls"
"fmt"
"log"
"net"
"os"
"strings"
"time"
"github.com/globalsign/mgo"
"github.com/pkg/errors"
)
// Server represents mongo instance and provides session accessor
type Server struct {
dial mgo.DialInfo
params ServerParams
sess *mgo.Session
}
// ServerParams optional set of parameters
type ServerParams struct {
Delay int // initial delay to give mongo server some time to start, in case if mongo part of the same compose
Debug bool // turn on mgo debug mode
ConsistencyMode mgo.Mode
SSL bool
}
// NewServerWithURL makes mongo server from url like
// mongodb://remark42:password@127.0.0.1t:27017/test?ssl=true&replicaSet=Cluster0-shard-0&authSource=admin
func NewServerWithURL(url string, timeout time.Duration) (res *Server, err error) {
dial, params, err := parseURL(url, timeout)
if err != nil {
return nil, errors.Wrapf(err, "failed to create mongo server from url %s", url)
}
return NewServer(dial, params)
}
// NewServer doing auth if passwd != "" and can delay to make sure local mongo is up
func NewServer(dial mgo.DialInfo, params ServerParams) (res *Server, err error) {
log.Printf("[INFO] make new mongo server %v with %+v", dial.Addrs, params)
result := Server{dial: dial, params: params}
if params.Debug {
mgo.SetDebug(true)
mgo.SetLogger(log.New(os.Stdout, "MGO ", log.Ldate|log.Ltime|log.Lmicroseconds))
}
if len(dial.Addrs) == 0 {
return nil, errors.New("missing mongo address")
}
if params.Delay > 0 {
log.Printf("[DEBUG] initial mongo delay=%d", params.Delay)
time.Sleep(time.Duration(params.Delay) * time.Second)
}
log.Printf("[DEBUG] dial mongo %s, ssl=%v", dial.Addrs, params.SSL)
if params.SSL {
tlsConfig := &tls.Config{}
dial.DialServer = func(addr *mgo.ServerAddr) (net.Conn, error) {
conn, e := tls.Dial("tcp", addr.String(), tlsConfig)
return conn, e
}
}
session, err := mgo.DialWithInfo(&dial)
if err != nil {
err = fmt.Errorf("can't connect to mongo, %v", err)
log.Printf("[ERROR] %v", err)
return nil, err
}
session.SetMode(params.ConsistencyMode, true)
session.SetSyncTimeout(30 * time.Second)
session.SetSocketTimeout(dial.Timeout)
if dial.Username != "" && dial.Password != "" {
creds := &mgo.Credential{Username: dial.Username, Password: dial.Password, Source: dial.Source}
log.Printf("[DEBUG] login to mongo, user=%s, db=%s", creds.Username, creds.Source)
if err = session.Login(creds); err != nil {
log.Printf("[ERROR] can't login to mongo, %v", err)
return nil, err
}
}
result.sess = session
return &result, nil
}
// SessionCopy returns copy of main session. Client should close it
func (m Server) SessionCopy() *mgo.Session {
return m.sess.Copy()
}
func (m Server) String() string {
return fmt.Sprintf("%v%s", m.dial.Addrs, m.dial.Database)
}
// parseURL extends mgo with debug option and extracts ssl flag to make ServerParams
func parseURL(mongoURL string, connectTimeout time.Duration) (mgo.DialInfo, ServerParams, error) {
params := ServerParams{
ConsistencyMode: mgo.Monotonic,
SSL: strings.Contains(mongoURL, "ssl=true"),
Debug: strings.Contains(mongoURL, "debug=true"),
}
mongoURL = strings.Replace(mongoURL, "&debug=true", "", 1)
mongoURL = strings.Replace(mongoURL, "?debug=true", "", 1)
dial, err := mgo.ParseURL(mongoURL)
if err != nil {
return mgo.DialInfo{}, ServerParams{}, errors.Wrapf(err, "failed to pars mongo url %s", mongoURL)
}
dial.Timeout = connectTimeout
return *dial, params, nil
}