1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2025-12-17 09:37:02 +00:00

storage: Make type/params configurable for #88

This commit is contained in:
James Hillyerd
2018-03-24 13:18:51 -07:00
parent bb0fb410c1
commit 281cc21412
8 changed files with 53 additions and 18 deletions

View File

@@ -23,6 +23,7 @@ import (
"github.com/jhillyerd/inbucket/pkg/server/web" "github.com/jhillyerd/inbucket/pkg/server/web"
"github.com/jhillyerd/inbucket/pkg/storage" "github.com/jhillyerd/inbucket/pkg/storage"
"github.com/jhillyerd/inbucket/pkg/storage/file" "github.com/jhillyerd/inbucket/pkg/storage/file"
"github.com/jhillyerd/inbucket/pkg/storage/mem"
"github.com/jhillyerd/inbucket/pkg/webui" "github.com/jhillyerd/inbucket/pkg/webui"
) )
@@ -45,6 +46,10 @@ func init() {
expvar.Publish("goroutines", expvar.Func(func() interface{} { expvar.Publish("goroutines", expvar.Func(func() interface{} {
return runtime.NumGoroutine() return runtime.NumGoroutine()
})) }))
// Register storage implementations.
storage.Constructors["file"] = file.New
storage.Constructors["memory"] = mem.New
} }
func main() { func main() {
@@ -97,8 +102,13 @@ func main() {
// Configure internal services. // Configure internal services.
rootCtx, rootCancel := context.WithCancel(context.Background()) rootCtx, rootCancel := context.WithCancel(context.Background())
shutdownChan := make(chan bool) shutdownChan := make(chan bool)
store, err := storage.FromConfig(conf.Storage)
if err != nil {
log.Errorf("Fatal storage error: %v", err)
removePIDFile(*pidfile)
os.Exit(1)
}
msgHub := msghub.New(rootCtx, conf.Web.MonitorHistory) msgHub := msghub.New(rootCtx, conf.Web.MonitorHistory)
store := file.New(conf.Storage)
addrPolicy := &policy.Addressing{Config: conf.SMTP} addrPolicy := &policy.Addressing{Config: conf.SMTP}
mmanager := &message.StoreManager{Store: store, Hub: msgHub} mmanager := &message.StoreManager{Store: store, Hub: msgHub}
// Start Retention scanner. // Start Retention scanner.

View File

@@ -69,10 +69,11 @@ type Web struct {
// Storage contains the mail store configuration. // Storage contains the mail store configuration.
type Storage struct { type Storage struct {
Path string `required:"true" default:"/tmp/inbucket" desc:"Mail store path"` Type string `required:"true" default:"memory" desc:"Storage impl: file or memory"`
RetentionPeriod time.Duration `required:"true" default:"24h" desc:"Duration to retain messages"` Params map[string]string `desc:"Storage impl parameters, see docs."`
RetentionSleep time.Duration `required:"true" default:"50ms" desc:"Duration to sleep between mailboxes"` RetentionPeriod time.Duration `required:"true" default:"24h" desc:"Duration to retain messages"`
MailboxMsgCap int `required:"true" default:"500" desc:"Maximum messages per mailbox"` RetentionSleep time.Duration `required:"true" default:"50ms" desc:"Duration to sleep between mailboxes"`
MailboxMsgCap int `required:"true" default:"500" desc:"Maximum messages per mailbox"`
} }
// Process loads and parses configuration from the environment. // Process loads and parses configuration from the environment.

View File

@@ -48,11 +48,10 @@ type Store struct {
} }
// New creates a new DataStore object using the specified path // New creates a new DataStore object using the specified path
func New(cfg config.Storage) storage.Store { func New(cfg config.Storage) (storage.Store, error) {
path := cfg.Path path := cfg.Params["path"]
if path == "" { if path == "" {
log.Errorf("No value configured for datastore path") return nil, fmt.Errorf("'path' parameter not specified")
return nil
} }
mailPath := filepath.Join(path, "mail") mailPath := filepath.Join(path, "mail")
if _, err := os.Stat(mailPath); err != nil { if _, err := os.Stat(mailPath); err != nil {
@@ -61,7 +60,7 @@ func New(cfg config.Storage) storage.Store {
log.Errorf("Error creating dir %q: %v", mailPath, err) log.Errorf("Error creating dir %q: %v", mailPath, err)
} }
} }
return &Store{path: path, mailPath: mailPath, messageCap: cfg.MailboxMsgCap} return &Store{path: path, mailPath: mailPath, messageCap: cfg.MailboxMsgCap}, nil
} }
// AddMessage adds a message to the specified mailbox. // AddMessage adds a message to the specified mailbox.

View File

@@ -265,13 +265,18 @@ func setupDataStore(cfg config.Storage) (*Store, *bytes.Buffer) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
// Capture log output.
// Capture log output
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
log.SetOutput(buf) log.SetOutput(buf)
if cfg.Params == nil {
cfg.Path = path cfg.Params = make(map[string]string)
return New(cfg).(*Store), buf }
cfg.Params["path"] = path
s, err := New(cfg)
if err != nil {
panic(err)
}
return s.(*Store), buf
} }
// deliverMessage creates and delivers a message to the specific mailbox, returning // deliverMessage creates and delivers a message to the specific mailbox, returning

View File

@@ -6,6 +6,7 @@ import (
"strconv" "strconv"
"sync" "sync"
"github.com/jhillyerd/inbucket/pkg/config"
"github.com/jhillyerd/inbucket/pkg/storage" "github.com/jhillyerd/inbucket/pkg/storage"
) )
@@ -25,10 +26,10 @@ type mbox struct {
var _ storage.Store = &Store{} var _ storage.Store = &Store{}
// New returns an emtpy memory store. // New returns an emtpy memory store.
func New() *Store { func New(cfg config.Storage) (storage.Store, error) {
return &Store{ return &Store{
boxes: make(map[string]*mbox), boxes: make(map[string]*mbox),
} }, nil
} }
// AddMessage stores the message, message ID and Size will be ignored. // AddMessage stores the message, message ID and Size will be ignored.

View File

@@ -3,6 +3,7 @@ package mem
import ( import (
"testing" "testing"
"github.com/jhillyerd/inbucket/pkg/config"
"github.com/jhillyerd/inbucket/pkg/storage" "github.com/jhillyerd/inbucket/pkg/storage"
"github.com/jhillyerd/inbucket/pkg/test" "github.com/jhillyerd/inbucket/pkg/test"
) )
@@ -10,7 +11,7 @@ import (
// TestSuite runs storage package test suite on file store. // TestSuite runs storage package test suite on file store.
func TestSuite(t *testing.T) { func TestSuite(t *testing.T) {
test.StoreSuite(t, func() (storage.Store, func(), error) { test.StoreSuite(t, func() (storage.Store, func(), error) {
s := New() s, _ := New(config.Storage{})
destroy := func() {} destroy := func() {}
return s, destroy, nil return s, destroy, nil
}) })

View File

@@ -3,9 +3,12 @@ package storage
import ( import (
"errors" "errors"
"fmt"
"io" "io"
"net/mail" "net/mail"
"time" "time"
"github.com/jhillyerd/inbucket/pkg/config"
) )
var ( var (
@@ -14,6 +17,9 @@ var (
// ErrNotWritable indicates the message is closed; no longer writable // ErrNotWritable indicates the message is closed; no longer writable
ErrNotWritable = errors.New("Message not writable") ErrNotWritable = errors.New("Message not writable")
// Constructors tracks registered storage constructors
Constructors = make(map[string]func(config.Storage) (Store, error))
) )
// Store is the interface Inbucket uses to interact with storage implementations. // Store is the interface Inbucket uses to interact with storage implementations.
@@ -38,3 +44,11 @@ type Message interface {
Source() (io.ReadCloser, error) Source() (io.ReadCloser, error)
Size() int64 Size() int64
} }
// FromConfig creates an instance of the Store based on the provided configuration.
func FromConfig(c config.Storage) (store Store, err error) {
if cf := Constructors[c.Type]; cf != nil {
return cf(c)
}
return nil, fmt.Errorf("unknown storage type configured: %q", c.Type)
}

View File

@@ -171,6 +171,10 @@ $(document).ready(
</div> </div>
<div class="panel-body"> <div class="panel-body">
<table class="metrics"> <table class="metrics">
<div class="row">
<div class="col-sm-3 col-xs-7"><b>Store Type:</b></div>
<div class="col-sm-8 col-xs-5">{{ .storageConfig.Type }}</div>
</div>
<div class="row"> <div class="row">
<div class="col-sm-3 col-xs-7"><b>Retention Period:</b></div> <div class="col-sm-3 col-xs-7"><b>Retention Period:</b></div>
<div class="col-sm-8 col-xs-5"> <div class="col-sm-8 col-xs-5">