Fix mux paths to be hardcoded; clarify server start; fix golint
parent
d1359c09b3
commit
d6a067427b
|
@ -8,9 +8,13 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
Dir = "../ui/build"
|
||||
Default = "../ui/build/index.html"
|
||||
DebugDir = "ui/build"
|
||||
// Dir is prefix of the assets in the bindata
|
||||
Dir = "../ui/build"
|
||||
// Default is the default item to load if 404
|
||||
Default = "../ui/build/index.html"
|
||||
// DebugDir is the prefix of the assets in development mode
|
||||
DebugDir = "ui/build"
|
||||
// DebugDefault is the default item to load if 404
|
||||
DebugDefault = "ui/build/index.html"
|
||||
)
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ type exploration struct {
|
|||
|
||||
func newExploration(e *chronograf.Exploration) exploration {
|
||||
rel := "self"
|
||||
href := fmt.Sprintf("%s/%d/explorations/%d", httpAPIUsrs, e.UserID, e.ID)
|
||||
href := fmt.Sprintf("%s/%d/explorations/%d", "/chronograf/v1/users", e.UserID, e.ID)
|
||||
return exploration{
|
||||
Name: e.Name,
|
||||
Data: e.Data,
|
||||
|
@ -41,7 +41,8 @@ type explorations struct {
|
|||
Explorations []exploration `json:"explorations"`
|
||||
}
|
||||
|
||||
func (h *Store) Explorations(w http.ResponseWriter, r *http.Request) {
|
||||
// Explorations returns all explorations scoped by user id.
|
||||
func (h *Service) Explorations(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := paramID("id", r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, err.Error())
|
||||
|
@ -66,7 +67,8 @@ func (h *Store) Explorations(w http.ResponseWriter, r *http.Request) {
|
|||
encodeJSON(w, http.StatusOK, res, h.Logger)
|
||||
}
|
||||
|
||||
func (h *Store) ExplorationsID(w http.ResponseWriter, r *http.Request) {
|
||||
// ExplorationsID retrieves exploration ID scoped under user.
|
||||
func (h *Service) ExplorationsID(w http.ResponseWriter, r *http.Request) {
|
||||
eID, err := paramID("eid", r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, err.Error())
|
||||
|
@ -95,7 +97,8 @@ type patchExplorationRequest struct {
|
|||
Name *string `json:"name,omitempty"` // Exploration name given by user.
|
||||
}
|
||||
|
||||
func (h *Store) UpdateExploration(w http.ResponseWriter, r *http.Request) {
|
||||
// UpdateExploration incrementally updates exploration
|
||||
func (h *Service) UpdateExploration(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := paramID("eid", r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, err.Error())
|
||||
|
@ -149,7 +152,8 @@ type postExplorationRequest struct {
|
|||
Name string `json:"name,omitempty"` // Exploration name given by user.
|
||||
}
|
||||
|
||||
func (h *Store) NewExploration(w http.ResponseWriter, r *http.Request) {
|
||||
// NewExploration adds valid exploration scoped by user id.
|
||||
func (h *Service) NewExploration(w http.ResponseWriter, r *http.Request) {
|
||||
uID, err := paramID("id", r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, err.Error())
|
||||
|
@ -186,7 +190,8 @@ func (h *Store) NewExploration(w http.ResponseWriter, r *http.Request) {
|
|||
encodeJSON(w, http.StatusCreated, res, h.Logger)
|
||||
}
|
||||
|
||||
func (h *Store) RemoveExploration(w http.ResponseWriter, r *http.Request) {
|
||||
// RemoveExploration deletes exploration from store.
|
||||
func (h *Service) RemoveExploration(w http.ResponseWriter, r *http.Request) {
|
||||
eID, err := paramID("eid", r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, err.Error())
|
||||
|
|
|
@ -15,7 +15,9 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
DefaultCookieName = "session"
|
||||
// DefaultCookieName is the name of the stored cookie
|
||||
DefaultCookieName = "session"
|
||||
// DefaultCookieDuration is the length of time the cookie is valid
|
||||
DefaultCookieDuration = time.Hour * 24 * 30
|
||||
)
|
||||
|
||||
|
|
|
@ -47,7 +47,8 @@ type kapacitor struct {
|
|||
Links kapaLinks `json:"links"` // Links are URI locations related to kapacitor
|
||||
}
|
||||
|
||||
func (h *Store) NewKapacitor(w http.ResponseWriter, r *http.Request) {
|
||||
// NewKapacitor adds valid kapacitor store store.
|
||||
func (h *Service) NewKapacitor(w http.ResponseWriter, r *http.Request) {
|
||||
srcID, err := paramID("id", r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, err.Error())
|
||||
|
@ -91,6 +92,7 @@ func (h *Store) NewKapacitor(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func newKapacitor(srv chronograf.Server) kapacitor {
|
||||
httpAPISrcs := "/chronograf/v1/sources"
|
||||
return kapacitor{
|
||||
ID: strconv.Itoa(srv.ID),
|
||||
Name: srv.Name,
|
||||
|
@ -108,7 +110,8 @@ type kapacitors struct {
|
|||
Kapacitors []kapacitor `json:"kapacitors"`
|
||||
}
|
||||
|
||||
func (h *Store) Kapacitors(w http.ResponseWriter, r *http.Request) {
|
||||
// Kapacitors retrieves all kapacitors from store.
|
||||
func (h *Service) Kapacitors(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
mrSrvs, err := h.ServersStore.All(ctx)
|
||||
if err != nil {
|
||||
|
@ -128,7 +131,8 @@ func (h *Store) Kapacitors(w http.ResponseWriter, r *http.Request) {
|
|||
encodeJSON(w, http.StatusOK, res, h.Logger)
|
||||
}
|
||||
|
||||
func (h *Store) KapacitorsID(w http.ResponseWriter, r *http.Request) {
|
||||
// KapacitorsID retrieves a kapacitor with ID from store.
|
||||
func (h *Service) KapacitorsID(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := paramID("kid", r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, err.Error())
|
||||
|
@ -152,7 +156,8 @@ func (h *Store) KapacitorsID(w http.ResponseWriter, r *http.Request) {
|
|||
encodeJSON(w, http.StatusOK, res, h.Logger)
|
||||
}
|
||||
|
||||
func (h *Store) RemoveKapacitor(w http.ResponseWriter, r *http.Request) {
|
||||
// RemoveKapacitor deletes kapacitor from store.
|
||||
func (h *Service) RemoveKapacitor(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := paramID("kid", r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, err.Error())
|
||||
|
@ -199,7 +204,8 @@ func (p *patchKapacitorRequest) Valid() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (h *Store) UpdateKapacitor(w http.ResponseWriter, r *http.Request) {
|
||||
// UpdateKapacitor incrementally updates a kapacitor definition in the store
|
||||
func (h *Service) UpdateKapacitor(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := paramID("kid", r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, err.Error())
|
||||
|
|
|
@ -15,6 +15,7 @@ type layoutResponse struct {
|
|||
}
|
||||
|
||||
func newLayoutResponse(layout chronograf.Layout) layoutResponse {
|
||||
httpAPILayouts := "/chronograf/v1/layouts"
|
||||
href := fmt.Sprintf("%s/%s", httpAPILayouts, layout.ID)
|
||||
rel := "self"
|
||||
|
||||
|
@ -27,7 +28,8 @@ func newLayoutResponse(layout chronograf.Layout) layoutResponse {
|
|||
}
|
||||
}
|
||||
|
||||
func (h *Store) NewLayout(w http.ResponseWriter, r *http.Request) {
|
||||
// NewLayout adds a valid layout to store.
|
||||
func (h *Service) NewLayout(w http.ResponseWriter, r *http.Request) {
|
||||
var layout chronograf.Layout
|
||||
if err := json.NewDecoder(r.Body).Decode(&layout); err != nil {
|
||||
invalidJSON(w)
|
||||
|
@ -54,7 +56,8 @@ type getLayoutsResponse struct {
|
|||
Layouts []layoutResponse `json:"layouts"`
|
||||
}
|
||||
|
||||
func (h *Store) Layouts(w http.ResponseWriter, r *http.Request) {
|
||||
// Layouts retrieves all layouts from store
|
||||
func (h *Service) Layouts(w http.ResponseWriter, r *http.Request) {
|
||||
// Construct a filter sieve for both applications and measurements
|
||||
filtered := map[string]bool{}
|
||||
for _, a := range r.URL.Query()["app"] {
|
||||
|
@ -93,7 +96,8 @@ func (h *Store) Layouts(w http.ResponseWriter, r *http.Request) {
|
|||
encodeJSON(w, http.StatusOK, res, h.Logger)
|
||||
}
|
||||
|
||||
func (h *Store) LayoutsID(w http.ResponseWriter, r *http.Request) {
|
||||
// LayoutsID retrieves layout with ID from store
|
||||
func (h *Service) LayoutsID(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
id := httprouter.GetParamFromContext(ctx, "id")
|
||||
|
||||
|
@ -107,7 +111,8 @@ func (h *Store) LayoutsID(w http.ResponseWriter, r *http.Request) {
|
|||
encodeJSON(w, http.StatusOK, res, h.Logger)
|
||||
}
|
||||
|
||||
func (h *Store) RemoveLayout(w http.ResponseWriter, r *http.Request) {
|
||||
// RemoveLayout deletes layout from store.
|
||||
func (h *Service) RemoveLayout(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
id := httprouter.GetParamFromContext(ctx, "id")
|
||||
|
||||
|
@ -123,7 +128,8 @@ func (h *Store) RemoveLayout(w http.ResponseWriter, r *http.Request) {
|
|||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (h *Store) UpdateLayout(w http.ResponseWriter, r *http.Request) {
|
||||
// UpdateLayout replaces the layout of ID with new valid layout.
|
||||
func (h *Service) UpdateLayout(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
id := httprouter.GetParamFromContext(ctx, "id")
|
||||
|
||||
|
@ -155,6 +161,7 @@ func (h *Store) UpdateLayout(w http.ResponseWriter, r *http.Request) {
|
|||
encodeJSON(w, http.StatusOK, res, h.Logger)
|
||||
}
|
||||
|
||||
// ValidLayoutRequest checks if the layout has valid application, measurement and cells.
|
||||
func ValidLayoutRequest(l chronograf.Layout) error {
|
||||
if l.Application == "" || l.Measurement == "" || len(l.Cells) == 0 {
|
||||
return fmt.Errorf("app, measurement, and cells required")
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/influxdata/chronograf"
|
||||
)
|
||||
|
||||
// Logger is middleware that logs the request
|
||||
func Logger(logger chronograf.Logger, next http.Handler) http.Handler {
|
||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||
logger.
|
||||
|
|
|
@ -11,7 +11,8 @@ type mapping struct {
|
|||
Name string `json:"name"` // The application name which will be assigned to the corresponding measurement
|
||||
}
|
||||
|
||||
func (h *Store) GetMappings(w http.ResponseWriter, r *http.Request) {
|
||||
// GetMappings returns the known mappings of measurements to applications
|
||||
func (h *Service) GetMappings(w http.ResponseWriter, r *http.Request) {
|
||||
cpu := "cpu"
|
||||
system := "System"
|
||||
mp := getMappingsResponse{
|
||||
|
|
119
server/mux.go
119
server/mux.go
|
@ -17,40 +17,18 @@ const (
|
|||
JSONType = "application/json"
|
||||
)
|
||||
|
||||
var (
|
||||
httpAPIRoot = "/chronograf/v1/"
|
||||
httpAPILayouts = "/chronograf/v1/layouts"
|
||||
httpAPILayoutsID = "/chronograf/v1/layouts/:id"
|
||||
httpAPIMappings = "/chronograf/v1/mappings"
|
||||
httpAPISrcs = "/chronograf/v1/sources"
|
||||
httpAPISrcsID = "/chronograf/v1/sources/:id"
|
||||
httpAPISrcsIDProxy = "/chronograf/v1/sources/:id/proxy"
|
||||
httpAPISrcsIDKapas = "/chronograf/v1/sources/:id/kapacitors"
|
||||
httpAPISrcsIDKapasID = "/chronograf/v1/sources/:id/kapacitors/:kid"
|
||||
httpAPISrcsIDKapasIDProxy = "/chronograf/v1/sources/:id/kapacitors/:kid/proxy"
|
||||
httpAPIUsrs = "/chronograf/v1/users"
|
||||
httpAPIUsrsID = "/chronograf/v1/users/:id"
|
||||
httpAPIUsrsIDExps = "/chronograf/v1/users/:id/explorations"
|
||||
httpAPIUsrsIDExpsID = "/chronograf/v1/users/:id/explorations/:eid"
|
||||
|
||||
httpOAuth = "/oauth"
|
||||
httpOAuthLogout = "/oauth/logout"
|
||||
httpOAuthGHCallback = "/oauth/github/callback"
|
||||
|
||||
httpSwagger = "/swagger.json"
|
||||
httpDocs = "/docs"
|
||||
)
|
||||
|
||||
// MuxOpts are the options for the router. Mostly related to auth.
|
||||
type MuxOpts struct {
|
||||
Develop bool
|
||||
UseAuth bool
|
||||
TokenSecret string
|
||||
GithubClientID string
|
||||
GithubClientSecret string
|
||||
Logger chronograf.Logger
|
||||
Develop bool // Develop loads assets from filesystem instead of bindata
|
||||
UseAuth bool // UseAuth turns on Github OAuth and JWT
|
||||
TokenSecret string // TokenSecret is the JWT secret
|
||||
GithubClientID string // GithubClientID is the GH OAuth id
|
||||
GithubClientSecret string // GithubClientSecret is the GH OAuth secret
|
||||
}
|
||||
|
||||
func NewMux(opts MuxOpts, store Store, proxy InfluxProxy) http.Handler {
|
||||
// NewMux attaches all the route handlers; handler returned servers chronograf.
|
||||
func NewMux(opts MuxOpts, service Service) http.Handler {
|
||||
router := httprouter.New()
|
||||
|
||||
/* React Application */
|
||||
|
@ -64,65 +42,65 @@ func NewMux(opts MuxOpts, store Store, proxy InfluxProxy) http.Handler {
|
|||
router.NotFound = assets
|
||||
|
||||
/* Documentation */
|
||||
router.GET(httpSwagger, Spec())
|
||||
router.GET(httpDocs, Redoc(httpSwagger))
|
||||
router.GET("/swagger.json", Spec())
|
||||
router.GET("/docs", Redoc("/swagger.json"))
|
||||
|
||||
/* API */
|
||||
// Root Routes returns all top-level routes in the API
|
||||
router.GET(httpAPIRoot, AllRoutes(opts.Logger))
|
||||
router.GET("/chronograf/v1/", AllRoutes(opts.Logger))
|
||||
|
||||
// Sources
|
||||
router.GET(httpAPISrcs, store.Sources)
|
||||
router.POST(httpAPISrcs, store.NewSource)
|
||||
router.GET("/chronograf/v1/sources", service.Sources)
|
||||
router.POST("/chronograf/v1/sources", service.NewSource)
|
||||
|
||||
router.GET(httpAPISrcsID, store.SourcesID)
|
||||
router.PATCH(httpAPISrcsID, store.UpdateSource)
|
||||
router.DELETE(httpAPISrcsID, store.RemoveSource)
|
||||
router.GET("/chronograf/v1/sources/:id", service.SourcesID)
|
||||
router.PATCH("/chronograf/v1/sources/:id", service.UpdateSource)
|
||||
router.DELETE("/chronograf/v1/sources/:id", service.RemoveSource)
|
||||
|
||||
// Source Proxy
|
||||
router.POST(httpAPISrcsIDProxy, proxy.Proxy)
|
||||
router.POST("/chronograf/v1/sources/:id/proxy", service.Proxy)
|
||||
|
||||
// Kapacitor
|
||||
router.GET(httpAPISrcsIDKapas, store.Kapacitors)
|
||||
router.POST(httpAPISrcsIDKapas, store.NewKapacitor)
|
||||
router.GET("/chronograf/v1/sources/:id/kapacitors", service.Kapacitors)
|
||||
router.POST("/chronograf/v1/sources/:id/kapacitors", service.NewKapacitor)
|
||||
|
||||
router.GET(httpAPISrcsIDKapasID, store.KapacitorsID)
|
||||
router.PATCH(httpAPISrcsIDKapasID, store.UpdateKapacitor)
|
||||
router.DELETE(httpAPISrcsIDKapasID, store.RemoveKapacitor)
|
||||
router.GET("/chronograf/v1/sources/:id/kapacitors/:kid", service.KapacitorsID)
|
||||
router.PATCH("/chronograf/v1/sources/:id/kapacitors/:kid", service.UpdateKapacitor)
|
||||
router.DELETE("/chronograf/v1/sources/:id/kapacitors/:kid", service.RemoveKapacitor)
|
||||
|
||||
// Kapacitor Proxy
|
||||
router.GET(httpAPISrcsIDKapasIDProxy, proxy.KapacitorProxyGet)
|
||||
router.POST(httpAPISrcsIDKapasIDProxy, proxy.KapacitorProxyPost)
|
||||
router.PATCH(httpAPISrcsIDKapasIDProxy, proxy.KapacitorProxyPatch)
|
||||
router.DELETE(httpAPISrcsIDKapasIDProxy, proxy.KapacitorProxyDelete)
|
||||
router.GET("/chronograf/v1/sources/:id/kapacitors/:kid/proxy", service.KapacitorProxyGet)
|
||||
router.POST("/chronograf/v1/sources/:id/kapacitors/:kid/proxy", service.KapacitorProxyPost)
|
||||
router.PATCH("/chronograf/v1/sources/:id/kapacitors/:kid/proxy", service.KapacitorProxyPatch)
|
||||
router.DELETE("/chronograf/v1/sources/:id/kapacitors/:kid/proxy", service.KapacitorProxyDelete)
|
||||
|
||||
// Mappings
|
||||
router.GET(httpAPIMappings, store.GetMappings)
|
||||
router.GET("/chronograf/v1/mappings", service.GetMappings)
|
||||
|
||||
// Layouts
|
||||
router.GET(httpAPILayouts, store.Layouts)
|
||||
router.POST(httpAPILayouts, store.NewLayout)
|
||||
router.GET("/chronograf/v1/layouts", service.Layouts)
|
||||
router.POST("/chronograf/v1/layouts", service.NewLayout)
|
||||
|
||||
router.GET(httpAPILayoutsID, store.LayoutsID)
|
||||
router.PUT(httpAPILayoutsID, store.UpdateLayout)
|
||||
router.DELETE(httpAPILayoutsID, store.RemoveLayout)
|
||||
router.GET("/chronograf/v1/layouts/:id", service.LayoutsID)
|
||||
router.PUT("/chronograf/v1/layouts/:id", service.UpdateLayout)
|
||||
router.DELETE("/chronograf/v1/layouts/:id", service.RemoveLayout)
|
||||
|
||||
// Users
|
||||
/*
|
||||
router.GET(httpAPIUsrs, Users)
|
||||
router.POST(httpAPIUsrs, NewUser)
|
||||
router.GET("/chronograf/v1/users", Users)
|
||||
router.POST("/chronograf/v1/users", NewUser)
|
||||
|
||||
router.GET(httpAPIUsrsID, UsersID)
|
||||
router.PATCH(httpAPIUsrsID, UpdateUser)
|
||||
router.DELETE(httpAPIUsrsID, RemoveUser)
|
||||
router.GET("/chronograf/v1/users/:id", UsersID)
|
||||
router.PATCH("/chronograf/v1/users/:id", UpdateUser)
|
||||
router.DELETE("/chronograf/v1/users/:id", RemoveUser)
|
||||
*/
|
||||
// Explorations
|
||||
router.GET(httpAPIUsrsIDExps, store.Explorations)
|
||||
router.POST(httpAPIUsrsIDExps, store.NewExploration)
|
||||
router.GET("/chronograf/v1/users/:id/explorations", service.Explorations)
|
||||
router.POST("/chronograf/v1/users/:id/explorations", service.NewExploration)
|
||||
|
||||
router.GET(httpAPIUsrsIDExpsID, store.ExplorationsID)
|
||||
router.PATCH(httpAPIUsrsIDExpsID, store.UpdateExploration)
|
||||
router.DELETE(httpAPIUsrsIDExpsID, store.RemoveExploration)
|
||||
router.GET("/chronograf/v1/users/:id/explorations/:eid", service.ExplorationsID)
|
||||
router.PATCH("/chronograf/v1/users/:id/explorations/:eid", service.UpdateExploration)
|
||||
router.DELETE("/chronograf/v1/users/:id/explorations/:eid", service.RemoveExploration)
|
||||
|
||||
/* Authentication */
|
||||
if opts.UseAuth {
|
||||
|
@ -132,6 +110,7 @@ func NewMux(opts MuxOpts, store Store, proxy InfluxProxy) http.Handler {
|
|||
return Logger(opts.Logger, router)
|
||||
}
|
||||
|
||||
// AuthAPI adds the OAuth routes if auth is enabled.
|
||||
func AuthAPI(opts MuxOpts, router *httprouter.Router) http.Handler {
|
||||
auth := jwt.NewJWT(opts.TokenSecret)
|
||||
|
||||
|
@ -146,19 +125,18 @@ func AuthAPI(opts MuxOpts, router *httprouter.Router) http.Handler {
|
|||
opts.Logger,
|
||||
)
|
||||
|
||||
router.GET(httpOAuth, gh.Login())
|
||||
router.GET(httpOAuthLogout, gh.Logout())
|
||||
router.GET(httpOAuthGHCallback, gh.Callback())
|
||||
router.GET("/oauth", gh.Login())
|
||||
router.GET("/oauth/logout", gh.Logout())
|
||||
router.GET("/oauth/github/callback", gh.Callback())
|
||||
|
||||
tokenMiddleware := AuthorizedToken(&auth, &CookieExtractor{Name: "session"}, opts.Logger, router)
|
||||
// Wrap the API with token validation middleware.
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if strings.HasPrefix(r.URL.Path, httpAPIRoot) {
|
||||
if strings.HasPrefix(r.URL.Path, "/chronograf/v1/") {
|
||||
tokenMiddleware.ServeHTTP(w, r)
|
||||
return
|
||||
} else {
|
||||
router.ServeHTTP(w, r)
|
||||
}
|
||||
router.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -170,6 +148,7 @@ func encodeJSON(w http.ResponseWriter, status int, v interface{}, logger chronog
|
|||
}
|
||||
}
|
||||
|
||||
// Error writes an JSON message
|
||||
func Error(w http.ResponseWriter, code int, msg string) {
|
||||
e := struct {
|
||||
Code int `json:"code"`
|
||||
|
|
|
@ -10,13 +10,7 @@ import (
|
|||
"github.com/influxdata/chronograf"
|
||||
)
|
||||
|
||||
type InfluxProxy struct {
|
||||
Srcs chronograf.SourcesStore
|
||||
ServersStore chronograf.ServersStore
|
||||
TimeSeries chronograf.TimeSeries
|
||||
Logger chronograf.Logger
|
||||
}
|
||||
|
||||
// ValidProxyRequest checks if queries specify a command.
|
||||
func ValidProxyRequest(p chronograf.Query) error {
|
||||
if p.Command == "" {
|
||||
return fmt.Errorf("query field required")
|
||||
|
@ -28,7 +22,8 @@ type postProxyResponse struct {
|
|||
Results interface{} `json:"results"` // results from influx
|
||||
}
|
||||
|
||||
func (h *InfluxProxy) Proxy(w http.ResponseWriter, r *http.Request) {
|
||||
// Proxy proxies requests to infludb.
|
||||
func (h *Service) Proxy(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := paramID("id", r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, err.Error())
|
||||
|
@ -46,7 +41,7 @@ func (h *InfluxProxy) Proxy(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
ctx := r.Context()
|
||||
src, err := h.Srcs.Get(ctx, id)
|
||||
src, err := h.SourcesStore.Get(ctx, id)
|
||||
if err != nil {
|
||||
notFound(w, id)
|
||||
return
|
||||
|
@ -76,7 +71,8 @@ func (h *InfluxProxy) Proxy(w http.ResponseWriter, r *http.Request) {
|
|||
encodeJSON(w, http.StatusOK, res, h.Logger)
|
||||
}
|
||||
|
||||
func (h *InfluxProxy) KapacitorProxy(w http.ResponseWriter, r *http.Request) {
|
||||
// KapacitorProxy proxies requests to kapacitor using the path query parameter.
|
||||
func (h *Service) KapacitorProxy(w http.ResponseWriter, r *http.Request) {
|
||||
srcID, err := paramID("id", r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, err.Error())
|
||||
|
@ -123,18 +119,22 @@ func (h *InfluxProxy) KapacitorProxy(w http.ResponseWriter, r *http.Request) {
|
|||
proxy.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (h *InfluxProxy) KapacitorProxyPost(w http.ResponseWriter, r *http.Request) {
|
||||
// KapacitorProxyPost proxies POST to kapacitor
|
||||
func (h *Service) KapacitorProxyPost(w http.ResponseWriter, r *http.Request) {
|
||||
h.KapacitorProxy(w, r)
|
||||
}
|
||||
|
||||
func (h *InfluxProxy) KapacitorProxyPatch(w http.ResponseWriter, r *http.Request) {
|
||||
// KapacitorProxyPatch proxies PATCH to kapacitor
|
||||
func (h *Service) KapacitorProxyPatch(w http.ResponseWriter, r *http.Request) {
|
||||
h.KapacitorProxy(w, r)
|
||||
}
|
||||
|
||||
func (h *InfluxProxy) KapacitorProxyGet(w http.ResponseWriter, r *http.Request) {
|
||||
// KapacitorProxyGet proxies GET to kapacitor
|
||||
func (h *Service) KapacitorProxyGet(w http.ResponseWriter, r *http.Request) {
|
||||
h.KapacitorProxy(w, r)
|
||||
}
|
||||
|
||||
func (h *InfluxProxy) KapacitorProxyDelete(w http.ResponseWriter, r *http.Request) {
|
||||
// KapacitorProxyDelete proxies DELETE to kapacitor
|
||||
func (h *Service) KapacitorProxyDelete(w http.ResponseWriter, r *http.Request) {
|
||||
h.KapacitorProxy(w, r)
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ const index = `<!DOCTYPE html>
|
|||
</html>
|
||||
`
|
||||
|
||||
// Redoc servers the swagger JSON using the redoc package.
|
||||
func Redoc(swagger string) http.HandlerFunc {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
|
|
|
@ -13,12 +13,13 @@ type getRoutesResponse struct {
|
|||
Users string `json:"users"` // Location of the users endpoint
|
||||
}
|
||||
|
||||
// AllRoutes returns all top level routes within chronograf
|
||||
func AllRoutes(logger chronograf.Logger) http.HandlerFunc {
|
||||
routes := getRoutesResponse{
|
||||
Sources: httpAPISrcs,
|
||||
Layouts: httpAPILayouts,
|
||||
Users: httpAPIUsrs,
|
||||
Mappings: httpAPIMappings,
|
||||
Sources: "/chronograf/v1/sources",
|
||||
Layouts: "/chronograf/v1/layouts",
|
||||
Users: "/chronograf/v1/users",
|
||||
Mappings: "/chronograf/v1/mappings",
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
111
server/server.go
111
server/server.go
|
@ -1,11 +1,9 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/chronograf"
|
||||
|
@ -19,7 +17,7 @@ import (
|
|||
"github.com/tylerb/graceful"
|
||||
)
|
||||
|
||||
var logger chronograf.Logger = clog.New()
|
||||
var logger = clog.New()
|
||||
|
||||
// Server for the chronograf API
|
||||
type Server struct {
|
||||
|
@ -38,73 +36,82 @@ type Server struct {
|
|||
GithubClientID string `short:"i" long:"github-client-id" description:"Github Client ID for OAuth 2 support" env:"GH_CLIENT_ID"`
|
||||
GithubClientSecret string `short:"s" long:"github-client-secret" description:"Github Client Secret for OAuth 2 support" env:"GH_CLIENT_SECRET"`
|
||||
|
||||
httpServerL net.Listener
|
||||
handler http.Handler
|
||||
Listener net.Listener
|
||||
handler http.Handler
|
||||
}
|
||||
|
||||
func (s *Server) useAuth() bool {
|
||||
return s.TokenSecret != "" && s.GithubClientID != "" && s.GithubClientSecret != ""
|
||||
}
|
||||
|
||||
// Serve starts and runs the chronograf server
|
||||
func (s *Server) Serve() error {
|
||||
c := bolt.NewClient()
|
||||
c.Path = s.BoltPath
|
||||
if err := c.Open(); err != nil {
|
||||
logger.WithField("component", "boltstore").Panic("Unable to open boltdb; is there a mrfusion already running?", err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
apps := canned.NewApps(s.CannedPath, &uuid.V4{})
|
||||
|
||||
// allLayouts acts as a front-end to both the bolt layouts and the filesystem layouts.
|
||||
allLayouts := &layouts.MultiLayoutStore{
|
||||
Stores: []chronograf.LayoutStore{
|
||||
c.LayoutStore,
|
||||
apps,
|
||||
},
|
||||
}
|
||||
h := Store{
|
||||
ExplorationStore: c.ExplorationStore,
|
||||
SourcesStore: c.SourcesStore,
|
||||
ServersStore: c.ServersStore,
|
||||
LayoutStore: allLayouts,
|
||||
}
|
||||
|
||||
p := InfluxProxy{
|
||||
Srcs: c.SourcesStore,
|
||||
TimeSeries: &influx.Client{},
|
||||
ServersStore: c.ServersStore,
|
||||
}
|
||||
|
||||
useAuth := s.TokenSecret != "" && s.GithubClientID != "" && s.GithubClientSecret != ""
|
||||
service := openService(s.BoltPath, s.CannedPath)
|
||||
s.handler = NewMux(MuxOpts{
|
||||
Develop: s.Develop,
|
||||
TokenSecret: s.TokenSecret,
|
||||
GithubClientID: s.GithubClientID,
|
||||
GithubClientSecret: s.GithubClientSecret,
|
||||
Logger: logger,
|
||||
UseAuth: useAuth,
|
||||
}, h, p)
|
||||
UseAuth: s.useAuth(),
|
||||
}, service)
|
||||
|
||||
listener, err := net.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port)))
|
||||
var err error
|
||||
s.Listener, err = net.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port)))
|
||||
if err != nil {
|
||||
logger.
|
||||
WithField("component", "server").
|
||||
Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
s.httpServerL = listener
|
||||
var wg sync.WaitGroup
|
||||
|
||||
httpServer := &graceful.Server{Server: new(http.Server)}
|
||||
httpServer.SetKeepAlivesEnabled(true)
|
||||
httpServer.TCPKeepAlive = 3 * time.Minute
|
||||
httpServer.TCPKeepAlive = 1 * time.Minute
|
||||
httpServer.Handler = s.handler
|
||||
|
||||
wg.Add(1)
|
||||
log.Printf("Serving chronograf at http://%s", s.httpServerL.Addr())
|
||||
go func(l net.Listener) {
|
||||
defer wg.Done()
|
||||
if err := httpServer.Serve(l); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
log.Printf("Stopped serving chronograf at http://%s", l.Addr())
|
||||
}(s.httpServerL)
|
||||
logger.
|
||||
WithField("component", "server").
|
||||
Info("Serving chronograf at http://%s", s.Listener.Addr())
|
||||
|
||||
if err := httpServer.Serve(s.Listener); err != nil {
|
||||
logger.
|
||||
WithField("component", "server").
|
||||
Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.
|
||||
WithField("component", "server").
|
||||
Info("Stopped serving chronograf at http://%s", s.Listener.Addr())
|
||||
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
func openService(boltPath, cannedPath string) Service {
|
||||
db := bolt.NewClient()
|
||||
db.Path = boltPath
|
||||
if err := db.Open(); err != nil {
|
||||
logger.
|
||||
WithField("component", "boltstore").
|
||||
Panic("Unable to open boltdb; is there a mrfusion already running?", err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
apps := canned.NewApps(cannedPath, &uuid.V4{})
|
||||
// Acts as a front-end to both the bolt layouts and the filesystem layouts.
|
||||
layouts := &layouts.MultiLayoutStore{
|
||||
Stores: []chronograf.LayoutStore{
|
||||
db.LayoutStore,
|
||||
apps,
|
||||
},
|
||||
}
|
||||
|
||||
return Service{
|
||||
ExplorationStore: db.ExplorationStore,
|
||||
SourcesStore: db.SourcesStore,
|
||||
ServersStore: db.ServersStore,
|
||||
TimeSeries: &influx.Client{},
|
||||
LayoutStore: layouts,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,12 @@ package server
|
|||
|
||||
import "github.com/influxdata/chronograf"
|
||||
|
||||
// Store handles REST calls to the persistence
|
||||
type Store struct {
|
||||
// Service handles REST calls to the persistence
|
||||
type Service struct {
|
||||
ExplorationStore chronograf.ExplorationStore
|
||||
SourcesStore chronograf.SourcesStore
|
||||
ServersStore chronograf.ServersStore
|
||||
LayoutStore chronograf.LayoutStore
|
||||
TimeSeries chronograf.TimeSeries
|
||||
Logger chronograf.Logger
|
||||
}
|
|
@ -21,6 +21,7 @@ type sourceResponse struct {
|
|||
}
|
||||
|
||||
func newSourceResponse(src chronograf.Source) sourceResponse {
|
||||
httpAPISrcs := "/chronograf/v1/sources"
|
||||
return sourceResponse{
|
||||
Source: src,
|
||||
Links: sourceLinks{
|
||||
|
@ -31,7 +32,8 @@ func newSourceResponse(src chronograf.Source) sourceResponse {
|
|||
}
|
||||
}
|
||||
|
||||
func (h *Store) NewSource(w http.ResponseWriter, r *http.Request) {
|
||||
// NewSource adds a new valid source to the store
|
||||
func (h *Service) NewSource(w http.ResponseWriter, r *http.Request) {
|
||||
var src chronograf.Source
|
||||
if err := json.NewDecoder(r.Body).Decode(&src); err != nil {
|
||||
invalidJSON(w)
|
||||
|
@ -58,7 +60,8 @@ type getSourcesResponse struct {
|
|||
Sources []sourceResponse `json:"sources"`
|
||||
}
|
||||
|
||||
func (h *Store) Sources(w http.ResponseWriter, r *http.Request) {
|
||||
// Sources returns all sources from the store.
|
||||
func (h *Service) Sources(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
srcs, err := h.SourcesStore.All(ctx)
|
||||
if err != nil {
|
||||
|
@ -77,7 +80,8 @@ func (h *Store) Sources(w http.ResponseWriter, r *http.Request) {
|
|||
encodeJSON(w, http.StatusOK, res, h.Logger)
|
||||
}
|
||||
|
||||
func (h *Store) SourcesID(w http.ResponseWriter, r *http.Request) {
|
||||
// SourcesID retrieves a source from the store
|
||||
func (h *Service) SourcesID(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := paramID("id", r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, err.Error())
|
||||
|
@ -95,7 +99,8 @@ func (h *Store) SourcesID(w http.ResponseWriter, r *http.Request) {
|
|||
encodeJSON(w, http.StatusOK, res, h.Logger)
|
||||
}
|
||||
|
||||
func (h *Store) RemoveSource(w http.ResponseWriter, r *http.Request) {
|
||||
// RemoveSource deletes the source from the store
|
||||
func (h *Service) RemoveSource(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := paramID("id", r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, err.Error())
|
||||
|
@ -112,7 +117,8 @@ func (h *Store) RemoveSource(w http.ResponseWriter, r *http.Request) {
|
|||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (h *Store) UpdateSource(w http.ResponseWriter, r *http.Request) {
|
||||
// UpdateSource handles incremental updates of a data source
|
||||
func (h *Service) UpdateSource(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := paramID("id", r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, err.Error())
|
||||
|
@ -162,6 +168,7 @@ func (h *Store) UpdateSource(w http.ResponseWriter, r *http.Request) {
|
|||
encodeJSON(w, http.StatusOK, newSourceResponse(src), h.Logger)
|
||||
}
|
||||
|
||||
// ValidSourceRequest checks if name, url and type are valid
|
||||
func ValidSourceRequest(s chronograf.Source) error {
|
||||
// Name and URL areq required
|
||||
if s.Name == "" || s.URL == "" {
|
||||
|
|
|
@ -4,6 +4,7 @@ package server
|
|||
|
||||
import "net/http"
|
||||
|
||||
// Spec servers the swagger.json file from bindata
|
||||
func Spec() http.HandlerFunc {
|
||||
swagger, err := Asset("swagger.json")
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
Loading…
Reference in New Issue