First split into crockery init
and crockery run
The init command creates a crockery.db file containing the domain name and TLS keypair. The run command starts IMAP and SMTP services based on that file. Supporting only a single domain is starting to look a bit unnecessary. We'll see how that goes.
This commit is contained in:
@@ -3,6 +3,11 @@ package store
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/asdine/storm"
|
||||
"github.com/coreos/bbolt"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
@@ -10,20 +15,61 @@ type Interface interface {
|
||||
TLS() tls.Certificate
|
||||
TLSConfig() *tls.Config
|
||||
|
||||
SetDomain(string)
|
||||
SetTLS(tls.Certificate)
|
||||
SetDomain(string) error
|
||||
SetTLS([]byte, []byte) error
|
||||
|
||||
io.Closer
|
||||
}
|
||||
|
||||
func New(ctx context.Context, filename string) (Interface, error) {
|
||||
return &concrete{
|
||||
db, err := storm.Open(filename, storm.BoltOptions(0600, &bolt.Options{}))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := &concrete{
|
||||
filename: filename,
|
||||
}, nil
|
||||
storm: db,
|
||||
}
|
||||
|
||||
if err := out.setup(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func Init(filename, domain string, certPEM, keyPEM []byte) error {
|
||||
db, err := storm.Open(filename, storm.BoltOptions(0600, &bolt.Options{}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
builder := &concrete{
|
||||
filename: filename,
|
||||
storm: db,
|
||||
}
|
||||
|
||||
defer builder.Close()
|
||||
|
||||
if err := builder.SetDomain(domain); err != nil {
|
||||
return fmt.Errorf("Couldn't set domain: %v", err)
|
||||
}
|
||||
|
||||
if err := builder.SetTLS(certPEM, keyPEM); err != nil {
|
||||
return fmt.Errorf("Couldn't set TLS: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type concrete struct {
|
||||
filename string
|
||||
filename string
|
||||
storm *storm.DB
|
||||
domainBucket storm.Node
|
||||
|
||||
// TODO: these will eventually be persisted to the file in `filename`
|
||||
// These are persisted in BoltDB, but we
|
||||
// Might as well keep them in memory for the duration, though.
|
||||
domain string
|
||||
cert tls.Certificate
|
||||
}
|
||||
@@ -48,14 +94,82 @@ func (c *concrete) TLSConfig() *tls.Config {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *concrete) SetDomain(domain string) {
|
||||
func (c *concrete) SetDomain(domain string) error {
|
||||
if err := c.storm.Set("config", "domain", domain); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.domain = domain
|
||||
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *concrete) SetTLS(cert tls.Certificate) {
|
||||
func (c *concrete) SetTLS(certPEM, keyPEM []byte) error {
|
||||
cert, err := tls.X509KeyPair(certPEM, keyPEM)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Couldn't parse data as TLS keypair: %v", err)
|
||||
}
|
||||
|
||||
if cert.PrivateKey == nil {
|
||||
return fmt.Errorf("No private key for TLS certificate")
|
||||
}
|
||||
|
||||
domainBucket := c.storm.From("domains").From(c.Domain())
|
||||
|
||||
tx, err := domainBucket.Begin(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Set("config", "cert", certPEM); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Set("config", "key", keyPEM); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.cert = cert
|
||||
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *concrete) Close() error {
|
||||
return c.storm.Close()
|
||||
}
|
||||
|
||||
// Bootstraps all cached values from storm
|
||||
func (c *concrete) setup() error {
|
||||
var domain string
|
||||
var keyPEM []byte
|
||||
var certPEM []byte
|
||||
|
||||
if err := c.storm.Get("config", "domain", &domain); err != nil {
|
||||
return fmt.Errorf("Couldn't read config/domain: %v", err)
|
||||
}
|
||||
|
||||
domainBucket := c.storm.From("domains").From(domain)
|
||||
|
||||
if err := domainBucket.Get("config", "cert", &certPEM); err != nil {
|
||||
return fmt.Errorf("Couldn't read domains/%s/config/cert: %v", domain, err)
|
||||
}
|
||||
|
||||
if err := domainBucket.Get("config", "key", &keyPEM); err != nil {
|
||||
return fmt.Errorf("Couldn't read domains/%s/config/key: %v", domain, err)
|
||||
}
|
||||
|
||||
cert, err := tls.X509KeyPair(certPEM, keyPEM)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Couldn't parse key and certificate: %v", err)
|
||||
}
|
||||
|
||||
c.domainBucket = domainBucket
|
||||
c.domain = domain
|
||||
c.cert = cert
|
||||
|
||||
return nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user