Add roles endpoint and switching between OSS and enterprise

pull/101/head
Chris Goller 2017-02-23 21:54:20 -06:00
parent 5aa6a4ef2b
commit a4a5b53bf5
9 changed files with 183 additions and 103 deletions

View File

@ -47,8 +47,24 @@ type Client struct {
}
// NewClientWithTimeSeries initializes a Client with a known set of TimeSeries.
func NewClientWithTimeSeries(lg chronograf.Logger, series ...chronograf.TimeSeries) *Client {
c := &Client{}
func NewClientWithTimeSeries(lg chronograf.Logger, mu, username, password string, tls bool, series ...chronograf.TimeSeries) (*Client, error) {
metaURL, err := parseMetaURL(mu, tls)
if err != nil {
return nil, err
}
metaURL.User = url.UserPassword(username, password)
ctrl := NewMetaClient(metaURL)
c := &Client{
Ctrl: ctrl,
UsersStore: &UserStore{
Ctrl: ctrl,
Logger: lg,
},
RolesStore: &RolesStore{
Ctrl: ctrl,
Logger: lg,
},
}
c.dataNodes = ring.New(len(series))
@ -57,7 +73,7 @@ func NewClientWithTimeSeries(lg chronograf.Logger, series ...chronograf.TimeSeri
c.dataNodes = c.dataNodes.Next()
}
return c
return c, nil
}
// NewClientWithURL initializes an Enterprise client with a URL to a Meta Node.

View File

@ -75,9 +75,12 @@ func Test_Enterprise_IssuesQueries(t *testing.T) {
func Test_Enterprise_AdvancesDataNodes(t *testing.T) {
m1 := NewMockTimeSeries("http://host-1.example.com:8086")
m2 := NewMockTimeSeries("http://host-2.example.com:8086")
cl := enterprise.NewClientWithTimeSeries(log.New(log.DebugLevel), chronograf.TimeSeries(m1), chronograf.TimeSeries(m2))
cl, err := enterprise.NewClientWithTimeSeries(log.New(log.DebugLevel), "http://meta.example.com:8091", "marty", "thelake", false, chronograf.TimeSeries(m1), chronograf.TimeSeries(m2))
if err != nil {
t.Error("Unexpected error while initializing client: err:", err)
}
err := cl.Connect(context.Background(), &chronograf.Source{})
err = cl.Connect(context.Background(), &chronograf.Source{})
if err != nil {
t.Error("Unexpected error while initializing client: err:", err)
}
@ -132,8 +135,11 @@ func Test_Enterprise_NewClientWithURL(t *testing.T) {
func Test_Enterprise_ComplainsIfNotOpened(t *testing.T) {
m1 := NewMockTimeSeries("http://host-1.example.com:8086")
cl := enterprise.NewClientWithTimeSeries(log.New(log.DebugLevel), chronograf.TimeSeries(m1))
_, err := cl.Query(context.Background(), chronograf.Query{Command: "show shards", DB: "_internal", RP: "autogen"})
cl, err := enterprise.NewClientWithTimeSeries(log.New(log.DebugLevel), "http://meta.example.com:8091", "docbrown", "1.21 gigawatts", false, chronograf.TimeSeries(m1))
if err != nil {
t.Error("Expected ErrUnitialized, but was this err:", err)
}
_, err = cl.Query(context.Background(), chronograf.Query{Command: "show shards", DB: "_internal", RP: "autogen"})
if err != chronograf.ErrUninitialized {
t.Error("Expected ErrUnitialized, but was this err:", err)
}

View File

@ -22,6 +22,11 @@ type TimeSeries struct {
RolesF func(context.Context) (chronograf.RolesStore, error)
}
// New implements TimeSeriesClient
func (t *TimeSeries) New(chronograf.Source, chronograf.Logger) (chronograf.TimeSeries, error) {
return t, nil
}
// Query retrieves time series data from the database.
func (t *TimeSeries) Query(ctx context.Context, query chronograf.Query) (chronograf.Response, error) {
return t.QueryF(ctx, query)

View File

@ -223,40 +223,47 @@ func (h *Service) UpdateSourceUser(w http.ResponseWriter, r *http.Request) {
encodeJSON(w, http.StatusOK, su, h.Logger)
}
func (h *Service) sourcesSeries(ctx context.Context, w http.ResponseWriter, r *http.Request) (int, error) {
func (h *Service) sourcesSeries(ctx context.Context, w http.ResponseWriter, r *http.Request) (int, chronograf.TimeSeries, error) {
srcID, err := paramID("id", r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger)
return 0, err
return 0, nil, err
}
src, err := h.SourcesStore.Get(ctx, srcID)
if err != nil {
notFound(w, srcID, h.Logger)
return 0, err
return 0, nil, err
}
if err = h.TimeSeries.Connect(ctx, &src); err != nil {
ts, err := h.TimeSeries(src)
if err != nil {
msg := fmt.Sprintf("Unable to connect to source %d", srcID)
Error(w, http.StatusBadRequest, msg, h.Logger)
return 0, err
return 0, nil, err
}
return srcID, nil
if err = ts.Connect(ctx, &src); err != nil {
msg := fmt.Sprintf("Unable to connect to source %d", srcID)
Error(w, http.StatusBadRequest, msg, h.Logger)
return 0, nil, err
}
return srcID, ts, nil
}
func (h *Service) sourceUsersStore(ctx context.Context, w http.ResponseWriter, r *http.Request) (int, chronograf.UsersStore, error) {
srcID, err := h.sourcesSeries(ctx, w, r)
srcID, ts, err := h.sourcesSeries(ctx, w, r)
if err != nil {
return 0, nil, err
}
store := h.TimeSeries.Users(ctx)
store := ts.Users(ctx)
return srcID, store, nil
}
// hasRoles checks if the influx source has roles or not
func (h *Service) hasRoles(ctx context.Context) (chronograf.RolesStore, bool) {
store, err := h.TimeSeries.Roles(ctx)
func (h *Service) hasRoles(ctx context.Context, ts chronograf.TimeSeries) (chronograf.RolesStore, bool) {
store, err := ts.Roles(ctx)
if err != nil {
return nil, false
}
@ -278,13 +285,20 @@ func (h *Service) Permissions(w http.ResponseWriter, r *http.Request) {
return
}
if err = h.TimeSeries.Connect(ctx, &src); err != nil {
ts, err := h.TimeSeries(src)
if err != nil {
msg := fmt.Sprintf("Unable to connect to source %d", srcID)
Error(w, http.StatusBadRequest, msg, h.Logger)
return
}
perms := h.TimeSeries.Allowances(ctx)
if err = ts.Connect(ctx, &src); err != nil {
msg := fmt.Sprintf("Unable to connect to source %d", srcID)
Error(w, http.StatusBadRequest, msg, h.Logger)
return
}
perms := ts.Allowances(ctx)
if err != nil {
Error(w, http.StatusBadRequest, err.Error(), h.Logger)
return
@ -373,12 +387,12 @@ func (h *Service) NewRole(w http.ResponseWriter, r *http.Request) {
}
ctx := r.Context()
srcID, err := h.sourcesSeries(ctx, w, r)
srcID, ts, err := h.sourcesSeries(ctx, w, r)
if err != nil {
return
}
roles, ok := h.hasRoles(ctx)
roles, ok := h.hasRoles(ctx, ts)
if !ok {
Error(w, http.StatusNotFound, fmt.Sprintf("Source %d does not have role capability", srcID), h.Logger)
return
@ -408,12 +422,12 @@ func (h *Service) UpdateRole(w http.ResponseWriter, r *http.Request) {
}
ctx := r.Context()
srcID, err := h.sourcesSeries(ctx, w, r)
srcID, ts, err := h.sourcesSeries(ctx, w, r)
if err != nil {
return
}
roles, ok := h.hasRoles(ctx)
roles, ok := h.hasRoles(ctx, ts)
if !ok {
Error(w, http.StatusNotFound, fmt.Sprintf("Source %d does not have role capability", srcID), h.Logger)
return
@ -440,12 +454,12 @@ func (h *Service) UpdateRole(w http.ResponseWriter, r *http.Request) {
// RoleID retrieves a role with ID from store.
func (h *Service) RoleID(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
srcID, err := h.sourcesSeries(ctx, w, r)
srcID, ts, err := h.sourcesSeries(ctx, w, r)
if err != nil {
return
}
roles, ok := h.hasRoles(ctx)
roles, ok := h.hasRoles(ctx, ts)
if !ok {
Error(w, http.StatusNotFound, fmt.Sprintf("Source %d does not have role capability", srcID), h.Logger)
return
@ -464,12 +478,12 @@ func (h *Service) RoleID(w http.ResponseWriter, r *http.Request) {
// Roles retrieves all roles from the store
func (h *Service) Roles(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
srcID, err := h.sourcesSeries(ctx, w, r)
srcID, ts, err := h.sourcesSeries(ctx, w, r)
if err != nil {
return
}
store, ok := h.hasRoles(ctx)
store, ok := h.hasRoles(ctx, ts)
if !ok {
Error(w, http.StatusNotFound, fmt.Sprintf("Source %d does not have role capability", srcID), h.Logger)
return
@ -495,12 +509,12 @@ func (h *Service) Roles(w http.ResponseWriter, r *http.Request) {
// RemoveRole removes role from data source.
func (h *Service) RemoveRole(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
srcID, err := h.sourcesSeries(ctx, w, r)
srcID, ts, err := h.sourcesSeries(ctx, w, r)
if err != nil {
return
}
roles, ok := h.hasRoles(ctx)
roles, ok := h.hasRoles(ctx, ts)
if !ok {
Error(w, http.StatusNotFound, fmt.Sprintf("Source %d does not have role capability", srcID), h.Logger)
return

View File

@ -19,7 +19,7 @@ import (
func TestService_NewSourceUser(t *testing.T) {
type fields struct {
SourcesStore chronograf.SourcesStore
TimeSeries chronograf.TimeSeries
TimeSeries server.TimeSeriesClient
Logger chronograf.Logger
UseAuth bool
}
@ -249,10 +249,10 @@ func TestService_NewSourceUser(t *testing.T) {
}))
h := &server.Service{
SourcesStore: tt.fields.SourcesStore,
TimeSeries: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
UseAuth: tt.fields.UseAuth,
SourcesStore: tt.fields.SourcesStore,
TimeSeriesClient: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
UseAuth: tt.fields.UseAuth,
}
h.NewSourceUser(tt.args.w, tt.args.r)
@ -276,7 +276,7 @@ func TestService_NewSourceUser(t *testing.T) {
func TestService_SourceUsers(t *testing.T) {
type fields struct {
SourcesStore chronograf.SourcesStore
TimeSeries chronograf.TimeSeries
TimeSeries server.TimeSeriesClient
Logger chronograf.Logger
UseAuth bool
}
@ -357,10 +357,10 @@ func TestService_SourceUsers(t *testing.T) {
},
}))
h := &server.Service{
SourcesStore: tt.fields.SourcesStore,
TimeSeries: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
UseAuth: tt.fields.UseAuth,
SourcesStore: tt.fields.SourcesStore,
TimeSeriesClient: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
UseAuth: tt.fields.UseAuth,
}
h.SourceUsers(tt.args.w, tt.args.r)
@ -383,7 +383,7 @@ func TestService_SourceUsers(t *testing.T) {
func TestService_SourceUserID(t *testing.T) {
type fields struct {
SourcesStore chronograf.SourcesStore
TimeSeries chronograf.TimeSeries
TimeSeries server.TimeSeriesClient
Logger chronograf.Logger
UseAuth bool
}
@ -464,10 +464,10 @@ func TestService_SourceUserID(t *testing.T) {
},
}))
h := &server.Service{
SourcesStore: tt.fields.SourcesStore,
TimeSeries: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
UseAuth: tt.fields.UseAuth,
SourcesStore: tt.fields.SourcesStore,
TimeSeriesClient: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
UseAuth: tt.fields.UseAuth,
}
h.SourceUserID(tt.args.w, tt.args.r)
@ -490,7 +490,7 @@ func TestService_SourceUserID(t *testing.T) {
func TestService_RemoveSourceUser(t *testing.T) {
type fields struct {
SourcesStore chronograf.SourcesStore
TimeSeries chronograf.TimeSeries
TimeSeries server.TimeSeriesClient
Logger chronograf.Logger
UseAuth bool
}
@ -559,10 +559,10 @@ func TestService_RemoveSourceUser(t *testing.T) {
},
}))
h := &server.Service{
SourcesStore: tt.fields.SourcesStore,
TimeSeries: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
UseAuth: tt.fields.UseAuth,
SourcesStore: tt.fields.SourcesStore,
TimeSeriesClient: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
UseAuth: tt.fields.UseAuth,
}
h.RemoveSourceUser(tt.args.w, tt.args.r)
resp := tt.args.w.Result()
@ -584,7 +584,7 @@ func TestService_RemoveSourceUser(t *testing.T) {
func TestService_UpdateSourceUser(t *testing.T) {
type fields struct {
SourcesStore chronograf.SourcesStore
TimeSeries chronograf.TimeSeries
TimeSeries server.TimeSeriesClient
Logger chronograf.Logger
UseAuth bool
}
@ -681,10 +681,10 @@ func TestService_UpdateSourceUser(t *testing.T) {
},
}))
h := &server.Service{
SourcesStore: tt.fields.SourcesStore,
TimeSeries: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
UseAuth: tt.fields.UseAuth,
SourcesStore: tt.fields.SourcesStore,
TimeSeriesClient: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
UseAuth: tt.fields.UseAuth,
}
h.UpdateSourceUser(tt.args.w, tt.args.r)
resp := tt.args.w.Result()
@ -706,7 +706,7 @@ func TestService_UpdateSourceUser(t *testing.T) {
func TestService_Permissions(t *testing.T) {
type fields struct {
SourcesStore chronograf.SourcesStore
TimeSeries chronograf.TimeSeries
TimeSeries server.TimeSeriesClient
Logger chronograf.Logger
UseAuth bool
}
@ -773,10 +773,10 @@ func TestService_Permissions(t *testing.T) {
},
}))
h := &server.Service{
SourcesStore: tt.fields.SourcesStore,
TimeSeries: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
UseAuth: tt.fields.UseAuth,
SourcesStore: tt.fields.SourcesStore,
TimeSeriesClient: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
UseAuth: tt.fields.UseAuth,
}
h.Permissions(tt.args.w, tt.args.r)
resp := tt.args.w.Result()
@ -798,7 +798,7 @@ func TestService_Permissions(t *testing.T) {
func TestService_NewSourceRole(t *testing.T) {
type fields struct {
SourcesStore chronograf.SourcesStore
TimeSeries chronograf.TimeSeries
TimeSeries server.TimeSeriesClient
Logger chronograf.Logger
}
type args struct {
@ -990,9 +990,9 @@ func TestService_NewSourceRole(t *testing.T) {
}
for _, tt := range tests {
h := &server.Service{
SourcesStore: tt.fields.SourcesStore,
TimeSeries: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
SourcesStore: tt.fields.SourcesStore,
TimeSeriesClient: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
}
tt.args.r = tt.args.r.WithContext(httprouter.WithParams(
context.Background(),
@ -1024,7 +1024,7 @@ func TestService_NewSourceRole(t *testing.T) {
func TestService_UpdateRole(t *testing.T) {
type fields struct {
SourcesStore chronograf.SourcesStore
TimeSeries chronograf.TimeSeries
TimeSeries server.TimeSeriesClient
Logger chronograf.Logger
}
type args struct {
@ -1103,9 +1103,9 @@ func TestService_UpdateRole(t *testing.T) {
}
for _, tt := range tests {
h := &server.Service{
SourcesStore: tt.fields.SourcesStore,
TimeSeries: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
SourcesStore: tt.fields.SourcesStore,
TimeSeriesClient: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
}
tt.args.r = tt.args.r.WithContext(httprouter.WithParams(
@ -1142,7 +1142,7 @@ func TestService_UpdateRole(t *testing.T) {
func TestService_RoleID(t *testing.T) {
type fields struct {
SourcesStore chronograf.SourcesStore
TimeSeries chronograf.TimeSeries
TimeSeries server.TimeSeriesClient
Logger chronograf.Logger
}
type args struct {
@ -1226,9 +1226,9 @@ func TestService_RoleID(t *testing.T) {
}
for _, tt := range tests {
h := &server.Service{
SourcesStore: tt.fields.SourcesStore,
TimeSeries: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
SourcesStore: tt.fields.SourcesStore,
TimeSeriesClient: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
}
tt.args.r = tt.args.r.WithContext(httprouter.WithParams(
@ -1265,7 +1265,7 @@ func TestService_RoleID(t *testing.T) {
func TestService_RemoveRole(t *testing.T) {
type fields struct {
SourcesStore chronograf.SourcesStore
TimeSeries chronograf.TimeSeries
TimeSeries server.TimeSeriesClient
Logger chronograf.Logger
}
type args struct {
@ -1322,9 +1322,9 @@ func TestService_RemoveRole(t *testing.T) {
}
for _, tt := range tests {
h := &server.Service{
SourcesStore: tt.fields.SourcesStore,
TimeSeries: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
SourcesStore: tt.fields.SourcesStore,
TimeSeriesClient: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
}
tt.args.r = tt.args.r.WithContext(httprouter.WithParams(
@ -1352,7 +1352,7 @@ func TestService_RemoveRole(t *testing.T) {
func TestService_Roles(t *testing.T) {
type fields struct {
SourcesStore chronograf.SourcesStore
TimeSeries chronograf.TimeSeries
TimeSeries server.TimeSeriesClient
Logger chronograf.Logger
}
type args struct {
@ -1434,9 +1434,9 @@ func TestService_Roles(t *testing.T) {
}
for _, tt := range tests {
h := &server.Service{
SourcesStore: tt.fields.SourcesStore,
TimeSeries: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
SourcesStore: tt.fields.SourcesStore,
TimeSeriesClient: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
}
tt.args.r = tt.args.r.WithContext(httprouter.WithParams(

View File

@ -45,13 +45,20 @@ func (h *Service) Influx(w http.ResponseWriter, r *http.Request) {
return
}
if err = h.TimeSeries.Connect(ctx, &src); err != nil {
ts, err := h.TimeSeries(src)
if err != nil {
msg := fmt.Sprintf("Unable to connect to source %d", id)
Error(w, http.StatusBadRequest, msg, h.Logger)
return
}
response, err := h.TimeSeries.Query(ctx, req)
if err = ts.Connect(ctx, &src); err != nil {
msg := fmt.Sprintf("Unable to connect to source %d", id)
Error(w, http.StatusBadRequest, msg, h.Logger)
return
}
response, err := ts.Query(ctx, req)
if err != nil {
if err == chronograf.ErrUpstreamTimeout {
msg := "Timeout waiting for Influx response"

View File

@ -80,6 +80,14 @@ func NewMux(opts MuxOpts, service Service) http.Handler {
router.DELETE("/chronograf/v1/sources/:id/users/:uid", service.RemoveSourceUser)
router.PATCH("/chronograf/v1/sources/:id/users/:uid", service.UpdateSourceUser)
// Roles associated with the data source
router.GET("/chronograf/v1/sources/:id/roles", service.Roles)
router.POST("/chronograf/v1/sources/:id/roles", service.NewRole)
router.GET("/chronograf/v1/sources/:id/roles/:rid", service.RoleID)
router.DELETE("/chronograf/v1/sources/:id/roles/:rid", service.RemoveRole)
router.PATCH("/chronograf/v1/sources/:id/roles/:rid", service.UpdateRole)
// Kapacitor
router.GET("/chronograf/v1/sources/:id/kapacitors", service.Kapacitors)
router.POST("/chronograf/v1/sources/:id/kapacitors", service.NewKapacitor)

View File

@ -12,7 +12,6 @@ import (
"github.com/influxdata/chronograf"
"github.com/influxdata/chronograf/bolt"
"github.com/influxdata/chronograf/canned"
"github.com/influxdata/chronograf/influx"
"github.com/influxdata/chronograf/layouts"
clog "github.com/influxdata/chronograf/log"
"github.com/influxdata/chronograf/uuid"
@ -190,17 +189,15 @@ func openService(boltPath, cannedPath string, logger chronograf.Logger, useAuth
}
return Service{
SourcesStore: db.SourcesStore,
ServersStore: db.ServersStore,
UsersStore: db.UsersStore,
TimeSeries: &influx.Client{
Logger: logger,
},
LayoutStore: layouts,
DashboardsStore: db.DashboardsStore,
AlertRulesStore: db.AlertsStore,
Logger: logger,
UseAuth: useAuth,
TimeSeriesClient: &InfluxClient{},
SourcesStore: db.SourcesStore,
ServersStore: db.ServersStore,
UsersStore: db.UsersStore,
LayoutStore: layouts,
DashboardsStore: db.DashboardsStore,
AlertRulesStore: db.AlertsStore,
Logger: logger,
UseAuth: useAuth,
}
}

View File

@ -1,18 +1,27 @@
package server
import "github.com/influxdata/chronograf"
import (
"github.com/influxdata/chronograf"
"github.com/influxdata/chronograf/enterprise"
"github.com/influxdata/chronograf/influx"
)
// Service handles REST calls to the persistence
type Service struct {
SourcesStore chronograf.SourcesStore
ServersStore chronograf.ServersStore
LayoutStore chronograf.LayoutStore
AlertRulesStore chronograf.AlertRulesStore
UsersStore chronograf.UsersStore
DashboardsStore chronograf.DashboardsStore
TimeSeries chronograf.TimeSeries
Logger chronograf.Logger
UseAuth bool
SourcesStore chronograf.SourcesStore
ServersStore chronograf.ServersStore
LayoutStore chronograf.LayoutStore
AlertRulesStore chronograf.AlertRulesStore
UsersStore chronograf.UsersStore
DashboardsStore chronograf.DashboardsStore
TimeSeriesClient TimeSeriesClient
Logger chronograf.Logger
UseAuth bool
}
// TimeSeriesClient returns the correct client for a time series database.
type TimeSeriesClient interface {
New(chronograf.Source, chronograf.Logger) (chronograf.TimeSeries, error)
}
// ErrorMessage is the error response format for all service errors
@ -20,3 +29,21 @@ type ErrorMessage struct {
Code int `json:"code"`
Message string `json:"message"`
}
// TimeSeries returns a new client connected to a time series database
func (s *Service) TimeSeries(src chronograf.Source) (chronograf.TimeSeries, error) {
return s.TimeSeriesClient.New(src, s.Logger)
}
// InfluxClient returns a new client to connect to OSS or Enterprise
type InfluxClient struct{}
// New creates a client to connect to OSS or enterprise
func (c *InfluxClient) New(src chronograf.Source, logger chronograf.Logger) (chronograf.TimeSeries, error) {
if src.Type == "influx-enterprise" {
return enterprise.NewClientWithURL(src.URL, src.Username, src.Password, false, logger)
}
return &influx.Client{
Logger: logger,
}, nil
}