Skip to content

Commit

Permalink
refactor: Improve data ownership interface
Browse files Browse the repository at this point in the history
  • Loading branch information
jameshoulahan committed Aug 15, 2022
1 parent a2b75a1 commit 5d639ab
Show file tree
Hide file tree
Showing 12 changed files with 189 additions and 199 deletions.
8 changes: 4 additions & 4 deletions benchmarks/gluon_bench/gluon_benchmarks/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ func (s *Sync) Setup(ctx context.Context, benchmarkDir string) error {
loggerIn := logrus.StandardLogger().WriterLevel(logrus.TraceLevel)
loggerOut := logrus.StandardLogger().WriterLevel(logrus.TraceLevel)

opts := []gluon.Option{gluon.WithLogger(loggerIn, loggerOut),
gluon.WithDataPath(benchmarkDir),
opts := []gluon.Option{
gluon.WithDataDir(benchmarkDir),
gluon.WithLogger(loggerIn, loggerOut),
}

server, err := gluon.New(benchmarkDir, opts...)

server, err := gluon.New(opts...)
if err != nil {
return err
}
Expand Down
5 changes: 2 additions & 3 deletions benchmarks/gluon_bench/imap_benchmarks/server/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,9 @@ func (*LocalServerBuilder) New(ctx context.Context, serverPath string, profiler

opts = append(opts, gluon.WithLogger(loggerIn, loggerOut))
opts = append(opts, gluon.WithCmdProfiler(profiler))
opts = append(opts, gluon.WithDataPath(serverPath))

server, err := gluon.New(serverPath, opts...)
opts = append(opts, gluon.WithDataDir(serverPath))

server, err := gluon.New(opts...)
if err != nil {
return nil, err
}
Expand Down
69 changes: 69 additions & 0 deletions builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package gluon

import (
"crypto/tls"
"io"
"net"
"os"
"path/filepath"

"github.com/ProtonMail/gluon/events"
"github.com/ProtonMail/gluon/internal"
"github.com/ProtonMail/gluon/internal/backend"
"github.com/ProtonMail/gluon/internal/session"
"github.com/ProtonMail/gluon/profiling"
"github.com/ProtonMail/gluon/store"
)

type serverBuilder struct {
dir string
delim string
tlsConfig *tls.Config
inLogger io.Writer
outLogger io.Writer
versionInfo internal.VersionInfo
cmdExecProfBuilder profiling.CmdProfilerBuilder
storeBuilder store.Builder
}

func newBuilder() (*serverBuilder, error) {
return &serverBuilder{
delim: "/",
cmdExecProfBuilder: &profiling.NullCmdExecProfilerBuilder{},
storeBuilder: &store.OnDiskStoreBuilder{},
}, nil
}

func (builder *serverBuilder) build() (*Server, error) {
if builder.dir == "" {
dir, err := os.MkdirTemp("", "gluon-*")
if err != nil {
return nil, err
}

builder.dir = dir
}

if err := os.MkdirAll(builder.dir, 0o700); err != nil {
return nil, err
}

backend, err := backend.New(filepath.Join(builder.dir, "backend"), builder.storeBuilder, builder.delim)
if err != nil {
return nil, err
}

return &Server{
dir: builder.dir,
backend: backend,
listeners: make(map[net.Listener]struct{}),
sessions: make(map[int]*session.Session),
inLogger: builder.inLogger,
outLogger: builder.outLogger,
tlsConfig: builder.tlsConfig,
watchers: make(map[chan events.Event]struct{}),
storeBuilder: builder.storeBuilder,
cmdExecProfBuilder: builder.cmdExecProfBuilder,
versionInfo: builder.versionInfo,
}, nil
}
21 changes: 8 additions & 13 deletions demo/demo.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ var blockProfileFlag = flag.Bool("profile-lock", false, "Enable lock profiling."
var profilePathFlag = flag.String("profile-path", "", "Path where to write profile data.")

func main() {
ctx := context.Background()

flag.Parse()

if *cpuProfileFlag {
Expand All @@ -41,23 +43,16 @@ func main() {
logrus.SetLevel(level)
}

loggerIn := logrus.StandardLogger().WriterLevel(logrus.TraceLevel)
loggerOut := logrus.StandardLogger().WriterLevel(logrus.TraceLevel)

ctx := context.Background()
server, err := gluon.New(temp(), gluon.WithLogger(
loggerIn,
loggerOut,
),
gluon.WithDataPath(temp()),
)

defer server.Close(ctx)

server, err := gluon.New(gluon.WithLogger(
logrus.StandardLogger().WriterLevel(logrus.TraceLevel),
logrus.StandardLogger().WriterLevel(logrus.TraceLevel),
))
if err != nil {
logrus.WithError(err).Fatal("Failed to create server")
}

defer server.Close(ctx)

if err := addUser(ctx, server, []string{"user1@example.com", "alias1@example.com"}, "password1"); err != nil {
logrus.WithError(err).Fatal("Failed to add user")
}
Expand Down
39 changes: 29 additions & 10 deletions internal/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package backend
import (
"context"
"fmt"
"path/filepath"
"sync"

"github.com/ProtonMail/gluon/connector"
Expand All @@ -13,6 +14,9 @@ import (
)

type Backend struct {
// dir is the directory in which backend files should be stored.
dir string

// remote manages operations to be performed on the API.
remote *remote.Manager

Expand All @@ -22,37 +26,52 @@ type Backend struct {
// users holds all registered backend users.
users map[string]*user
usersLock sync.Mutex

// storeBuilder builds stores for the backend users.
storeBuilder store.Builder
}

func New(dir string) (*Backend, error) {
manager, err := remote.New(dir)
func New(dir string, storeBuilder store.Builder, delim string) (*Backend, error) {
manager, err := remote.New(filepath.Join(dir, "remote"))
if err != nil {
return nil, err
}

return &Backend{
remote: manager,
delim: "/",
users: make(map[string]*user),
dir: dir,
remote: manager,
storeBuilder: storeBuilder,
delim: delim,
users: make(map[string]*user),
}, nil
}

func (b *Backend) NewUserID() string {
return uuid.NewString()
}

func (b *Backend) SetDelimiter(delim string) {
b.delim = delim
}

func (b *Backend) GetDelimiter() string {
return b.delim
}

func (b *Backend) AddUser(ctx context.Context, userID string, conn connector.Connector, store store.Store, db *DB) error {
func (b *Backend) AddUser(ctx context.Context, userID string, conn connector.Connector, passphrase []byte) error {
b.usersLock.Lock()
defer b.usersLock.Unlock()

store, err := b.storeBuilder.New(filepath.Join(b.dir, "store"), userID, passphrase)
if err != nil {
return err
}

db, err := b.newDB(userID)
if err != nil {
if err := store.Close(); err != nil {
logrus.WithError(err).Error("Failed to close storage")
}

return err
}

remote, err := b.remote.AddUser(ctx, userID, conn)
if err != nil {
return err
Expand Down
17 changes: 11 additions & 6 deletions internal/backend/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package backend
import (
"context"
"fmt"
"os"
"path/filepath"
"sync"

Expand Down Expand Up @@ -67,17 +68,21 @@ func (d *DB) Close() error {
return d.db.Close()
}

func getDatabasePath(dataPath, userID string) string {
return fmt.Sprintf("file:%v?cache=shared&_fk=1", filepath.Join(dataPath, fmt.Sprintf("%v.db", userID)))
}
func (b *Backend) newDB(userID string) (*DB, error) {
dir := filepath.Join(b.dir, "db")

func NewDB(dataPath, userID string) (*DB, error) {
dbPath := getDatabasePath(dataPath, userID)
client, err := ent.Open(dialect.SQLite, dbPath)
if err := os.MkdirAll(dir, 0o700); err != nil {
return nil, err
}

client, err := ent.Open(dialect.SQLite, getDatabasePath(dir, userID))
if err != nil {
return nil, err
}

return &DB{db: client}, nil
}

func getDatabasePath(dir, userID string) string {
return fmt.Sprintf("file:%v?cache=shared&_fk=1", filepath.Join(dir, fmt.Sprintf("%v.db", userID)))
}
42 changes: 21 additions & 21 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

// Option represents a type that can be used to configure the server.
type Option interface {
config(server *Server)
config(*serverBuilder)
}

// WithDelimiter instructs the server to use the given path delimiter instead of the default ('/').
Expand All @@ -25,8 +25,8 @@ type withDelimiter struct {
delimiter string
}

func (opt withDelimiter) config(server *Server) {
server.backend.SetDelimiter(opt.delimiter)
func (opt withDelimiter) config(builder *serverBuilder) {
builder.delim = opt.delimiter
}

// WithTLS instructs the server to use the given TLS config.
Expand All @@ -40,8 +40,8 @@ type withTLS struct {
cfg *tls.Config
}

func (opt withTLS) config(server *Server) {
server.tlsConfig = opt.cfg
func (opt withTLS) config(builder *serverBuilder) {
builder.tlsConfig = opt.cfg
}

// WithLogger instructs the server to write incoming and outgoing IMAP communication to the given io.Writers.
Expand All @@ -56,17 +56,17 @@ type withLogger struct {
in, out io.Writer
}

func (opt withLogger) config(server *Server) {
server.inLogger = opt.in
server.outLogger = opt.out
func (opt withLogger) config(builder *serverBuilder) {
builder.inLogger = opt.in
builder.outLogger = opt.out
}

type withVersionInfo struct {
versionInfo internal.VersionInfo
}

func (vi *withVersionInfo) config(server *Server) {
server.versionInfo = vi.versionInfo
func (vi *withVersionInfo) config(builder *serverBuilder) {
builder.versionInfo = vi.versionInfo
}

func WithVersionInfo(vmajor, vminor, vpatch int, name, vendor, supportURL string) Option {
Expand All @@ -88,8 +88,8 @@ type withCmdExecProfiler struct {
builder profiling.CmdProfilerBuilder
}

func (c *withCmdExecProfiler) config(server *Server) {
server.cmdExecProfBuilder = c.builder
func (c *withCmdExecProfiler) config(builder *serverBuilder) {
builder.cmdExecProfBuilder = c.builder
}

// WithCmdProfiler allows a specific CmdProfilerBuilder to be set for the server's execution.
Expand All @@ -98,25 +98,25 @@ func WithCmdProfiler(builder profiling.CmdProfilerBuilder) Option {
}

type withStoreBuilder struct {
builder store.StoreBuilder
builder store.Builder
}

func (w *withStoreBuilder) config(server *Server) {
server.storeBuilder = w.builder
func (w *withStoreBuilder) config(builder *serverBuilder) {
builder.storeBuilder = w.builder
}

func WithStoreBuilder(builder store.StoreBuilder) Option {
func WithStoreBuilder(builder store.Builder) Option {
return &withStoreBuilder{builder: builder}
}

type withDataPath struct {
type withDataDir struct {
path string
}

func (w *withDataPath) config(server *Server) {
server.dataPath = w.path
func (w *withDataDir) config(builder *serverBuilder) {
builder.dir = w.path
}

func WithDataPath(path string) Option {
return &withDataPath{path: path}
func WithDataDir(path string) Option {
return &withDataDir{path: path}
}
Loading

0 comments on commit 5d639ab

Please sign in to comment.