parent
af4faf0206
commit
4cfaf2124f
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
12
server.go
12
server.go
@ -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)
|
||||||
}
|
}
|
||||||
|
45
storage.go
45
storage.go
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user