From c2e568f22e5fd89feefad7954234f8c5ab3519f3 Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Fri, 19 Jun 2020 12:43:03 +0200 Subject: [PATCH] Update github.com/titpetric/factory --- go.mod | 2 +- go.sum | 2 + .../github.com/titpetric/factory/.drone.yml | 5 +- vendor/github.com/titpetric/factory/Makefile | 5 +- .../github.com/titpetric/factory/database.go | 101 ++++++++++------- .../titpetric/factory/database_connector.go | 106 ++++++++++++++++++ vendor/modules.txt | 2 +- 7 files changed, 172 insertions(+), 51 deletions(-) create mode 100644 vendor/github.com/titpetric/factory/database_connector.go diff --git a/go.mod b/go.mod index 192c2433e..d70b32a57 100644 --- a/go.mod +++ b/go.mod @@ -51,7 +51,7 @@ require ( github.com/steinfletcher/apitest v1.3.8 github.com/steinfletcher/apitest-jsonpath v1.3.0 github.com/stretchr/testify v1.5.1 - github.com/titpetric/factory v0.0.0-20190806200833-ae4b02b9e034 + github.com/titpetric/factory v0.0.0-20190828134837-8466c9bef13f go.uber.org/atomic v1.5.0 go.uber.org/zap v1.13.0 golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 diff --git a/go.sum b/go.sum index 15ddecb98..4a5cc58d5 100644 --- a/go.sum +++ b/go.sum @@ -232,6 +232,8 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/titpetric/factory v0.0.0-20190806200833-ae4b02b9e034 h1:H2WfXOKTtF37QV4McGIRH37zNa00NgZjRJ/5wwxqSv8= github.com/titpetric/factory v0.0.0-20190806200833-ae4b02b9e034/go.mod h1:VFd2XRrQZoX9cOxpeZezKpOlXDwU/dbRejKLjwP+xY8= +github.com/titpetric/factory v0.0.0-20190828134837-8466c9bef13f h1:sTgdEUlmmNSU3QKe8n20jR26zYEKw6ciSuj919XobV4= +github.com/titpetric/factory v0.0.0-20190828134837-8466c9bef13f/go.mod h1:VFd2XRrQZoX9cOxpeZezKpOlXDwU/dbRejKLjwP+xY8= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= diff --git a/vendor/github.com/titpetric/factory/.drone.yml b/vendor/github.com/titpetric/factory/.drone.yml index 6e931f4b7..3e65f43a8 100644 --- a/vendor/github.com/titpetric/factory/.drone.yml +++ b/vendor/github.com/titpetric/factory/.drone.yml @@ -6,7 +6,7 @@ name: crust steps: - name: build - image: crusttech/crust-builder:latest + image: golang:1.12-alpine pull: always environment: CGO_ENABLED: 0 @@ -16,8 +16,7 @@ steps: CI: travis commands: - go fmt ./... - - wait-for-it.sh -t 60 --strict factory-db:3306 -- echo "database service is up" - - make test + - go test ./... -v -cover --tags="integration" services: - name: factory-db diff --git a/vendor/github.com/titpetric/factory/Makefile b/vendor/github.com/titpetric/factory/Makefile index ff9058100..064f4a147 100644 --- a/vendor/github.com/titpetric/factory/Makefile +++ b/vendor/github.com/titpetric/factory/Makefile @@ -1,7 +1,4 @@ -.PHONY: all test +.PHONY: all all: drone exec - -test: - gotest ./... -v -cover diff --git a/vendor/github.com/titpetric/factory/database.go b/vendor/github.com/titpetric/factory/database.go index 0b4ac058f..95f0d5de1 100644 --- a/vendor/github.com/titpetric/factory/database.go +++ b/vendor/github.com/titpetric/factory/database.go @@ -20,12 +20,14 @@ import ( // DatabaseCredential is a configuration struct for a database connection type DatabaseCredential struct { - DSN string + DSN string + DriverName string + Connector func() (*sql.DB, error) } // DatabaseFactory contains all database credentials and instances type DatabaseFactory struct { - credentials map[string]*DatabaseCredential + credentials map[string]DatabaseCredential instances map[string]*DB } @@ -34,7 +36,7 @@ var Database *DatabaseFactory func init() { Database = &DatabaseFactory{} - Database.credentials = make(map[string]*DatabaseCredential) + Database.credentials = make(map[string]DatabaseCredential) Database.instances = make(map[string]*DB) } @@ -48,10 +50,10 @@ func init() { // Example: // // ``` -// factory.Database.Add("default", "sqlapi:sqlapi@tcp(db1:3306)/sqlapi?collation=utf8mb4_general_ci") +// factory.Database.Add("default", factory.DatabaseCredential{"mysql", "sqlapi:sqlapi@tcp(db1:3306)/sqlapi?collation=utf8mb4_general_ci"}) // ``` // -// By default, additional options will be added to the credentials: +// By default, additional options will be added to the credentials DSN: // // - collation will be set to `utf8_general_ci` // - parseTime will be set to `true` @@ -59,21 +61,12 @@ func init() { // // If your passed DSN will include any of these options, the default values will not // be applied, and your custom settings will be honored. -func (r *DatabaseFactory) Add(name string, config interface{}) { - switch val := config.(type) { - case *string: - r.credentials[name] = &DatabaseCredential{DSN: *val} - case string: - r.credentials[name] = &DatabaseCredential{DSN: val} - case DatabaseCredential: - r.credentials[name] = &val - default: - panic("factory.Database.Add can take config as string|*string|factory.DatabaseCredential") - } +func (r *DatabaseFactory) Add(name string, credentials DatabaseCredential) { + credentials.DSN = r.CleanDSN(credentials.DriverName, credentials.DSN) + r.credentials[name] = credentials } -// GetDSN returns the augmented DSN from the named database -func (r *DatabaseFactory) GetDSN(name string) (string, error) { +func (r *DatabaseFactory) CleanDSN(driverName string, dsn string) string { addOption := func(s, match, option string) string { if !strings.Contains(s, match) { s += option @@ -81,15 +74,20 @@ func (r *DatabaseFactory) GetDSN(name string) (string, error) { return s } + dsn = addOption(dsn, "?", "?") + dsn = addOption(dsn, "collation=", "&collation=utf8_general_ci") + dsn = addOption(dsn, "parseTime=", "&parseTime=true") + dsn = addOption(dsn, "loc=", "&loc=Local") + dsn = strings.Replace(dsn, "?&", "?", 1) + return dsn +} + +// getCredentials returns the DatabaseCredential{} for a given db name +func (r *DatabaseFactory) getCredentials(name string) (*DatabaseCredential, error) { if value, ok := r.credentials[name]; ok { - value.DSN = addOption(value.DSN, "?", "?") - value.DSN = addOption(value.DSN, "collation=", "&collation=utf8_general_ci") - value.DSN = addOption(value.DSN, "parseTime=", "&parseTime=true") - value.DSN = addOption(value.DSN, "loc=", "&loc=Local") - value.DSN = strings.Replace(value.DSN, "?&", "?", 1) - return value.DSN, nil + return &value, nil } - return "", errors.New("No configuration found for database: " + name) + return nil, errors.New("No configuration found for database: " + name) } // Get returns a database connection @@ -119,24 +117,43 @@ func (r *DatabaseFactory) Get(dbName ...string) (*DB, error) { if value, ok := r.instances[name]; ok { return value, nil } - dsn, _ := r.GetDSN(name) - if dsn != "" { - handle, err := sqlx.Connect("mysql", dsn) - if err != nil { - return nil, err - } - r.instances[name] = &DB{ - handle, - context.Background(), - 0, - nil, - &sql.TxOptions{ - ReadOnly: false, - }, - logger.Silent{}, - } - return r.instances[name], nil + + credentials, err := r.getCredentials(name) + if err != nil { + return nil, err } + + if credentials.DriverName == "" { + return nil, errors.New("Credentials missing DriverName") + } + + var handle *sqlx.DB + + switch true { + case credentials.Connector != nil: + db, err := credentials.Connector() + if err != nil { + return nil, errors.WithStack(err) + } + handle = sqlx.NewDb(db, credentials.DriverName) + default: + handle, err = sqlx.Connect(credentials.DriverName, credentials.DSN) + if err != nil { + return nil, errors.WithStack(err) + } + } + + r.instances[name] = &DB{ + handle, + context.Background(), + 0, + nil, + &sql.TxOptions{ + ReadOnly: false, + }, + logger.Silent{}, + } + return r.instances[name], nil } return nil, fmt.Errorf("No configuration found for database: %v", names) } diff --git a/vendor/github.com/titpetric/factory/database_connector.go b/vendor/github.com/titpetric/factory/database_connector.go new file mode 100644 index 000000000..2f6c1dcf0 --- /dev/null +++ b/vendor/github.com/titpetric/factory/database_connector.go @@ -0,0 +1,106 @@ +package factory + +import ( + "context" + "fmt" + "log" + "regexp" + "time" + + "github.com/pkg/errors" + "github.com/titpetric/factory/logger" +) + +// DatabaseConnectionOptions is a configuration struct for connection retry +type DatabaseConnectionOptions struct { + DSN string + DriverName string + Logger string + + Retries int + RetryTimeout time.Duration + ConnectTimeout time.Duration +} + +var ( + dsnMasker = regexp.MustCompile("(.)(?:.*)(.):(.)(?:.*)(.)@") +) + +func (df *DatabaseFactory) TryToConnect(ctx context.Context, name string, options *DatabaseConnectionOptions) (db *DB, err error) { + df.Add(name, DatabaseCredential{ + DSN: options.DSN, + DriverName: options.DriverName, + }) + + var ( + connErrCh = make(chan error, 1) + ) + + // We'll not add this to the general log because we do not want to carry it with us for every query. + dsnField := fmt.Sprintf("dsn=%s", dsnMasker.ReplaceAllString(options.DSN, "$1****$2:$3****$4@")) + + defer close(connErrCh) + + log.Println("connecting to database", dsnField) + + go func() { + var ( + try = 0 + ) + + for { + try++ + + if options.Retries <= try { + err = errors.Errorf("could not connect to %q, in %d tries", name, try) + return + } + + db, err = df.Get(name) + if err != nil { + log.Println( + "could not connect to the database", + err, + dsnField, + fmt.Sprintf("try=%d", try), + ) + + select { + case <-ctx.Done(): + // Forced break + break + case <-time.After(options.RetryTimeout): + // Wait before next try + continue + } + } + + break + } + + connErrCh <- err + }() + + select { + case err = <-connErrCh: + break + case <-time.After(options.ConnectTimeout): + // Wait before next try + return nil, errors.Errorf("db init for %q timed out", name) + case <-ctx.Done(): + return nil, errors.Errorf("db connection for %q cancelled", name) + } + + switch options.Logger { + case "stdout": + db.SetLogger(logger.Default{}) + default: + db.SetLogger(logger.Silent{}) + } + + if err != nil { + return nil, err + } + + return db, nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 74853ee73..04a09755b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -155,7 +155,7 @@ github.com/steinfletcher/apitest-jsonpath # github.com/stretchr/testify v1.5.1 github.com/stretchr/testify/assert github.com/stretchr/testify/require -# github.com/titpetric/factory v0.0.0-20190806200833-ae4b02b9e034 +# github.com/titpetric/factory v0.0.0-20190828134837-8466c9bef13f github.com/titpetric/factory github.com/titpetric/factory/logger github.com/titpetric/factory/resputil