diff --git a/cmd/inbucket/main.go b/cmd/inbucket/main.go index a54a8d8..17ccb0e 100644 --- a/cmd/inbucket/main.go +++ b/cmd/inbucket/main.go @@ -23,6 +23,7 @@ import ( "github.com/jhillyerd/inbucket/pkg/server/web" "github.com/jhillyerd/inbucket/pkg/storage" "github.com/jhillyerd/inbucket/pkg/storage/file" + "github.com/jhillyerd/inbucket/pkg/storage/mem" "github.com/jhillyerd/inbucket/pkg/webui" ) @@ -45,6 +46,10 @@ func init() { expvar.Publish("goroutines", expvar.Func(func() interface{} { return runtime.NumGoroutine() })) + + // Register storage implementations. + storage.Constructors["file"] = file.New + storage.Constructors["memory"] = mem.New } func main() { @@ -97,8 +102,13 @@ func main() { // Configure internal services. rootCtx, rootCancel := context.WithCancel(context.Background()) 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) - store := file.New(conf.Storage) addrPolicy := &policy.Addressing{Config: conf.SMTP} mmanager := &message.StoreManager{Store: store, Hub: msgHub} // Start Retention scanner. diff --git a/pkg/config/config.go b/pkg/config/config.go index 25f805a..24017f4 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -69,10 +69,11 @@ type Web struct { // Storage contains the mail store configuration. type Storage struct { - Path string `required:"true" default:"/tmp/inbucket" desc:"Mail store path"` - RetentionPeriod time.Duration `required:"true" default:"24h" desc:"Duration to retain messages"` - RetentionSleep time.Duration `required:"true" default:"50ms" desc:"Duration to sleep between mailboxes"` - MailboxMsgCap int `required:"true" default:"500" desc:"Maximum messages per mailbox"` + Type string `required:"true" default:"memory" desc:"Storage impl: file or memory"` + Params map[string]string `desc:"Storage impl parameters, see docs."` + RetentionPeriod time.Duration `required:"true" default:"24h" desc:"Duration to retain messages"` + 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. diff --git a/pkg/storage/file/fstore.go b/pkg/storage/file/fstore.go index 069a3fe..fa00f3c 100644 --- a/pkg/storage/file/fstore.go +++ b/pkg/storage/file/fstore.go @@ -48,11 +48,10 @@ type Store struct { } // New creates a new DataStore object using the specified path -func New(cfg config.Storage) storage.Store { - path := cfg.Path +func New(cfg config.Storage) (storage.Store, error) { + path := cfg.Params["path"] if path == "" { - log.Errorf("No value configured for datastore path") - return nil + return nil, fmt.Errorf("'path' parameter not specified") } mailPath := filepath.Join(path, "mail") 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) } } - 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. diff --git a/pkg/storage/file/fstore_test.go b/pkg/storage/file/fstore_test.go index 0b42e86..10326b0 100644 --- a/pkg/storage/file/fstore_test.go +++ b/pkg/storage/file/fstore_test.go @@ -265,13 +265,18 @@ func setupDataStore(cfg config.Storage) (*Store, *bytes.Buffer) { if err != nil { panic(err) } - - // Capture log output + // Capture log output. buf := new(bytes.Buffer) log.SetOutput(buf) - - cfg.Path = path - return New(cfg).(*Store), buf + if cfg.Params == nil { + cfg.Params = make(map[string]string) + } + 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 diff --git a/pkg/storage/mem/store.go b/pkg/storage/mem/store.go index f70d5be..cbf78b5 100644 --- a/pkg/storage/mem/store.go +++ b/pkg/storage/mem/store.go @@ -6,6 +6,7 @@ import ( "strconv" "sync" + "github.com/jhillyerd/inbucket/pkg/config" "github.com/jhillyerd/inbucket/pkg/storage" ) @@ -25,10 +26,10 @@ type mbox struct { var _ storage.Store = &Store{} // New returns an emtpy memory store. -func New() *Store { +func New(cfg config.Storage) (storage.Store, error) { return &Store{ boxes: make(map[string]*mbox), - } + }, nil } // AddMessage stores the message, message ID and Size will be ignored. diff --git a/pkg/storage/mem/store_test.go b/pkg/storage/mem/store_test.go index cbfdb05..53f986d 100644 --- a/pkg/storage/mem/store_test.go +++ b/pkg/storage/mem/store_test.go @@ -3,6 +3,7 @@ package mem import ( "testing" + "github.com/jhillyerd/inbucket/pkg/config" "github.com/jhillyerd/inbucket/pkg/storage" "github.com/jhillyerd/inbucket/pkg/test" ) @@ -10,7 +11,7 @@ import ( // TestSuite runs storage package test suite on file store. func TestSuite(t *testing.T) { test.StoreSuite(t, func() (storage.Store, func(), error) { - s := New() + s, _ := New(config.Storage{}) destroy := func() {} return s, destroy, nil }) diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index edc4afd..67ef9fc 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -3,9 +3,12 @@ package storage import ( "errors" + "fmt" "io" "net/mail" "time" + + "github.com/jhillyerd/inbucket/pkg/config" ) var ( @@ -14,6 +17,9 @@ var ( // ErrNotWritable indicates the message is closed; no longer 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. @@ -38,3 +44,11 @@ type Message interface { Source() (io.ReadCloser, error) 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) +} diff --git a/themes/bootstrap/templates/root/status.html b/themes/bootstrap/templates/root/status.html index 3c708b1..f666ee7 100644 --- a/themes/bootstrap/templates/root/status.html +++ b/themes/bootstrap/templates/root/status.html @@ -171,6 +171,10 @@ $(document).ready(
+
+
Store Type:
+
{{ .storageConfig.Type }}
+
Retention Period: