1
0
forked from sm/vain

Added Save/Load to db

Fixes #3. Fixes #2
This commit is contained in:
Stephen McQuay 2016-02-14 22:19:41 -08:00
parent af4faf0206
commit 4cfaf2124f
4 changed files with 70 additions and 17 deletions

View File

@ -9,7 +9,7 @@ import (
) )
func TestAdd(t *testing.T) { func TestAdd(t *testing.T) {
ms := NewMemStore() ms := NewMemStore("")
s := &Server{ s := &Server{
storage: ms, storage: ms,
} }
@ -60,7 +60,7 @@ func TestAdd(t *testing.T) {
} }
func TestInvalidPath(t *testing.T) { func TestInvalidPath(t *testing.T) {
ms := NewMemStore() ms := NewMemStore("")
s := &Server{ s := &Server{
storage: ms, storage: ms,
} }
@ -80,7 +80,7 @@ func TestInvalidPath(t *testing.T) {
} }
func TestCannotDuplicateExistingPath(t *testing.T) { func TestCannotDuplicateExistingPath(t *testing.T) {
ms := NewMemStore() ms := NewMemStore("")
s := &Server{ s := &Server{
storage: ms, storage: ms,
} }
@ -105,7 +105,7 @@ func TestCannotDuplicateExistingPath(t *testing.T) {
} }
func TestCannotAddExistingSubPath(t *testing.T) { func TestCannotAddExistingSubPath(t *testing.T) {
ms := NewMemStore() ms := NewMemStore("")
s := &Server{ s := &Server{
storage: ms, storage: ms,
} }

View File

@ -22,17 +22,18 @@ environment vars:
YSV_PORT: tcp listen port YSV_PORT: tcp listen port
YSV_HOST: hostname to use YSV_HOST: hostname to use
YSV_DB: path to json database
` `
type config struct { type config struct {
Port int Port int
Host string Host string
DB string
} }
func main() { func main() {
c := &config{ c := &config{
Port: 4040, Port: 4040,
Host: "localhost",
} }
if err := envconfig.Process("ysv", c); err != nil { if err := envconfig.Process("ysv", c); err != nil {
fmt.Fprintf(os.Stderr, "problem processing environment: %v", err) fmt.Fprintf(os.Stderr, "problem processing environment: %v", err)
@ -41,9 +42,16 @@ func main() {
if len(os.Args) > 1 { if len(os.Args) > 1 {
switch os.Args[1] { switch os.Args[1] {
case "env", "e", "help", "h": case "env", "e", "help", "h":
fmt.Fprintf(os.Stderr, "%s\n", usage) fmt.Printf("%s\n", usage)
os.Exit(0)
}
}
if c.Host == "" {
log.Printf("must set YSV_HOST; please run $(ysvd env) for more information")
os.Exit(1) os.Exit(1)
} }
if c.DB == "" {
log.Printf("warning: in-memory db mode; if you do not want this set YSV_DB")
} }
hostname := "localhost" hostname := "localhost"
if hn, err := os.Hostname(); err != nil { if hn, err := os.Hostname(); err != nil {
@ -53,7 +61,11 @@ func main() {
} }
log.Printf("serving at: http://%s:%d/", hostname, c.Port) log.Printf("serving at: http://%s:%d/", hostname, c.Port)
sm := http.NewServeMux() sm := http.NewServeMux()
ms := vain.NewMemStore() ms := vain.NewMemStore(c.DB)
if err := ms.Load(); err != nil {
log.Printf("unable to load db: %v", err)
os.Exit(1)
}
vain.NewServer(sm, ms, c.Host) vain.NewServer(sm, ms, c.Host)
addr := fmt.Sprintf(":%d", c.Port) addr := fmt.Sprintf(":%d", c.Port)
if err := http.ListenAndServe(addr, sm); err != nil { if err := http.ListenAndServe(addr, sm); err != nil {

View File

@ -35,7 +35,17 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
http.Error(w, fmt.Sprintf("invalid path; prefix already taken %q", req.URL.Path), http.StatusConflict) http.Error(w, fmt.Sprintf("invalid path; prefix already taken %q", req.URL.Path), http.StatusConflict)
return return
} }
s.storage.Add(p) if err := s.storage.Add(p); err != nil {
http.Error(w, fmt.Sprintf("unable to add package: %v", err), http.StatusInternalServerError)
return
}
if err := s.storage.Save(); err != nil {
http.Error(w, fmt.Sprintf("unable to store db: %v", err), http.StatusInternalServerError)
if err := s.storage.Remove(p.Path); err != nil {
fmt.Fprintf(w, "to add insult to injury, could not delete package: %v\n", err)
}
return
}
default: default:
http.Error(w, fmt.Sprintf("unsupported method %q; accepted: POST, GET", req.Method), http.StatusMethodNotAllowed) http.Error(w, fmt.Sprintf("unsupported method %q; accepted: POST, GET", req.Method), http.StatusMethodNotAllowed)
} }

View File

@ -1,7 +1,8 @@
package vain package vain
import ( import (
"errors" "encoding/json"
"os"
"strings" "strings"
"sync" "sync"
) )
@ -18,33 +19,63 @@ func Valid(p string, packages []Package) bool {
type MemStore struct { type MemStore struct {
l sync.RWMutex l sync.RWMutex
p map[string]Package p map[string]Package
dbl sync.Mutex
path string
} }
func NewMemStore() *MemStore { func NewMemStore(path string) *MemStore {
return &MemStore{ return &MemStore{
path: path,
p: make(map[string]Package), p: make(map[string]Package),
} }
} }
func (ms MemStore) Add(p Package) error { func (ms *MemStore) Add(p Package) error {
ms.l.Lock() ms.l.Lock()
ms.p[p.Path] = p ms.p[p.Path] = p
ms.l.Unlock() ms.l.Unlock()
return nil return nil
} }
func (ms MemStore) Remove(path string) error { func (ms *MemStore) Remove(path string) error {
ms.l.Lock() ms.l.Lock()
delete(ms.p, path) delete(ms.p, path)
ms.l.Unlock() ms.l.Unlock()
return nil return nil
} }
func (ms MemStore) Save() error { func (ms *MemStore) Save() error {
return errors.New("save is not implemented") // running in-memory only
if ms.path == "" {
return nil
}
ms.dbl.Lock()
defer ms.dbl.Unlock()
f, err := os.Create(ms.path)
if err != nil {
return err
}
defer f.Close()
return json.NewEncoder(f).Encode(ms.p)
} }
func (ms MemStore) All() []Package { func (ms *MemStore) Load() error {
// running in-memory only
if ms.path == "" {
return nil
}
ms.dbl.Lock()
defer ms.dbl.Unlock()
f, err := os.Open(ms.path)
if err != nil {
return err
}
defer f.Close()
return json.NewDecoder(f).Decode(&ms.p)
}
func (ms *MemStore) All() []Package {
r := []Package{} r := []Package{}
ms.l.RLock() ms.l.RLock()
for _, p := range ms.p { for _, p := range ms.p {