More messing around

This commit is contained in:
tsmethurst
2021-03-02 22:52:31 +01:00
parent d8e8d44d47
commit be1b631681
7 changed files with 136 additions and 50 deletions

View File

@ -21,15 +21,23 @@ package db
import "regexp"
const (
// general db defaults
/*
general db defaults
*/
// default database to use in whatever db implementation we have
defaultDatabase string = "gotosocial"
// default address should in most cases be overwritten
defaultAddress string = "localhost"
// implementation-specific defaults
/*
implementation-specific defaults
*/
// widely-recognised default postgres port
postgresDefaultPort int = 5432
// default user should in most cases be overwritten
postgresDefaultUser string = "postgres"
)
var ipv4Regex = regexp.MustCompile(`^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$`)

View File

@ -23,31 +23,69 @@ import (
"errors"
"fmt"
"net/url"
"time"
"github.com/go-fed/activity/streams/vocab"
"github.com/go-pg/pg"
"github.com/sirupsen/logrus"
)
type postgresService struct {
config *Config
conn *pg.DB
ready bool
log *logrus.Entry
cancel context.CancelFunc
}
// newPostgresService returns a postgresService derived from the provided config, which implements the go-fed DB interface.
// Under the hood, it uses https://github.com/go-pg/pg to create and maintain a database connection.
func newPostgresService(config *Config) (*postgresService, error) {
func newPostgresService(ctx context.Context, config *Config, log *logrus.Entry) (*postgresService, error) {
opts, err := derivePGOptions(config)
if err != nil {
return nil, fmt.Errorf("could not create postgres service: %s", err)
}
conn := pg.Connect(opts)
return &postgresService{
config,
conn,
false,
}, nil
readyChan := make(chan interface{})
opts.OnConnect = func(c *pg.Conn) error {
close(readyChan)
return nil
}
// create a connection
pgCtx, cancel := context.WithCancel(ctx)
conn := pg.Connect(opts).WithContext(pgCtx)
// actually *begin* the connection so that we can tell if the db is there
// and listening, and also trigger the opts.OnConnect function passed in above
tx, err := conn.Begin()
if err != nil {
cancel()
return nil, fmt.Errorf("db connection error: %s", err)
}
// close the transaction we just started so it doesn't hang around
if err := tx.Rollback(); err != nil {
cancel()
return nil, fmt.Errorf("db connection error: %s", err)
}
// make sure the opts.OnConnect function has been triggered
// and closed the ready channel
select {
case <-readyChan:
log.Infof("postgres connection ready")
case <-time.After(5 * time.Second):
cancel()
return nil, errors.New("db connection timeout")
}
// we can confidently return this useable postgres service now
return &postgresService{
config: config,
conn: conn,
log: log,
cancel: cancel,
}, nil
}
/*
@ -68,22 +106,35 @@ func derivePGOptions(config *Config) (*pg.Options, error) {
}
// validate address
address := config.Address
if address == "" {
return nil, errors.New("address not provided")
if config.Address == "" {
config.Address = defaultAddress
}
if !hostnameRegex.MatchString(address) && !ipv4Regex.MatchString(address) {
return nil, fmt.Errorf("address %s was neither an ipv4 address nor a valid hostname", address)
if !hostnameRegex.MatchString(config.Address) && !ipv4Regex.MatchString(config.Address) && config.Address != "localhost" {
return nil, fmt.Errorf("address %s was neither an ipv4 address nor a valid hostname", config.Address)
}
// validate username
if config.User == "" {
config.User = postgresDefaultUser
}
// validate that there's a password
if config.Password == "" {
return nil, errors.New("no password set")
}
// validate database
if config.Database == "" {
config.Database = defaultDatabase
}
// We can rely on the pg library we're using to set
// sensible defaults for everything we don't set here.
options := &pg.Options{
Addr: fmt.Sprintf("%s:%d", config.Address, config.Port),
User: config.User,
Password: config.Password,
Database: config.Database,
OnConnect: func(c *pg.Conn) error {
return nil
},
}
return options, nil
@ -176,6 +227,12 @@ func (ps *postgresService) Liked(c context.Context, actorIRI *url.URL) (follower
EXTRA FUNCTIONS
*/
func (ps *postgresService) Ready() bool {
return false
func (ps *postgresService) Stop(ctx context.Context) error {
ps.log.Info("closing db connection")
if err := ps.conn.Close(); err != nil {
// only cancel if there's a problem closing the db
ps.cancel()
return err
}
return nil
}

View File

@ -19,10 +19,12 @@
package db
import (
"context"
"fmt"
"strings"
"github.com/go-fed/activity/pub"
"github.com/sirupsen/logrus"
)
const dbTypePostgres string = "POSTGRES"
@ -39,9 +41,7 @@ type Service interface {
/*
ANY ADDITIONAL DESIRED FUNCTIONS
*/
// Ready indicates whether the database is ready to handle queries and whatnot.
Ready() bool
Stop(context.Context) error
}
// Config provides configuration options for the database connection
@ -57,10 +57,10 @@ type Config struct {
// NewService returns a new database service that satisfies the Service interface and, by extension,
// the go-fed database interface described here: https://github.com/go-fed/activity/blob/master/pub/database.go
func NewService(config *Config) (Service, error) {
func NewService(context context.Context, config *Config, log *logrus.Logger) (Service, error) {
switch strings.ToUpper(config.Type) {
case dbTypePostgres:
return newPostgresService(config)
return newPostgresService(context, config, log.WithField("service", "db"))
default:
return nil, fmt.Errorf("database type %s not supported", config.Type)
}