mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-17 17:47:03 +00:00
storage: Make type/params configurable for #88
This commit is contained in:
@@ -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.
|
||||||
|
|||||||
@@ -69,7 +69,8 @@ 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"`
|
||||||
|
Params map[string]string `desc:"Storage impl parameters, see docs."`
|
||||||
RetentionPeriod time.Duration `required:"true" default:"24h" desc:"Duration to retain messages"`
|
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"`
|
RetentionSleep time.Duration `required:"true" default:"50ms" desc:"Duration to sleep between mailboxes"`
|
||||||
MailboxMsgCap int `required:"true" default:"500" desc:"Maximum messages per mailbox"`
|
MailboxMsgCap int `required:"true" default:"500" desc:"Maximum messages per mailbox"`
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
Reference in New Issue
Block a user