Refactor how data dir is initialized

This commit is contained in:
Brian Picciano 2022-05-07 13:17:18 -06:00
parent c99b37c5b3
commit a10a604018
8 changed files with 130 additions and 61 deletions

74
srv/src/cfg/data_dir.go Normal file
View File

@ -0,0 +1,74 @@
package cfg
import (
"context"
"fmt"
"os"
"github.com/mediocregopher/mediocre-go-lib/v2/mctx"
)
// DataDir manages the blog's data directory.
type DataDir struct {
Path string
deleteOnClose bool
}
// Init initializes the data directory, creating the directory named at path if
// it doesn't exist.
//
// If Path is not set, then a temporary directory will be created and its path
// set to the Path field. This directory will be removed when Close is called.
func (d *DataDir) Init() error {
if d.Path == "" {
d.deleteOnClose = true
var err error
if d.Path, err = os.MkdirTemp("", "mediocre-blog-data-*"); err != nil {
return fmt.Errorf("creating temporary directory: %w", err)
}
return nil
}
if err := os.MkdirAll(d.Path, 0700); err != nil {
return fmt.Errorf(
"creating directory (and parents) of %q: %w",
d.Path,
err,
)
}
return nil
}
// SetupCfg implement the cfg.Cfger interface.
func (d *DataDir) SetupCfg(cfg *Cfg) {
cfg.StringVar(&d.Path, "data-dir", "", "Directory to use for persistent storage. If unset a temp directory will be created, and will be deleted when the process exits.")
cfg.OnInit(func(ctx context.Context) error {
return d.Init()
})
}
// Annotate implements mctx.Annotator interface.
func (d *DataDir) Annotate(a mctx.Annotations) {
a["dataDirPath"] = d.Path
}
// Close cleans up any temporary state created by DataDir.
func (d *DataDir) Close() error {
if !d.deleteOnClose {
return nil
}
if err := os.RemoveAll(d.Path); err != nil {
return fmt.Errorf("removing temp dir %q: %w", d.Path, err)
}
return nil
}

View File

@ -0,0 +1,9 @@
package cfg
// this file contains functionality specific to the mediocre blog.
// NewBlogCfg returns a Cfg specifically configured for mediocre blog processes.
func NewBlogCfg(params Params) *Cfg {
params.EnvPrefix = "MEDIOCRE_BLOG"
return New(params)
}

View File

@ -4,9 +4,9 @@ import (
"context"
"errors"
"io"
"path"
"github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
cfgpkg "github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
"github.com/mediocregopher/blog.mediocregopher.com/srv/mailinglist"
"github.com/mediocregopher/mediocre-go-lib/v2/mctx"
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
@ -17,11 +17,12 @@ func main() {
ctx := context.Background()
cfg := cfg.New(cfg.Params{
EnvPrefix: "MEDIOCRE_BLOG",
})
cfg := cfgpkg.NewBlogCfg(cfg.Params{})
dataDir := cfg.String("data-dir", ".", "Directory to use for long term storage")
var dataDir cfgpkg.DataDir
dataDir.SetupCfg(cfg)
defer dataDir.Close()
ctx = mctx.WithAnnotator(ctx, &dataDir)
var mailerParams mailinglist.MailerParams
mailerParams.SetupCfg(cfg)
@ -54,10 +55,7 @@ func main() {
mailer = mailinglist.NewMailer(mailerParams)
}
mailingListDBFile := path.Join(*dataDir, "mailinglist.sqlite3")
ctx = mctx.Annotate(ctx, "mailingListDBFile", mailingListDBFile)
mlStore, err := mailinglist.NewStore(mailingListDBFile)
mlStore, err := mailinglist.NewStore(dataDir)
if err != nil {
logger.Fatal(ctx, "initializing mailing list storage", err)
}

View File

@ -4,12 +4,12 @@ import (
"context"
"os"
"os/signal"
"path"
"syscall"
"time"
"github.com/mediocregopher/blog.mediocregopher.com/srv/api"
"github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
cfgpkg "github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
"github.com/mediocregopher/blog.mediocregopher.com/srv/chat"
"github.com/mediocregopher/blog.mediocregopher.com/srv/mailinglist"
"github.com/mediocregopher/blog.mediocregopher.com/srv/pow"
@ -23,11 +23,12 @@ func main() {
ctx := context.Background()
cfg := cfg.New(cfg.Params{
EnvPrefix: "MEDIOCRE_BLOG",
})
cfg := cfg.NewBlogCfg(cfg.Params{})
dataDir := cfg.String("data-dir", ".", "Directory to use for long term storage")
var dataDir cfgpkg.DataDir
dataDir.SetupCfg(cfg)
defer dataDir.Close()
ctx = mctx.WithAnnotator(ctx, &dataDir)
var powMgrParams pow.ManagerParams
powMgrParams.SetupCfg(cfg)
@ -91,10 +92,7 @@ func main() {
mailer = mailinglist.NewMailer(mailerParams)
}
mailingListDBFile := path.Join(*dataDir, "mailinglist.sqlite3")
ctx = mctx.Annotate(ctx, "mailingListDBFile", mailingListDBFile)
mlStore, err := mailinglist.NewStore(mailingListDBFile)
mlStore, err := mailinglist.NewStore(dataDir)
if err != nil {
logger.Fatal(ctx, "initializing mailing list storage", err)
}

View File

@ -7,10 +7,12 @@ import (
"errors"
"fmt"
"io"
"path"
"strings"
"time"
_ "github.com/mattn/go-sqlite3"
"github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
migrate "github.com/rubenv/sql-migrate"
)
@ -83,13 +85,15 @@ type store struct {
db *sql.DB
}
// NewStore initializes a new Store using a sqlite3 database at the given file
// path.
func NewStore(dbFile string) (Store, error) {
// NewStore initializes a new Store using a sqlite3 database in the given
// DataDir.
func NewStore(dataDir cfg.DataDir) (Store, error) {
db, err := sql.Open("sqlite3", dbFile)
path := path.Join(dataDir.Path, "mailinglist.sqlite3")
db, err := sql.Open("sqlite3", path)
if err != nil {
return nil, fmt.Errorf("opening sqlite file: %w", err)
return nil, fmt.Errorf("opening sqlite file at %q: %w", path, err)
}
migrations := &migrate.MemoryMigrationSource{Migrations: migrations}

View File

@ -2,31 +2,24 @@ package mailinglist
import (
"io"
"io/ioutil"
"os"
"testing"
"time"
"github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
"github.com/stretchr/testify/assert"
)
func TestStore(t *testing.T) {
tmpFile, err := ioutil.TempFile(os.TempDir(), "mediocre-blog-mailinglist-store-test-")
if err != nil {
t.Fatal("Cannot create temporary file", err)
var dataDir cfg.DataDir
if err := dataDir.Init(); err != nil {
t.Fatal(err)
}
tmpFilePath := tmpFile.Name()
tmpFile.Close()
t.Logf("using temporary sqlite file at %q", tmpFilePath)
t.Cleanup(func() { dataDir.Close() })
t.Cleanup(func() {
if err := os.Remove(tmpFilePath); err != nil {
panic(err)
}
})
store, err := NewStore(tmpFilePath)
store, err := NewStore(dataDir)
assert.NoError(t, err)
t.Cleanup(func() {

View File

@ -9,6 +9,7 @@ import (
"time"
_ "github.com/mattn/go-sqlite3" // we need dis
"github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
migrate "github.com/rubenv/sql-migrate"
)
@ -99,9 +100,7 @@ var migrations = []*migrate.Migration{
// Params are parameters used to initialize a new Store. All fields are required
// unless otherwise noted.
type StoreParams struct {
// Path to the file the database will be stored at.
DBFilePath string
DataDir cfg.DataDir
}
type store struct {
@ -113,9 +112,11 @@ type store struct {
// path.
func NewStore(params StoreParams) (Store, error) {
db, err := sql.Open("sqlite3", params.DBFilePath)
path := path.Join(params.DataDir.Path, "post.sqlite3")
db, err := sql.Open("sqlite3", path)
if err != nil {
return nil, fmt.Errorf("opening sqlite file: %w", err)
return nil, fmt.Errorf("opening sqlite file at %q: %w", path, err)
}
migrations := &migrate.MemoryMigrationSource{Migrations: migrations}

View File

@ -1,13 +1,12 @@
package post
import (
"io/ioutil"
"os"
"sort"
"strconv"
"testing"
"time"
"github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
"github.com/stretchr/testify/assert"
"github.com/tilinna/clock"
)
@ -29,25 +28,18 @@ type storeTestHarness struct {
func newStoreTestHarness(t *testing.T) storeTestHarness {
var dataDir cfg.DataDir
if err := dataDir.Init(); err != nil {
t.Fatal(err)
}
t.Cleanup(func() { dataDir.Close() })
clock := clock.NewMock(time.Now().UTC().Truncate(1 * time.Hour))
tmpFile, err := ioutil.TempFile(os.TempDir(), "mediocre-blog-post-store-test-")
if err != nil {
t.Fatal("Cannot create temporary file", err)
}
tmpFilePath := tmpFile.Name()
tmpFile.Close()
t.Logf("using temporary sqlite file at %q", tmpFilePath)
t.Cleanup(func() {
if err := os.Remove(tmpFilePath); err != nil {
panic(err)
}
})
store, err := NewStore(StoreParams{
DBFilePath: tmpFilePath,
DataDir: dataDir,
})
assert.NoError(t, err)