Sfoglia il codice sorgente

Work on data source proxying, #6

Torkel Ödegaard 11 anni fa
parent
commit
a2a0e0394d

+ 4 - 1
pkg/api/api.go

@@ -25,13 +25,16 @@ func Register(m *macaron.Macaron) {
 	m.Post("/api/account/using/:id", auth, SetUsingAccount)
 	m.Post("/api/account/using/:id", auth, SetUsingAccount)
 	m.Get("/api/account/others", auth, GetOtherAccounts)
 	m.Get("/api/account/others", auth, GetOtherAccounts)
 
 
-	// datasources
+	// data sources
 	m.Get("/admin/datasources/", auth, Index)
 	m.Get("/admin/datasources/", auth, Index)
 	m.Get("/api/admin/datasources/list", auth, GetDataSources)
 	m.Get("/api/admin/datasources/list", auth, GetDataSources)
 	m.Put("/api/admin/datasources", auth, AddDataSource)
 	m.Put("/api/admin/datasources", auth, AddDataSource)
 	m.Post("/api/admin/datasources", auth, UpdateDataSource)
 	m.Post("/api/admin/datasources", auth, UpdateDataSource)
 	m.Delete("/api/admin/datasources/:id", auth, DeleteDataSource)
 	m.Delete("/api/admin/datasources/:id", auth, DeleteDataSource)
 
 
+	// data source proxy
+	m.Any("/api/datasources/proxy/:name/*", auth, ProxyDataSourceRequest)
+
 	// user register
 	// user register
 	m.Get("/register/*_", Index)
 	m.Get("/register/*_", Index)
 	m.Post("/api/account", CreateAccount)
 	m.Post("/api/account", CreateAccount)

+ 5 - 1
pkg/api/api_config.go

@@ -25,9 +25,13 @@ func renderConfig(data *configJsTmplModel) string {
 	datasources := make(map[string]interface{})
 	datasources := make(map[string]interface{})
 
 
 	for _, ds := range data.DataSources {
 	for _, ds := range data.DataSources {
+		url := ds.Url
+		if ds.Access == m.DS_ACCESS_PROXY {
+			url = "/api/datasources/proxy/" + ds.Name
+		}
 		datasources[ds.Name] = map[string]interface{}{
 		datasources[ds.Name] = map[string]interface{}{
 			"type": ds.Type,
 			"type": ds.Type,
-			"url":  ds.Url,
+			"url":  url,
 		}
 		}
 	}
 	}
 
 

+ 63 - 0
pkg/api/api_dataproxy.go

@@ -0,0 +1,63 @@
+package api
+
+import (
+	"net/http"
+	"net/http/httputil"
+	"net/url"
+	"strings"
+
+	"github.com/torkelo/grafana-pro/pkg/bus"
+	"github.com/torkelo/grafana-pro/pkg/log"
+	"github.com/torkelo/grafana-pro/pkg/middleware"
+	m "github.com/torkelo/grafana-pro/pkg/models"
+)
+
+func singleJoiningSlash(a, b string) string {
+	aslash := strings.HasSuffix(a, "/")
+	bslash := strings.HasPrefix(b, "/")
+	switch {
+	case aslash && bslash:
+		return a + b[1:]
+	case !aslash && !bslash:
+		return a + "/" + b
+	}
+	return a + b
+}
+
+func NewReverseProxy(target *url.URL, proxyPath string) *httputil.ReverseProxy {
+	targetQuery := target.RawQuery
+
+	director := func(req *http.Request) {
+		req.URL.Scheme = target.Scheme
+		req.URL.Host = target.Host
+		req.URL.Path = singleJoiningSlash(target.Path, proxyPath)
+		if targetQuery == "" || req.URL.RawQuery == "" {
+			req.URL.RawQuery = targetQuery + req.URL.RawQuery
+		} else {
+			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
+		}
+
+		log.Info("Proxy: %v", req.URL.Path)
+	}
+	return &httputil.ReverseProxy{Director: director}
+}
+
+// TODO: need to cache datasources
+func ProxyDataSourceRequest(c *middleware.Context) {
+	name := c.Params(":name")
+
+	query := m.GetDataSourceByNameQuery{
+		Name:      name,
+		AccountId: c.GetAccountId(),
+	}
+
+	err := bus.Dispatch(&query)
+	if err != nil {
+		c.JsonApiErr(500, "Unable to load datasource meta data", err)
+	}
+
+	proxyPath := c.Params("*")
+	url, _ := url.Parse(query.Result.Url)
+	proxy := NewReverseProxy(url, proxyPath)
+	proxy.ServeHTTP(c.RW(), c.Req.Request)
+}

+ 15 - 1
pkg/models/datasource.go

@@ -1,6 +1,9 @@
 package models
 package models
 
 
-import "time"
+import (
+	"errors"
+	"time"
+)
 
 
 const (
 const (
 	DS_GRAPHITE      = "graphite"
 	DS_GRAPHITE      = "graphite"
@@ -10,6 +13,11 @@ const (
 	DS_ACCESS_PROXY  = "proxy"
 	DS_ACCESS_PROXY  = "proxy"
 )
 )
 
 
+// Typed errors
+var (
+	ErrDataSourceNotFound = errors.New("Data source not found")
+)
+
 type DsType string
 type DsType string
 type DsAccess string
 type DsAccess string
 
 
@@ -34,6 +42,12 @@ type GetDataSourcesQuery struct {
 	Result    []*DataSource
 	Result    []*DataSource
 }
 }
 
 
+type GetDataSourceByNameQuery struct {
+	Name      string
+	AccountId int64
+	Result    DataSource
+}
+
 type AddDataSourceCommand struct {
 type AddDataSourceCommand struct {
 	AccountId int64
 	AccountId int64
 	Name      string
 	Name      string

+ 11 - 0
pkg/stores/sqlstore/sqlstore_datasource.go

@@ -14,6 +14,17 @@ func init() {
 	bus.AddHandler("sql", AddDataSource)
 	bus.AddHandler("sql", AddDataSource)
 	bus.AddHandler("sql", DeleteDataSource)
 	bus.AddHandler("sql", DeleteDataSource)
 	bus.AddHandler("sql", UpdateDataSource)
 	bus.AddHandler("sql", UpdateDataSource)
+	bus.AddHandler("sql", GetDataSourceByName)
+}
+
+func GetDataSourceByName(query *m.GetDataSourceByNameQuery) error {
+	sess := x.Limit(100, 0).Where("account_id=? AND name=?", query.AccountId, query.Name)
+	has, err := sess.Get(&query.Result)
+
+	if !has {
+		return m.ErrDataSourceNotFound
+	}
+	return err
 }
 }
 
 
 func GetDataSources(query *m.GetDataSourcesQuery) error {
 func GetDataSources(query *m.GetDataSourcesQuery) error {