Refactor how data dir is initialized
This commit is contained in:
parent
c99b37c5b3
commit
a10a604018
74
srv/src/cfg/data_dir.go
Normal file
74
srv/src/cfg/data_dir.go
Normal 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
|
||||||
|
}
|
9
srv/src/cfg/mediocre_blog.go
Normal file
9
srv/src/cfg/mediocre_blog.go
Normal 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)
|
||||||
|
}
|
@ -4,9 +4,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"path"
|
|
||||||
|
|
||||||
"github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
|
"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/blog.mediocregopher.com/srv/mailinglist"
|
||||||
"github.com/mediocregopher/mediocre-go-lib/v2/mctx"
|
"github.com/mediocregopher/mediocre-go-lib/v2/mctx"
|
||||||
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
|
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
|
||||||
@ -17,11 +17,12 @@ func main() {
|
|||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
cfg := cfg.New(cfg.Params{
|
cfg := cfgpkg.NewBlogCfg(cfg.Params{})
|
||||||
EnvPrefix: "MEDIOCRE_BLOG",
|
|
||||||
})
|
|
||||||
|
|
||||||
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
|
var mailerParams mailinglist.MailerParams
|
||||||
mailerParams.SetupCfg(cfg)
|
mailerParams.SetupCfg(cfg)
|
||||||
@ -54,10 +55,7 @@ func main() {
|
|||||||
mailer = mailinglist.NewMailer(mailerParams)
|
mailer = mailinglist.NewMailer(mailerParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
mailingListDBFile := path.Join(*dataDir, "mailinglist.sqlite3")
|
mlStore, err := mailinglist.NewStore(dataDir)
|
||||||
ctx = mctx.Annotate(ctx, "mailingListDBFile", mailingListDBFile)
|
|
||||||
|
|
||||||
mlStore, err := mailinglist.NewStore(mailingListDBFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal(ctx, "initializing mailing list storage", err)
|
logger.Fatal(ctx, "initializing mailing list storage", err)
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mediocregopher/blog.mediocregopher.com/srv/api"
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/api"
|
||||||
"github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
|
"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/chat"
|
||||||
"github.com/mediocregopher/blog.mediocregopher.com/srv/mailinglist"
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/mailinglist"
|
||||||
"github.com/mediocregopher/blog.mediocregopher.com/srv/pow"
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/pow"
|
||||||
@ -23,11 +23,12 @@ func main() {
|
|||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
cfg := cfg.New(cfg.Params{
|
cfg := cfg.NewBlogCfg(cfg.Params{})
|
||||||
EnvPrefix: "MEDIOCRE_BLOG",
|
|
||||||
})
|
|
||||||
|
|
||||||
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
|
var powMgrParams pow.ManagerParams
|
||||||
powMgrParams.SetupCfg(cfg)
|
powMgrParams.SetupCfg(cfg)
|
||||||
@ -91,10 +92,7 @@ func main() {
|
|||||||
mailer = mailinglist.NewMailer(mailerParams)
|
mailer = mailinglist.NewMailer(mailerParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
mailingListDBFile := path.Join(*dataDir, "mailinglist.sqlite3")
|
mlStore, err := mailinglist.NewStore(dataDir)
|
||||||
ctx = mctx.Annotate(ctx, "mailingListDBFile", mailingListDBFile)
|
|
||||||
|
|
||||||
mlStore, err := mailinglist.NewStore(mailingListDBFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal(ctx, "initializing mailing list storage", err)
|
logger.Fatal(ctx, "initializing mailing list storage", err)
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,12 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
|
||||||
migrate "github.com/rubenv/sql-migrate"
|
migrate "github.com/rubenv/sql-migrate"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -83,13 +85,15 @@ type store struct {
|
|||||||
db *sql.DB
|
db *sql.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStore initializes a new Store using a sqlite3 database at the given file
|
// NewStore initializes a new Store using a sqlite3 database in the given
|
||||||
// path.
|
// DataDir.
|
||||||
func NewStore(dbFile string) (Store, error) {
|
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 {
|
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}
|
migrations := &migrate.MemoryMigrationSource{Migrations: migrations}
|
||||||
|
@ -2,31 +2,24 @@ package mailinglist
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStore(t *testing.T) {
|
func TestStore(t *testing.T) {
|
||||||
tmpFile, err := ioutil.TempFile(os.TempDir(), "mediocre-blog-mailinglist-store-test-")
|
|
||||||
if err != nil {
|
var dataDir cfg.DataDir
|
||||||
t.Fatal("Cannot create temporary file", err)
|
|
||||||
|
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() {
|
store, err := NewStore(dataDir)
|
||||||
if err := os.Remove(tmpFilePath); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
store, err := NewStore(tmpFilePath)
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
_ "github.com/mattn/go-sqlite3" // we need dis
|
_ "github.com/mattn/go-sqlite3" // we need dis
|
||||||
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
|
||||||
migrate "github.com/rubenv/sql-migrate"
|
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
|
// Params are parameters used to initialize a new Store. All fields are required
|
||||||
// unless otherwise noted.
|
// unless otherwise noted.
|
||||||
type StoreParams struct {
|
type StoreParams struct {
|
||||||
|
DataDir cfg.DataDir
|
||||||
// Path to the file the database will be stored at.
|
|
||||||
DBFilePath string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type store struct {
|
type store struct {
|
||||||
@ -113,9 +112,11 @@ type store struct {
|
|||||||
// path.
|
// path.
|
||||||
func NewStore(params StoreParams) (Store, error) {
|
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 {
|
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}
|
migrations := &migrate.MemoryMigrationSource{Migrations: migrations}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
package post
|
package post
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/tilinna/clock"
|
"github.com/tilinna/clock"
|
||||||
)
|
)
|
||||||
@ -29,25 +28,18 @@ type storeTestHarness struct {
|
|||||||
|
|
||||||
func newStoreTestHarness(t *testing.T) storeTestHarness {
|
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))
|
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{
|
store, err := NewStore(StoreParams{
|
||||||
DBFilePath: tmpFilePath,
|
DataDir: dataDir,
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user