فهرست منبع

working on rethinkdb stuff

Torkel Ödegaard 11 سال پیش
والد
کامیت
07d8b542bf
10فایلهای تغییر یافته به همراه437 افزوده شده و 286 حذف شده
  1. 1 1
      grafana
  2. 2 0
      install_dependencies.sh
  3. 13 3
      pkg/api/api_dashboard.go
  4. 23 16
      pkg/models/dashboards.go
  5. 154 153
      pkg/stores/file_store.go
  6. 111 110
      pkg/stores/file_store_test.go
  7. 94 0
      pkg/stores/rethinkdb.go
  8. 29 0
      pkg/stores/rethinkdb_test.go
  9. 3 3
      pkg/stores/store.go
  10. 7 0
      start_dependencies.sh

+ 1 - 1
grafana

@@ -1 +1 @@
-Subproject commit 34ab1e529b499af836631f8076c2c4df02be5860
+Subproject commit 06de80f2a764b76df6047fc4700c74aaf5734521

+ 2 - 0
install_dependencies.sh

@@ -0,0 +1,2 @@
+go get code.google.com/p/goprotobuf/{proto,protoc-gen-go}
+

+ 13 - 3
pkg/api/api_dashboard.go

@@ -1,6 +1,7 @@
 package api
 
 import (
+	log "github.com/alecthomas/log4go"
 	"github.com/gin-gonic/gin"
 	"github.com/torkelo/grafana-pro/pkg/models"
 )
@@ -16,7 +17,7 @@ func init() {
 func (self *HttpServer) getDashboard(c *gin.Context) {
 	id := c.Params.ByName("id")
 
-	dash, err := self.store.GetById(id)
+	dash, err := self.store.GetDashboardByTitle(id, "test")
 	if err != nil {
 		c.JSON(404, newErrorResponse("Dashboard not found"))
 		return
@@ -30,6 +31,7 @@ func (self *HttpServer) search(c *gin.Context) {
 
 	results, err := self.store.Query(query)
 	if err != nil {
+		log.Error("Store query error: %v", err)
 		c.JSON(500, newErrorResponse("Failed"))
 		return
 	}
@@ -41,9 +43,17 @@ func (self *HttpServer) postDashboard(c *gin.Context) {
 	var command saveDashboardCommand
 
 	if c.EnsureBody(&command) {
-		err := self.store.Save(&models.Dashboard{Data: command.Dashboard})
+		dashboard := models.NewDashboard("test")
+		dashboard.Data = command.Dashboard
+		dashboard.Title = dashboard.Data["title"].(string)
+
+		if dashboard.Data["id"] != nil {
+			dashboard.Id = dashboard.Data["id"].(string)
+		}
+
+		err := self.store.SaveDashboard(dashboard)
 		if err == nil {
-			c.JSON(200, gin.H{"status": "saved"})
+			c.JSON(200, gin.H{"status": "success", "id": dashboard.Id})
 			return
 		}
 	}

+ 23 - 16
pkg/models/dashboards.go

@@ -3,20 +3,39 @@ package models
 import (
 	"encoding/json"
 	"io"
+	"time"
 )
 
 type Dashboard struct {
-	Data map[string]interface{}
+	Id                   string `gorethink:"id,omitempty"`
+	AccountId            string
+	LastModifiedByUserId string
+	LastModifiedByDate   time.Time
+	CreatedDate          time.Time
+
+	Title string
+	Tags  []string
+	Data  map[string]interface{}
+}
+
+type UserContext struct {
+	UserId    string
+	AccountId string
 }
 
 type SearchResult struct {
-	Type  string `json:"title"`
 	Id    string `json:"id"`
 	Title string `json:"title"`
 }
 
 func NewDashboard(title string) *Dashboard {
 	dash := &Dashboard{}
+	dash.Id = ""
+	dash.AccountId = "test"
+	dash.LastModifiedByDate = time.Now()
+	dash.CreatedDate = time.Now()
+	dash.LastModifiedByUserId = "123"
+	dash.Title = title
 	dash.Data = make(map[string]interface{})
 	dash.Data["title"] = title
 
@@ -31,23 +50,11 @@ func NewFromJson(reader io.Reader) (*Dashboard, error) {
 		return nil, err
 	}
 
-	return dash, nil
-}
+	dash.Title = dash.Data["title"].(string)
 
-/*type DashboardServices struct {
-}
-
-type DashboardServicesFilter struct {
+	return dash, nil
 }
 
-type DashboardServicesFilterTime struct {
-	From string 	To	string
-}*/
-
 func (dash *Dashboard) GetString(prop string) string {
 	return dash.Data[prop].(string)
 }
-
-func (dash *Dashboard) Title() string {
-	return dash.GetString("title")
-}

+ 154 - 153
pkg/stores/file_store.go

@@ -1,155 +1,156 @@
 package stores
 
-import (
-	"encoding/json"
-	"io"
-	"os"
-	"path/filepath"
-	"strings"
-
-	log "github.com/alecthomas/log4go"
-	"github.com/torkelo/grafana-pro/pkg/models"
-)
-
-type fileStore struct {
-	dataDir string
-	dashDir string
-	cache   map[string]*models.Dashboard
-}
-
-func NewFileStore(dataDir string) *fileStore {
-
-	if dirDoesNotExist(dataDir) {
-		log.Crashf("FileStore failed to initialize, dataDir does not exist %v", dataDir)
-	}
-
-	dashDir := filepath.Join(dataDir, "dashboards")
-
-	if dirDoesNotExist(dashDir) {
-		log.Debug("Did not find dashboard dir, creating...")
-		err := os.Mkdir(dashDir, 0777)
-		if err != nil {
-			log.Crashf("FileStore failed to initialize, could not create directory %v, error: %v", dashDir, err)
-		}
-	}
-
-	store := &fileStore{}
-	store.dataDir = dataDir
-	store.dashDir = dashDir
-	store.cache = make(map[string]*models.Dashboard)
-	go store.scanFiles()
-
-	return store
-}
-
-func (store *fileStore) scanFiles() {
-	visitor := func(path string, f os.FileInfo, err error) error {
-		if err != nil {
-			return err
-		}
-		if f.IsDir() {
-			return nil
-		}
-		if strings.HasSuffix(f.Name(), ".json") {
-			err = store.loadDashboardIntoCache(path)
-			if err != nil {
-				return err
-			}
-		}
-		return nil
-	}
-
-	err := filepath.Walk(store.dashDir, visitor)
-	if err != nil {
-		log.Error("FileStore::updateCache failed %v", err)
-	}
-}
-
-func (store fileStore) loadDashboardIntoCache(filename string) error {
-	log.Info("Loading dashboard file %v into cache", filename)
-	dash, err := loadDashboardFromFile(filename)
-	if err != nil {
-		return err
-	}
-
-	store.cache[dash.Title()] = dash
-
-	return nil
-}
-
-func (store *fileStore) Close() {
-
-}
-
-func (store *fileStore) GetById(id string) (*models.Dashboard, error) {
-	log.Debug("FileStore::GetById id = %v", id)
-	filename := store.getFilePathForDashboard(id)
-
-	return loadDashboardFromFile(filename)
-}
-
-func (store *fileStore) Save(dash *models.Dashboard) error {
-	filename := store.getFilePathForDashboard(dash.Title())
-
-	log.Debug("Saving dashboard %v to %v", dash.Title(), filename)
-
-	var err error
-	var data []byte
-	if data, err = json.Marshal(dash.Data); err != nil {
-		return err
-	}
-
-	return writeFile(filename, data)
-}
-
-func (store *fileStore) Query(query string) ([]*models.SearchResult, error) {
-	results := make([]*models.SearchResult, 0, 50)
-
-	for _, dash := range store.cache {
-		item := &models.SearchResult{
-			Id:   dash.Title(),
-			Type: "dashboard",
-		}
-		results = append(results, item)
-	}
-
-	return results, nil
-}
-
-func loadDashboardFromFile(filename string) (*models.Dashboard, error) {
-	log.Debug("FileStore::loading dashboard from file %v", filename)
-
-	configFile, err := os.Open(filename)
-	if err != nil {
-		return nil, err
-	}
-
-	return models.NewFromJson(configFile)
-}
-
-func (store *fileStore) getFilePathForDashboard(id string) string {
-	id = strings.ToLower(id)
-	id = strings.Replace(id, " ", "-", -1)
-	return filepath.Join(store.dashDir, id) + ".json"
-}
-
-func dirDoesNotExist(dir string) bool {
-	_, err := os.Stat(dir)
-	return os.IsNotExist(err)
-}
-
-func writeFile(filename string, data []byte) error {
-	f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
-	if err != nil {
-		return err
-	}
-	n, err := f.Write(data)
-	if err == nil && n < len(data) {
-		err = io.ErrShortWrite
-	}
-	if err1 := f.Close(); err == nil {
-		err = err1
-	}
-
-	return err
-}
+//
+// import (
+// 	"encoding/json"
+// 	"io"
+// 	"os"
+// 	"path/filepath"
+// 	"strings"
+//
+// 	log "github.com/alecthomas/log4go"
+// 	"github.com/torkelo/grafana-pro/pkg/models"
+// )
+//
+// type fileStore struct {
+// 	dataDir string
+// 	dashDir string
+// 	cache   map[string]*models.Dashboard
+// }
+//
+// func NewFileStore(dataDir string) *fileStore {
+//
+// 	if dirDoesNotExist(dataDir) {
+// 		log.Crashf("FileStore failed to initialize, dataDir does not exist %v", dataDir)
+// 	}
+//
+// 	dashDir := filepath.Join(dataDir, "dashboards")
+//
+// 	if dirDoesNotExist(dashDir) {
+// 		log.Debug("Did not find dashboard dir, creating...")
+// 		err := os.Mkdir(dashDir, 0777)
+// 		if err != nil {
+// 			log.Crashf("FileStore failed to initialize, could not create directory %v, error: %v", dashDir, err)
+// 		}
+// 	}
+//
+// 	store := &fileStore{}
+// 	store.dataDir = dataDir
+// 	store.dashDir = dashDir
+// 	store.cache = make(map[string]*models.Dashboard)
+// 	store.scanFiles()
+//
+// 	return store
+// }
+//
+// func (store *fileStore) scanFiles() {
+// 	visitor := func(path string, f os.FileInfo, err error) error {
+// 		if err != nil {
+// 			return err
+// 		}
+// 		if f.IsDir() {
+// 			return nil
+// 		}
+// 		if strings.HasSuffix(f.Name(), ".json") {
+// 			err = store.loadDashboardIntoCache(path)
+// 			if err != nil {
+// 				return err
+// 			}
+// 		}
+// 		return nil
+// 	}
+//
+// 	err := filepath.Walk(store.dashDir, visitor)
+// 	if err != nil {
+// 		log.Error("FileStore::updateCache failed %v", err)
+// 	}
+// }
+//
+// func (store fileStore) loadDashboardIntoCache(filename string) error {
+// 	log.Info("Loading dashboard file %v into cache", filename)
+// 	dash, err := loadDashboardFromFile(filename)
+// 	if err != nil {
+// 		return err
+// 	}
+//
+// 	store.cache[dash.Title] = dash
+//
+// 	return nil
+// }
+//
+// func (store *fileStore) Close() {
+//
+// }
+//
+// func (store *fileStore) GetById(id string) (*models.Dashboard, error) {
+// 	log.Debug("FileStore::GetById id = %v", id)
+// 	filename := store.getFilePathForDashboard(id)
+//
+// 	return loadDashboardFromFile(filename)
+// }
+//
+// func (store *fileStore) Save(dash *models.Dashboard) error {
+// 	filename := store.getFilePathForDashboard(dash.Title)
+//
+// 	log.Debug("Saving dashboard %v to %v", dash.Title, filename)
+//
+// 	var err error
+// 	var data []byte
+// 	if data, err = json.Marshal(dash.Data); err != nil {
+// 		return err
+// 	}
+//
+// 	return writeFile(filename, data)
+// }
+//
+// func (store *fileStore) Query(query string) ([]*models.SearchResult, error) {
+// 	results := make([]*models.SearchResult, 0, 50)
+//
+// 	for _, dash := range store.cache {
+// 		item := &models.SearchResult{
+// 			Id:   dash.Title,
+// 			Type: "dashboard",
+// 		}
+// 		results = append(results, item)
+// 	}
+//
+// 	return results, nil
+// }
+//
+// func loadDashboardFromFile(filename string) (*models.Dashboard, error) {
+// 	log.Debug("FileStore::loading dashboard from file %v", filename)
+//
+// 	configFile, err := os.Open(filename)
+// 	if err != nil {
+// 		return nil, err
+// 	}
+//
+// 	return models.NewFromJson(configFile)
+// }
+//
+// func (store *fileStore) getFilePathForDashboard(id string) string {
+// 	id = strings.ToLower(id)
+// 	id = strings.Replace(id, " ", "-", -1)
+// 	return filepath.Join(store.dashDir, id) + ".json"
+// }
+//
+// func dirDoesNotExist(dir string) bool {
+// 	_, err := os.Stat(dir)
+// 	return os.IsNotExist(err)
+// }
+//
+// func writeFile(filename string, data []byte) error {
+// 	f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
+// 	if err != nil {
+// 		return err
+// 	}
+// 	n, err := f.Write(data)
+// 	if err == nil && n < len(data) {
+// 		err = io.ErrShortWrite
+// 	}
+// 	if err1 := f.Close(); err == nil {
+// 		err = err1
+// 	}
+//
+// 	return err
+// }

+ 111 - 110
pkg/stores/file_store_test.go

@@ -1,112 +1,113 @@
 package stores
 
-import (
-	"fmt"
-	"io"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"testing"
-
-	. "github.com/smartystreets/goconvey/convey"
-	"github.com/torkelo/grafana-pro/pkg/models"
-)
-
-func TestFileStore(t *testing.T) {
-
-	GivenFileStore("When saving a dashboard", t, func(store *fileStore) {
-		dashboard := models.NewDashboard("hello")
-
-		err := store.Save(dashboard)
-
-		Convey("should be saved to disk", func() {
-			So(err, ShouldBeNil)
-
-			_, err = os.Stat(store.getFilePathForDashboard("hello"))
-			So(err, ShouldBeNil)
-		})
-	})
-
-	GivenFileStore("When getting a saved dashboard", t, func(store *fileStore) {
-		copyDashboardToTempData("default.json", "", store.dashDir)
-		dash, err := store.GetById("default")
-
-		Convey("should be read from disk", func() {
-			So(err, ShouldBeNil)
-			So(dash, ShouldNotBeNil)
-
-			So(dash.Title(), ShouldEqual, "Grafana Play Home")
-		})
-	})
-
-	GivenFileStore("when getting dashboard with capital letters", t, func(store *fileStore) {
-		copyDashboardToTempData("annotations.json", "", store.dashDir)
-		dash, err := store.GetById("AnnoTations")
-
-		Convey("should be read from disk", func() {
-			So(err, ShouldBeNil)
-			So(dash, ShouldNotBeNil)
-
-			So(dash.Title(), ShouldEqual, "Annotations")
-		})
-	})
-
-	GivenFileStore("When copying dashboards into data dir", t, func(store *fileStore) {
-		copyDashboardToTempData("annotations.json", "", store.dashDir)
-		copyDashboardToTempData("default.json", "", store.dashDir)
-		copyDashboardToTempData("graph-styles.json", "", store.dashDir)
-		store.scanFiles()
-
-		Convey("scan should generate index of all dashboards", func() {
-
-			result, err := store.Query("*")
-			So(err, ShouldBeNil)
-			So(len(result), ShouldEqual, 3)
-		})
-	})
-}
-
-func copyDashboardToTempData(name string, destName string, dir string) {
-	if destName == "" {
-		destName = name
-	}
-	source, _ := filepath.Abs("../../data/dashboards/" + name)
-	dest := filepath.Join(dir, destName)
-	err := copyFile(dest, source)
-	if err != nil {
-		panic(fmt.Sprintf("failed to copy file %v", name))
-	}
-}
-
-func GivenFileStore(desc string, t *testing.T, f func(store *fileStore)) {
-	Convey(desc, t, func() {
-		tempDir, _ := ioutil.TempDir("", "store")
-
-		store := NewFileStore(tempDir)
-
-		f(store)
-
-		Reset(func() {
-			os.RemoveAll(tempDir)
-		})
-	})
-}
-
-func copyFile(dst, src string) error {
-	in, err := os.Open(src)
-	if err != nil {
-		return err
-	}
-	defer in.Close()
-	out, err := os.Create(dst)
-	if err != nil {
-		return err
-	}
-	defer out.Close()
-	_, err = io.Copy(out, in)
-	cerr := out.Close()
-	if err != nil {
-		return err
-	}
-	return cerr
-}
+//
+// import (
+// 	"fmt"
+// 	"io"
+// 	"io/ioutil"
+// 	"os"
+// 	"path/filepath"
+// 	"testing"
+//
+// 	. "github.com/smartystreets/goconvey/convey"
+// 	"github.com/torkelo/grafana-pro/pkg/models"
+// )
+//
+// func TestFileStore(t *testing.T) {
+//
+// 	GivenFileStore("When saving a dashboard", t, func(store *fileStore) {
+// 		dashboard := models.NewDashboard("hello")
+//
+// 		err := store.Save(dashboard)
+//
+// 		Convey("should be saved to disk", func() {
+// 			So(err, ShouldBeNil)
+//
+// 			_, err = os.Stat(store.getFilePathForDashboard("hello"))
+// 			So(err, ShouldBeNil)
+// 		})
+// 	})
+//
+// 	GivenFileStore("When getting a saved dashboard", t, func(store *fileStore) {
+// 		copyDashboardToTempData("default.json", "", store.dashDir)
+// 		dash, err := store.GetById("default")
+//
+// 		Convey("should be read from disk", func() {
+// 			So(err, ShouldBeNil)
+// 			So(dash, ShouldNotBeNil)
+//
+// 			So(dash.Title, ShouldEqual, "Grafana Play Home")
+// 		})
+// 	})
+//
+// 	GivenFileStore("when getting dashboard with capital letters", t, func(store *fileStore) {
+// 		copyDashboardToTempData("annotations.json", "", store.dashDir)
+// 		dash, err := store.GetById("AnnoTations")
+//
+// 		Convey("should be read from disk", func() {
+// 			So(err, ShouldBeNil)
+// 			So(dash, ShouldNotBeNil)
+//
+// 			So(dash.Title, ShouldEqual, "Annotations")
+// 		})
+// 	})
+//
+// 	GivenFileStore("When copying dashboards into data dir", t, func(store *fileStore) {
+// 		copyDashboardToTempData("annotations.json", "", store.dashDir)
+// 		copyDashboardToTempData("default.json", "", store.dashDir)
+// 		copyDashboardToTempData("graph-styles.json", "", store.dashDir)
+// 		store.scanFiles()
+//
+// 		Convey("scan should generate index of all dashboards", func() {
+//
+// 			result, err := store.Query("*")
+// 			So(err, ShouldBeNil)
+// 			So(len(result), ShouldEqual, 3)
+// 		})
+// 	})
+// }
+//
+// func copyDashboardToTempData(name string, destName string, dir string) {
+// 	if destName == "" {
+// 		destName = name
+// 	}
+// 	source, _ := filepath.Abs("../../data/dashboards/" + name)
+// 	dest := filepath.Join(dir, destName)
+// 	err := copyFile(dest, source)
+// 	if err != nil {
+// 		panic(fmt.Sprintf("failed to copy file %v", name))
+// 	}
+// }
+//
+// func GivenFileStore(desc string, t *testing.T, f func(store *fileStore)) {
+// 	Convey(desc, t, func() {
+// 		tempDir, _ := ioutil.TempDir("", "store")
+//
+// 		store := NewFileStore(tempDir)
+//
+// 		f(store)
+//
+// 		Reset(func() {
+// 			os.RemoveAll(tempDir)
+// 		})
+// 	})
+// }
+//
+// func copyFile(dst, src string) error {
+// 	in, err := os.Open(src)
+// 	if err != nil {
+// 		return err
+// 	}
+// 	defer in.Close()
+// 	out, err := os.Create(dst)
+// 	if err != nil {
+// 		return err
+// 	}
+// 	defer out.Close()
+// 	_, err = io.Copy(out, in)
+// 	cerr := out.Close()
+// 	if err != nil {
+// 		return err
+// 	}
+// 	return cerr
+// }

+ 94 - 0
pkg/stores/rethinkdb.go

@@ -0,0 +1,94 @@
+package stores
+
+import (
+	"time"
+
+	log "github.com/alecthomas/log4go"
+	r "github.com/dancannon/gorethink"
+	"github.com/torkelo/grafana-pro/pkg/models"
+)
+
+type rethinkStore struct {
+	session *r.Session
+}
+
+type RethinkCfg struct {
+	DatabaseName string
+}
+
+func NewRethinkStore(config *RethinkCfg) *rethinkStore {
+	log.Info("Initializing rethink storage")
+
+	session, err := r.Connect(r.ConnectOpts{
+		Address:     "localhost:28015",
+		Database:    config.DatabaseName,
+		MaxIdle:     10,
+		IdleTimeout: time.Second * 10,
+	})
+
+	if err != nil {
+		log.Crash("Failed to connect to rethink database %v", err)
+	}
+
+	r.DbCreate(config.DatabaseName).Exec(session)
+	r.Db(config.DatabaseName).TableCreate("dashboards").Exec(session)
+	r.Db(config.DatabaseName).Table("dashboards").IndexCreateFunc("AccountIdTitle", func(row r.Term) interface{} {
+		return []interface{}{row.Field("AccountId"), row.Field("Title")}
+	}).Exec(session)
+
+	return &rethinkStore{
+		session: session,
+	}
+}
+
+func (self *rethinkStore) SaveDashboard(dash *models.Dashboard) error {
+	resp, err := r.Table("dashboards").Insert(dash).RunWrite(self.session)
+	if err != nil {
+		return err
+	}
+
+	log.Info("Inserted: %v, Errors: %v, Updated: %v", resp.Inserted, resp.Errors, resp.Updated)
+	log.Info("First error:", resp.FirstError)
+	if len(resp.GeneratedKeys) > 0 {
+		dash.Id = resp.GeneratedKeys[0]
+	}
+
+	return nil
+}
+
+func (self *rethinkStore) GetDashboardByTitle(title string, accountId string) (*models.Dashboard, error) {
+	resp, err := r.Table("dashboards").GetAllByIndex("AccountIdTitle", []interface{}{accountId, title}).Run(self.session)
+	if err != nil {
+		return nil, err
+	}
+
+	var dashboard models.Dashboard
+	err = resp.One(&dashboard)
+	if err != nil {
+		return nil, err
+	}
+
+	return &dashboard, nil
+}
+
+func (self *rethinkStore) Query(query string) ([]*models.SearchResult, error) {
+
+	docs, err := r.Table("dashboards").Filter(r.Row.Field("Title").Match(".*")).Run(self.session)
+	if err != nil {
+		return nil, err
+	}
+
+	results := make([]*models.SearchResult, 0, 50)
+	var dashboard models.Dashboard
+	for docs.Next(&dashboard) {
+		log.Info("title: ", dashboard.Title)
+		results = append(results, &models.SearchResult{
+			Title: dashboard.Title,
+			Id:    dashboard.Id,
+		})
+	}
+
+	return results, nil
+}
+
+func (self *rethinkStore) Close() {}

+ 29 - 0
pkg/stores/rethinkdb_test.go

@@ -0,0 +1,29 @@
+package stores
+
+import (
+	"testing"
+
+	. "github.com/smartystreets/goconvey/convey"
+	"github.com/torkelo/grafana-pro/pkg/models"
+)
+
+func TestRethinkStore(t *testing.T) {
+
+	Convey("Insert dashboard", t, func() {
+		store := NewRethinkStore(&RethinkCfg{DatabaseName: "tests"})
+		//defer r.DbDrop("tests").Exec(store.session)
+
+		dashboard := models.NewDashboard("test")
+		dashboard.AccountId = "123"
+
+		err := store.SaveDashboard(dashboard)
+		So(err, ShouldBeNil)
+		So(dashboard.Id, ShouldNotBeEmpty)
+
+		read, err := store.GetDashboardByTitle("test", "123")
+		So(err, ShouldBeNil)
+		So(read, ShouldNotBeNil)
+
+	})
+
+}

+ 3 - 3
pkg/stores/store.go

@@ -5,12 +5,12 @@ import (
 )
 
 type Store interface {
-	GetById(id string) (*models.Dashboard, error)
-	Save(dash *models.Dashboard) error
+	GetDashboardByTitle(id string, accountId string) (*models.Dashboard, error)
+	SaveDashboard(dash *models.Dashboard) error
 	Query(query string) ([]*models.SearchResult, error)
 	Close()
 }
 
 func New() Store {
-	return NewFileStore("data")
+	return NewRethinkStore(&RethinkCfg{DatabaseName: "grafana"})
 }

+ 7 - 0
start_dependencies.sh

@@ -0,0 +1,7 @@
+docker kill gfdev
+docker rm gfdev
+
+docker run -d -p 8180:8080 -p 28015:28015 -p 29015:29015 \
+  --name rethinkdb \
+  -v /var/docker/grafana-pro-rethinkdb:/data \
+  dockerfile/rethinkdb