veyron/services/store/stored: Add viewer to stored itself (enabled by default), and update mdb example accordingly, with a proper Makefile and full usage instructions in a README.md file.
Change-Id: I1d99b3e5990479bf82a6e16ef14f87dba36229c5
diff --git a/examples/mdb/Makefile b/examples/mdb/Makefile
new file mode 100644
index 0000000..aaa45dc
--- /dev/null
+++ b/examples/mdb/Makefile
@@ -0,0 +1,7 @@
+build:
+ ${VEYRON_ROOT}/veyron/scripts/build/go install veyron/... veyron2/...
+
+run: build
+ ./run.sh
+
+.PHONY: build run
diff --git a/examples/mdb/README.md b/examples/mdb/README.md
new file mode 100644
index 0000000..49cfc8d
--- /dev/null
+++ b/examples/mdb/README.md
@@ -0,0 +1,12 @@
+# mdb example
+
+A simple "movie database" example of store usage.
+
+## How to run
+
+Simply run `make run`. Under the hood, this generates a self-signed identity,
+starts a mounttable daemon, starts a store daemon, and initializes the store
+with mdb data and templates.
+
+Once everything's up and running, visit the store daemon's viewer in your
+browser (http://localhost:5000 by default) to explore the mdb data.
diff --git a/examples/storage/mdb/mdb_init/main.go b/examples/mdb/mdb_init/main.go
similarity index 95%
rename from examples/storage/mdb/mdb_init/main.go
rename to examples/mdb/mdb_init/main.go
index 28f8166..022a362 100644
--- a/examples/storage/mdb/mdb_init/main.go
+++ b/examples/mdb/mdb_init/main.go
@@ -22,7 +22,7 @@
"strings"
"time"
- "veyron/examples/storage/mdb/schema"
+ "veyron/examples/mdb/schema"
"veyron2/naming"
"veyron2/rt"
"veyron2/storage"
@@ -353,7 +353,7 @@
return nil
}
-// processFile stores the contens of the file to the store.
+// processFile stores the contents of the file in the store.
func (st *state) processFile(path, name string) error {
switch filepath.Ext(path) {
case ".json":
@@ -375,11 +375,6 @@
// main reads all the files in the templates directory and adds them to the
// store.
func main() {
- // The client's identity needs to match the Admin ACLs at the empty
- // store (since only the admin can put data). The identity here
- // matches with that used for server.ServerConfig.Admin in
- // mdb_stored/main.go. An alternative would be to relax the ACLs on
- // the store.
rt.Init()
vlog.Infof("Binding to store on %s", storeName)
@@ -389,11 +384,13 @@
}
state := newState(st)
- // Store all templates.
+ // Store all data and templates.
filepath.Walk(*templatesDir, func(path string, _ os.FileInfo, _ error) error {
err := state.processFile(path, strings.TrimPrefix(path, *templatesDir))
if err != nil {
- vlog.Infof("%s: %s", path, err)
+ vlog.Fatalf("Error processing %s: %s", path, err)
+ } else {
+ vlog.Infof("Processed %s", path)
}
return err
})
diff --git a/examples/mdb/run.sh b/examples/mdb/run.sh
new file mode 100755
index 0000000..aa0d811
--- /dev/null
+++ b/examples/mdb/run.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+set -e
+set -u
+
+VEYRON_BIN=${VEYRON_ROOT}/veyron/go/bin
+ID_FILE=/var/tmp/id
+
+# Generate a self-signed identity.
+${VEYRON_BIN}/identity generate > ${ID_FILE}
+
+# Start the mounttable daemon.
+${VEYRON_BIN}/mounttabled --address=':8100' &
+
+export VEYRON_IDENTITY=${ID_FILE}
+export NAMESPACE_ROOT='/127.0.0.1:8100'
+
+sleep 1 # Wait for mounttabled to start up.
+
+# Start the store daemon.
+rm -rf /var/tmp/veyron_store.db
+${VEYRON_BIN}/stored &
+
+sleep 1 # Wait for stored to start up.
+
+# Initialize the store with mdb data and templates.
+${VEYRON_BIN}/mdb_init --load-all
+
+echo
+echo 'Visit http://localhost:5000 to browse the mdb data.'
+echo 'Hit Ctrl-C to kill all running services.'
+
+trap 'kill $(jobs -pr)' INT TERM EXIT
+wait
diff --git a/examples/storage/mdb/schema/init.go b/examples/mdb/schema/init.go
similarity index 100%
rename from examples/storage/mdb/schema/init.go
rename to examples/mdb/schema/init.go
diff --git a/examples/storage/mdb/schema/schema.vdl b/examples/mdb/schema/schema.vdl
similarity index 100%
rename from examples/storage/mdb/schema/schema.vdl
rename to examples/mdb/schema/schema.vdl
diff --git a/examples/storage/mdb/schema/schema.vdl.go b/examples/mdb/schema/schema.vdl.go
similarity index 100%
rename from examples/storage/mdb/schema/schema.vdl.go
rename to examples/mdb/schema/schema.vdl.go
diff --git a/examples/storage/mdb/templates/contents.json b/examples/mdb/templates/contents.json
similarity index 100%
rename from examples/storage/mdb/templates/contents.json
rename to examples/mdb/templates/contents.json
diff --git a/examples/storage/mdb/templates/css/movie.css b/examples/mdb/templates/css/movie.css
similarity index 100%
rename from examples/storage/mdb/templates/css/movie.css
rename to examples/mdb/templates/css/movie.css
diff --git a/examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Dir.tmpl b/examples/mdb/templates/veyron/examples/mdb/schema/Dir.tmpl
similarity index 100%
rename from examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Dir.tmpl
rename to examples/mdb/templates/veyron/examples/mdb/schema/Dir.tmpl
diff --git a/examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Movie.tmpl b/examples/mdb/templates/veyron/examples/mdb/schema/Movie.tmpl
similarity index 100%
rename from examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Movie.tmpl
rename to examples/mdb/templates/veyron/examples/mdb/schema/Movie.tmpl
diff --git a/examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Person.tmpl b/examples/mdb/templates/veyron/examples/mdb/schema/Person.tmpl
similarity index 100%
rename from examples/storage/mdb/templates/veyron/examples/storage/mdb/schema/Person.tmpl
rename to examples/mdb/templates/veyron/examples/mdb/schema/Person.tmpl
diff --git a/examples/storage/mdb/mdbd/main.go b/examples/storage/mdb/mdbd/main.go
deleted file mode 100644
index 1cd2735..0000000
--- a/examples/storage/mdb/mdbd/main.go
+++ /dev/null
@@ -1,49 +0,0 @@
-// mdbd provides a UI for the mdb/schema movie database.
-//
-// The main purpose of this server is to register the types for the mdb/schema
-// so that the html/templates work correctly.
-package main
-
-import (
- "flag"
- "fmt"
- "log"
- "os"
- "os/user"
-
- // Register the mdb/schema types.
- _ "veyron/examples/storage/mdb/schema"
- "veyron/examples/storage/viewer"
- "veyron2/rt"
- "veyron2/storage/vstore"
-)
-
-var (
- storeName string
- port = flag.Int("port", 10000, "IPV4 port number to serve")
-)
-
-func init() {
- username := "unknown"
- if u, err := user.Current(); err == nil {
- username = u.Username
- }
- hostname := "unknown"
- if h, err := os.Hostname(); err == nil {
- hostname = h
- }
- dir := "global/vstore/" + hostname + "/" + username
- flag.StringVar(&storeName, "store", dir, "Name of the Veyron store")
-}
-
-func main() {
- rt.Init()
-
- log.Printf("Binding to store on %s", storeName)
- st, err := vstore.New(storeName)
- if err != nil {
- log.Fatalf("Can't connect to store: %s: %s", storeName, err)
- }
-
- viewer.ListenAndServe(fmt.Sprintf(":%d", *port), st)
-}
diff --git a/examples/storage/viewer/reflect.go b/examples/storage/viewer/reflect.go
deleted file mode 100644
index c7b4359..0000000
--- a/examples/storage/viewer/reflect.go
+++ /dev/null
@@ -1,150 +0,0 @@
-package viewer
-
-import (
- "bytes"
- "fmt"
- "reflect"
-
- "veyron2/storage"
-)
-
-// printer defines a pretty printer for Go values, using reflection to traverse
-// the values.
-type printer struct {
- buf bytes.Buffer
-}
-
-var (
- tyID = reflect.TypeOf(storage.ID{})
-)
-
-// stringTypePointers removes the outer Ptr and Interface types.
-func stripTypePointers(ty reflect.Type) reflect.Type {
- kind := ty.Kind()
- for kind == reflect.Ptr || kind == reflect.Interface {
- ty = ty.Elem()
- kind = ty.Kind()
- }
- return ty
-}
-
-// templatePath returns the path to the templates value, defined as
-// /templates/<pkgPath>/<typeName>.
-func templatePath(v interface{}) string {
- ty := stripTypePointers(reflect.TypeOf(v))
- pkgPath := ty.PkgPath()
- tyName := ty.Name()
- if pkgPath == "" || tyName == "" {
- return ""
- }
- return fmt.Sprintf("templates/%s/%s", pkgPath, tyName)
-}
-
-// print formats the argument.
-func (p *printer) print(v interface{}) {
- p.printValue(0, reflect.ValueOf(v))
-}
-
-// printType formats the type.
-func (p *printer) printType(indent int, v reflect.Type) {
- p.printString(v.Name())
-}
-
-// printValue is the main pretty-printer method. It formats the value at the
-// indentation level specified by the argument.
-func (p *printer) printValue(indent int, v reflect.Value) {
- if !v.IsValid() {
- p.printString("<invalid>")
- return
- }
- ty := v.Type()
- if ty == tyID {
- p.printString(v.Interface().(storage.ID).String())
- return
- }
- switch ty.Kind() {
- case reflect.Ptr:
- p.printString("&")
- fallthrough
- case reflect.Interface:
- p.printValue(indent, v.Elem())
- case reflect.Array, reflect.Slice:
- p.printSliceValue(indent, v)
- case reflect.Map:
- p.printMapValue(indent, v)
- case reflect.Struct:
- p.printStructValue(indent, v)
- case reflect.String:
- fmt.Fprintf(&p.buf, "%q", v.Interface())
- default:
- fmt.Fprintf(&p.buf, "%+v", v.Interface())
- }
-}
-
-// printSliceValue formats a slice or array.
-func (p *printer) printSliceValue(indent int, v reflect.Value) {
- p.printType(indent, v.Type())
- p.printString("{")
- l := v.Len()
- for i := 0; i < l; i++ {
- p.printIndent(indent + 1)
- p.printValue(indent+1, v.Index(i))
- p.printString(",")
- }
- p.printIndent(indent)
- p.printString("}")
-}
-
-// printMapValue formats a map.
-func (p *printer) printMapValue(indent int, v reflect.Value) {
- p.printType(indent, v.Type())
- p.printString("{")
- for _, key := range v.MapKeys() {
- p.printIndent(indent + 1)
- p.printValue(indent+1, key)
- p.printString(": ")
- p.printValue(indent+2, v.MapIndex(key))
- p.printString(",")
- }
- p.printIndent(indent)
- p.printString("}")
-}
-
-// printStructValue formats a struct.
-func (p *printer) printStructValue(indent int, v reflect.Value) {
- ty := v.Type()
- p.printType(indent, ty)
- p.printString("{")
- l := ty.NumField()
- for i := 0; i < l; i++ {
- field := ty.Field(i)
- if field.PkgPath != "" {
- continue
- }
- p.printIndent(indent + 1)
- p.printString(field.Name)
- p.printString(": ")
- p.printValue(indent+1, v.Field(i))
- p.printString(",")
- }
- p.printIndent(indent)
- p.printString("}")
-}
-
-// printString adds a string to the output.
-func (p *printer) printString(s string) {
- p.buf.WriteString(s)
-}
-
-// printIndent prints a newline and then indents to the specified indentation.
-func (p *printer) printIndent(indent int) {
- p.buf.WriteRune('\n')
- for i := 0; i < indent; i++ {
- p.buf.WriteString(" ")
- }
-}
-
-// String returns the formatted text.
-func (p *printer) String() string {
- return p.buf.String()
-}
diff --git a/examples/storage/viewer/value.go b/examples/storage/viewer/value.go
deleted file mode 100644
index b82f09e..0000000
--- a/examples/storage/viewer/value.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package viewer
-
-import (
- "path/filepath"
- "sort"
- "time"
-
- "veyron2/rt"
- "veyron2/storage"
-)
-
-// Value is the type used to pass store values to the template. The Value
-// contains the name of the value, the actual value, and a list of
-// subdirectories.
-type Value struct {
- store storage.Store
- Name string
- Value interface{}
- Subdirs []string
-}
-
-// glob performs a glob expansion of the pattern. The results are sorted.
-func glob(st storage.Store, path, pattern string) ([]string, error) {
- results := st.BindObject(path).Glob(rt.R().TODOContext(), pattern)
- names := []string{}
- rStream := results.RecvStream()
- for rStream.Advance() {
- names = append(names, "/"+rStream.Value())
- }
- if err := rStream.Err(); err != nil {
- return nil, err
- }
- sort.Strings(names)
- return names, nil
-}
-
-// fullpath returns the absolute path from a relative path.
-func (v *Value) fullpath(path string) string {
- if len(path) == 0 || path[0] != '/' {
- return v.Name + "/" + path
- }
- return path
-}
-
-// Date performs a Time conversion, given an integer argument that represents a
-// time in nanoseconds since the Unix epoch.
-func (v *Value) Date(ns int64) time.Time {
- return time.Unix(0, ns)
-}
-
-// Join joins the path elements.
-func (v *Value) Join(elem ...string) string {
- return filepath.ToSlash(filepath.Join(elem...))
-}
-
-// Base returns the last element of the path.
-func (v *Value) Base(path string) string {
- return filepath.Base(path)
-}
-
-// Glob performs a glob expansion of a pattern.
-func (v *Value) Glob(pattern string) ([]string, error) {
- return glob(v.store, v.Name, pattern)
-}
-
-// Get fetches a value from the store. The result is nil if the value does not
-// exist.
-func (v *Value) Get(path string) interface{} {
- path = v.fullpath(path)
- e, err := v.store.BindObject(path).Get(rt.R().TODOContext())
- if err != nil {
- return nil
- }
- return e.Value
-}
diff --git a/examples/storage/viewer/viewer.go b/examples/storage/viewer/viewer.go
deleted file mode 100644
index f7cee5b..0000000
--- a/examples/storage/viewer/viewer.go
+++ /dev/null
@@ -1,157 +0,0 @@
-// package viewer exports a store through an HTTP server, with the following features.
-//
-// URL paths correspond to store paths. For example, if the URL is
-// http://myhost/a/b/c, the store value that is fetched is /a/b/c.
-//
-// Values can be formatted using html templates (documented in the
-// text/templates package). Templates are named according to the type of the
-// value that they format, using a path /templates/<pkgPath>/<typeName>. For
-// example, suppose we are viewing the page for /movies/Inception, and it
-// contains a value of type *examples/store/mdb/schema.Movie. We fetch the
-// template /templates/examples/store/mdb/schema/Movie, which must be a string in
-// html/template format. If it exists, the template is compiled and used to
-// print the value. If the template does not exist, the value is formatted in
-// raw form.
-//
-// String values that have a path ending with suffix .css are printed in raw form.
-package viewer
-
-import (
- "fmt"
- "html"
- "html/template"
- "io"
- "net/http"
- "path/filepath"
-
- "veyron2/rt"
- "veyron2/storage"
- "veyron2/vlog"
-)
-
-// server is the HTTP server handler.
-type server struct {
- store storage.Store
-}
-
-var _ http.Handler = (*server)(nil)
-
-const (
- // rawTemplateText is used to format the output in a raw textual form.
- rawTemplateText = `<html>
-{{$value := .}}
-<head>
-<title>{{.Name}}</title>
-</head>
-<body>
-<h1>{{.Name}}</h1>
-<pre>{{.Value}}</pre>
-{{with .Subdirs}}
-<h3>Subdirectories</h3>
-{{range .}}
-<p><a href="{{.}}">{{$value.Base .}}</a></p>
-{{end}}
-{{end}}
-</body>
-</html>`
-)
-
-var (
- rawTemplate = mustParse("raw", rawTemplateText)
-)
-
-// mustParse parses the template text. It panics on error.
-func mustParse(name, text string) *template.Template {
- tmpl, err := template.New(name).Parse(text)
- if err != nil {
- panic(fmt.Sprintf("Error parsing template %q: %s", text, err))
- }
- return tmpl
-}
-
-// loadTemplate fetches the template for the value from the store. The template
-// is based on the type of the value, under /template/<pkgPath>/<typeName>.
-func (s *server) loadTemplate(v interface{}) *template.Template {
- path := templatePath(v)
- e, err := s.store.BindObject(path).Get(rt.R().TODOContext())
- if err != nil {
- return nil
- }
- str, ok := e.Value.(string)
- if !ok {
- return nil
- }
- tmpl, err := template.New(path).Parse(str)
- if err != nil {
- vlog.Infof("Template error: %s: %s", path, err)
- return nil
- }
- return tmpl
-}
-
-// printRawValuePage prints the value in raw format.
-func (s *server) printRawValuePage(w http.ResponseWriter, path string, v interface{}) {
- var p printer
- p.print(v)
- subdirs, _ := glob(s.store, path, "*")
- x := &Value{Name: path, Value: p.String(), Subdirs: subdirs}
- if err := rawTemplate.Execute(w, x); err != nil {
- w.Write([]byte(html.EscapeString(err.Error())))
- }
-}
-
-// printValuePage prints the value using a template if possible. If a template
-// is not found, the value is printed in raw format instead.
-func (s *server) printValuePage(w http.ResponseWriter, path string, v interface{}) {
- if tmpl := s.loadTemplate(v); tmpl != nil {
- if err := tmpl.Execute(w, &Value{store: s.store, Name: path, Value: v}); err != nil {
- w.Write([]byte(html.EscapeString(err.Error())))
- }
- return
- }
- s.printRawValuePage(w, path, v)
-}
-
-// printRawPage prints a string value directly, without processing.
-func (s *server) printRawPage(w http.ResponseWriter, v interface{}) {
- str, ok := v.(string)
- if !ok {
- fmt.Fprintf(w, "%s", v)
- } else {
- io.WriteString(w, str)
- }
-}
-
-// ServeHTTP is the main HTTP handler.
-func (s *server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
- path := req.URL.Path
- e, err := s.store.BindObject(path).Get(rt.R().TODOContext())
- if err != nil {
- msg := fmt.Sprintf("<html><body><h1>%s</h1><h2>Error: %s</h2></body></html>",
- html.EscapeString(path),
- html.EscapeString(err.Error()))
- w.WriteHeader(http.StatusNotFound)
- w.Write([]byte(msg))
- return
- }
-
- q := req.URL.Query()
- switch filepath.Ext(path) {
- case ".css":
- s.printRawPage(w, e.Value)
- default:
- if q["raw"] != nil {
- s.printRawValuePage(w, path, e.Value)
- } else {
- s.printValuePage(w, path, e.Value)
- }
- }
-}
-
-// ListenAndServe is the main entry point. It serves store at the specified
-// network address.
-func ListenAndServe(addr string, st storage.Store) error {
- s := &server{store: st}
- vlog.Infof("Viewer running at http://localhost%s", addr)
- return http.ListenAndServe(addr, s)
-}
diff --git a/examples/todos/todos_appd/main.go b/examples/todos/todos_appd/main.go
index ebc1192..3c6c2b7 100644
--- a/examples/todos/todos_appd/main.go
+++ b/examples/todos/todos_appd/main.go
@@ -1,6 +1,6 @@
// todos_appd is a web application backed by a Veyron store.
//
-// For now, it simply displays the raw contents of the store.
+// It doesn't work yet, but it will soon. :)
// TODO(sadovsky): Implement actual app, using Veyron {store,query,watch,sync}
// over veyron.js.
@@ -11,22 +11,18 @@
"fmt"
"html/template"
"io"
- "log"
"net/http"
"os"
"os/user"
"path"
- "veyron/examples/storage/viewer"
_ "veyron/examples/todos/schema" // Register the todos/schema types.
"veyron2/rt"
- "veyron2/storage/vstore"
)
var (
storeName string
- port = flag.Int("port", 10000, "IPV4 port number to serve")
- useViewer = flag.Bool("useViewer", false, "If true, serve viewer instead")
+ port = flag.Int("port", 10000, "IPV4 port to serve")
)
var rootDir = path.Join(
@@ -47,16 +43,6 @@
flag.StringVar(&storeName, "store", dir, "Name of the Veyron store")
}
-func serveViewer() {
- log.Printf("Binding to store on %s", storeName)
- st, err := vstore.New(storeName)
- if err != nil {
- log.Fatalf("Can't connect to store: %s: %s", storeName, err)
- }
-
- viewer.ListenAndServe(fmt.Sprintf(":%d", *port), st)
-}
-
func renderTemplate(w io.Writer, basename string, data interface{}) {
filename := path.Join(rootDir, "templates", basename)
t, err := template.ParseFiles(filename)
@@ -84,13 +70,10 @@
func main() {
rt.Init()
- if *useViewer {
- serveViewer()
- } else {
- http.HandleFunc("/", wrap(handleHome))
- http.Handle("/css/", http.FileServer(http.Dir(rootDir)))
- http.Handle("/js/", http.FileServer(http.Dir(rootDir)))
- fmt.Printf("Server running at http://localhost:%d\n", *port)
- http.ListenAndServe(fmt.Sprintf(":%d", *port), nil)
- }
+ http.HandleFunc("/", wrap(handleHome))
+ http.Handle("/css/", http.FileServer(http.Dir(rootDir)))
+ http.Handle("/js/", http.FileServer(http.Dir(rootDir)))
+
+ fmt.Printf("Server running at http://localhost:%d\n", *port)
+ http.ListenAndServe(fmt.Sprintf(":%d", *port), nil)
}
diff --git a/examples/todos/todos_init/main.go b/examples/todos/todos_init/main.go
index 7013a0c..e163461 100644
--- a/examples/todos/todos_init/main.go
+++ b/examples/todos/todos_init/main.go
@@ -1,12 +1,4 @@
-// todos_init is a tool to initialize the store with an initial database. This
-// is really for demo purposes; in a real database, the contents would be
-// persistent.
-//
-// The data is loaded from a JSON file, todos_init/data.json.
-//
-// Since JSON doesn't support all of the store types, there is a translation
-// phase, where the contents are loaded into a string form, then converted to
-// the todos/schema schema.
+// todos_init reads data.json and populates the store with initial data.
package main
import (
@@ -42,7 +34,6 @@
if h, err := os.Hostname(); err == nil {
hostname = h
}
- // TODO(sadovsky): Change this to be the correct veyron2 path.
dir := "global/vstore/" + hostname + "/" + username
flag.StringVar(&storeName, "store", dir, "Name of the Veyron store")
}
@@ -61,7 +52,7 @@
// state is the initial store state.
type state struct {
store storage.Store
- tname string // Current transaction name; nil if there's no transaction.
+ tname string // Current transaction name; empty if there's no transaction.
}
// newState returns a fresh state.
@@ -87,9 +78,11 @@
for i, _ := range l {
prefix := filepath.Join(l[:i]...)
o := st.store.BindObject(naming.Join(st.tname, prefix))
- if _, err := o.Get(rt.R().TODOContext()); err != nil {
+ if exist, err := o.Exists(rt.R().TODOContext()); err != nil {
+ vlog.Infof("Error checking existence at %q: %s", prefix, err)
+ } else if !exist {
if _, err := o.Put(rt.R().TODOContext(), &schema.Dir{}); err != nil {
- vlog.Errorf("Error creating parent %q: %s", prefix, err)
+ vlog.Infof("Error creating parent %q: %s", prefix, err)
}
}
}
@@ -171,9 +164,8 @@
// main reads the data JSON file and populates the store.
func main() {
// The client's identity needs to match the Admin ACLs at the empty store
- // (since only the admin can put data). The identity here matches with that
- // used for server.ServerConfig.Admin in todos_stored/main.go. An alternative
- // would be to relax the ACLs on the store.
+ // (since only the admin can put data).
+ // TODO(sadovsky): What identity should we pass here?
rt.Init(veyron2.RuntimeID(security.FakePrivateID("anonymous")))
vlog.Infof("Binding to store on %s", storeName)
diff --git a/services/store/stored/main.go b/services/store/stored/main.go
index 0988933..55012ab 100644
--- a/services/store/stored/main.go
+++ b/services/store/stored/main.go
@@ -14,6 +14,7 @@
import (
"flag"
+ "fmt"
"log"
"os"
"os/user"
@@ -22,6 +23,8 @@
"veyron/services/store/server"
"veyron2/rt"
+ "veyron2/storage/viewer"
+ "veyron2/storage/vstore"
_ "veyron/services/store/typeregistryhack"
)
@@ -29,8 +32,11 @@
var (
mountName string
dbName = flag.String("db", "/var/tmp/veyron_store.db", "Metadata database")
- // TODO(rthellend): Remove the address flag when the config manager is working.
- address = flag.String("address", ":0", "Address to listen on.")
+ // TODO(rthellend): Remove the address flag when the config manager is
+ // working.
+ address = flag.String("address", ":0", "Address to listen on")
+ viewerPort = flag.Int("viewerPort", 5000,
+ "IPV4 port to serve viewer from, or 0 to disable viewer")
)
func init() {
@@ -46,8 +52,7 @@
flag.StringVar(&mountName, "name", dir, "Mount point for media")
}
-// Main starts the content service, taking arguments from the command line
-// flags.
+// main starts the store service, taking args from command line flags.
func main() {
r := rt.Init()
@@ -58,7 +63,8 @@
}
// Create a new StoreService.
- storeService, err := server.New(server.ServerConfig{Admin: r.Identity().PublicID(), DBName: *dbName})
+ storeService, err := server.New(
+ server.ServerConfig{Admin: r.Identity().PublicID(), DBName: *dbName})
if err != nil {
log.Fatal("server.New() failed: ", err)
}
@@ -80,6 +86,15 @@
log.Fatal("s.Serve() failed: ", err)
}
+ // Run viewer if requested.
+ if *viewerPort > 0 {
+ vst, err := vstore.New(mountName)
+ if err != nil {
+ log.Fatalf("Failed to start viewer: %s", err)
+ }
+ go viewer.ListenAndServe(fmt.Sprintf(":%d", *viewerPort), vst)
+ }
+
// Wait forever.
done := make(chan struct{})
<-done
diff --git a/services/store/typeregistryhack/init.go b/services/store/typeregistryhack/init.go
index 72b5216..bea5d02 100644
--- a/services/store/typeregistryhack/init.go
+++ b/services/store/typeregistryhack/init.go
@@ -8,7 +8,7 @@
// Register boxes types.
"veyron/examples/boxes"
// Register mdb types.
- _ "veyron/examples/storage/mdb/schema"
+ _ "veyron/examples/mdb/schema"
// Register todos types.
_ "veyron/examples/todos/schema"
// Register bank types.