Fix mux paths to be hardcoded; clarify server start; fix golint

pull/296/head
Chris Goller 2016-10-28 11:27:06 -05:00
parent d1359c09b3
commit d6a067427b
15 changed files with 191 additions and 168 deletions

View File

@ -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"
)

View File

@ -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())

View File

@ -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
)

View File

@ -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())

View File

@ -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")

View File

@ -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.

View File

@ -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{

View File

@ -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"`

View File

@ -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)
}

View File

@ -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")

View File

@ -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) {

View File

@ -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,
}
}

View File

@ -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
}

View File

@ -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 == "" {

View File

@ -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) {