diff --git a/server/mux.go b/server/mux.go index 91c22bcfc..e77b0732e 100644 --- a/server/mux.go +++ b/server/mux.go @@ -28,6 +28,7 @@ type MuxOpts struct { UseAuth bool // UseAuth turns on Github OAuth and JWT Auth oauth2.Authenticator // Auth is used to authenticate and authorize ProviderFuncs []func(func(oauth2.Provider, oauth2.Mux)) + StatusFeedURL string // JSON Feed URL for the client Status page News Feed } // NewMux attaches all the route handlers; handler returned servers chronograf. @@ -180,8 +181,13 @@ func NewMux(opts MuxOpts, service Service) http.Handler { router.PUT("/chronograf/v1/sources/:id/dbs/:dbid/rps/:rpid", service.UpdateRetentionPolicy) router.DELETE("/chronograf/v1/sources/:id/dbs/:dbid/rps/:rpid", service.DropRetentionPolicy) + externalLinks := getExternalLinksResponse{ + StatusFeed: &opts.StatusFeedURL, + } + allRoutes := &AllRoutes{ - Logger: opts.Logger, + Logger: opts.Logger, + ExternalLinks: externalLinks, } router.Handler("GET", "/chronograf/v1/", allRoutes) diff --git a/server/routes.go b/server/routes.go index d50116cac..cc35ebc5f 100644 --- a/server/routes.go +++ b/server/routes.go @@ -29,32 +29,45 @@ func (r *AuthRoutes) Lookup(provider string) (AuthRoute, bool) { } type getRoutesResponse struct { - Layouts string `json:"layouts"` // Location of the layouts endpoint - Mappings string `json:"mappings"` // Location of the application mappings endpoint - Sources string `json:"sources"` // Location of the sources endpoint - Me string `json:"me"` // Location of the me endpoint - Dashboards string `json:"dashboards"` // Location of the dashboards endpoint - Auth []AuthRoute `json:"auth"` // Location of all auth routes. - Logout *string `json:"logout,omitempty"` // Location of the logout route for all auth routes + Layouts string `json:"layouts"` // Location of the layouts endpoint + Mappings string `json:"mappings"` // Location of the application mappings endpoint + Sources string `json:"sources"` // Location of the sources endpoint + Me string `json:"me"` // Location of the me endpoint + Dashboards string `json:"dashboards"` // Location of the dashboards endpoint + Auth []AuthRoute `json:"auth"` // Location of all auth routes. + Logout *string `json:"logout,omitempty"` // Location of the logout route for all auth routes + ExternalLinks getExternalLinksResponse `json:"external"` // All external links for the client to use } -// AllRoutes is a handler that returns all links to resources in Chronograf server. +type getExternalLinksResponse struct { + StatusFeed *string `json:"statusFeed,omitempty"` // Location of the a JSON Feed for client's Status page News Feed +} + +// AllRoutes is a handler that returns all links to resources in Chronograf server, as well as +// external links for the client to know about, such as for JSON feeds or custom side nav buttons. // Optionally, routes for authentication can be returned. type AllRoutes struct { - AuthRoutes []AuthRoute // Location of all auth routes. If no auth, this can be empty. - LogoutLink string // Location of the logout route for all auth routes. If no auth, this can be empty. - Logger chronograf.Logger + AuthRoutes []AuthRoute // Location of all auth routes. If no auth, this can be empty. + LogoutLink string // Location of the logout route for all auth routes. If no auth, this can be empty. + ExternalLinks getExternalLinksResponse // All external links for the client to use + Logger chronograf.Logger +} + +// AllExternalLinks returns +type AllExternalLinks struct { + StatusFeed string // Location of the JSON Feed for consumption by the News Feed on the client Status page. } // ServeHTTP returns all top level routes within chronograf func (a *AllRoutes) ServeHTTP(w http.ResponseWriter, r *http.Request) { routes := getRoutesResponse{ - Sources: "/chronograf/v1/sources", - Layouts: "/chronograf/v1/layouts", - Me: "/chronograf/v1/me", - Mappings: "/chronograf/v1/mappings", - Dashboards: "/chronograf/v1/dashboards", - Auth: make([]AuthRoute, len(a.AuthRoutes)), // We want to return at least an empty array, rather than null + Sources: "/chronograf/v1/sources", + Layouts: "/chronograf/v1/layouts", + Me: "/chronograf/v1/me", + Mappings: "/chronograf/v1/mappings", + Dashboards: "/chronograf/v1/dashboards", + Auth: make([]AuthRoute, len(a.AuthRoutes)), // We want to return at least an empty array, rather than null + ExternalLinks: a.ExternalLinks, } // The JSON response will have no field present for the LogoutLink if there is no logout link. diff --git a/server/routes_test.go b/server/routes_test.go index 8e18fd0f8..aa0c882c9 100644 --- a/server/routes_test.go +++ b/server/routes_test.go @@ -29,7 +29,7 @@ func TestAllRoutes(t *testing.T) { if err := json.Unmarshal(body, &routes); err != nil { t.Error("TestAllRoutes not able to unmarshal JSON response") } - want := `{"layouts":"/chronograf/v1/layouts","mappings":"/chronograf/v1/mappings","sources":"/chronograf/v1/sources","me":"/chronograf/v1/me","dashboards":"/chronograf/v1/dashboards","auth":[]} + want := `{"layouts":"/chronograf/v1/layouts","mappings":"/chronograf/v1/mappings","sources":"/chronograf/v1/sources","me":"/chronograf/v1/me","dashboards":"/chronograf/v1/dashboards","auth":[],"external":{}} ` if want != string(body) { t.Errorf("TestAllRoutes\nwanted\n*%s*\ngot\n*%s*", want, string(body)) @@ -67,7 +67,7 @@ func TestAllRoutesWithAuth(t *testing.T) { if err := json.Unmarshal(body, &routes); err != nil { t.Error("TestAllRoutesWithAuth not able to unmarshal JSON response") } - want := `{"layouts":"/chronograf/v1/layouts","mappings":"/chronograf/v1/mappings","sources":"/chronograf/v1/sources","me":"/chronograf/v1/me","dashboards":"/chronograf/v1/dashboards","auth":[{"name":"github","label":"GitHub","login":"/oauth/github/login","logout":"/oauth/github/logout","callback":"/oauth/github/callback"}],"logout":"/oauth/logout"} + want := `{"layouts":"/chronograf/v1/layouts","mappings":"/chronograf/v1/mappings","sources":"/chronograf/v1/sources","me":"/chronograf/v1/me","dashboards":"/chronograf/v1/dashboards","auth":[{"name":"github","label":"GitHub","login":"/oauth/github/login","logout":"/oauth/github/logout","callback":"/oauth/github/callback"}],"logout":"/oauth/logout","external":{}} ` if want != string(body) { t.Errorf("TestAllRoutesWithAuth\nwanted\n*%s*\ngot\n*%s*", want, string(body)) diff --git a/server/server.go b/server/server.go index b86cde6ae..0b8296a9c 100644 --- a/server/server.go +++ b/server/server.go @@ -76,6 +76,8 @@ type Server struct { GenericTokenURL string `long:"generic-token-url" description:"OAuth 2.0 provider's token endpoint URL" env:"GENERIC_TOKEN_URL"` GenericAPIURL string `long:"generic-api-url" description:"URL that returns OpenID UserInfo compatible information." env:"GENERIC_API_URL"` + StatusFeedURL string `long:"status-feed-url" description:"URL of a JSON Feed to display as a News Feed on the client Status page." env:"STATUS_FEED_URL"` + ReportingDisabled bool `short:"r" long:"reporting-disabled" description:"Disable reporting of usage stats (os,arch,version,cluster_id,uptime) once every 24hr" env:"REPORTING_DISABLED"` LogLevel string `short:"l" long:"log-level" value-name:"choice" choice:"debug" choice:"info" choice:"error" default:"info" description:"Set the logging level" env:"LOG_LEVEL"` Basepath string `short:"p" long:"basepath" description:"A URL path prefix under which all chronograf routes will be mounted" env:"BASE_PATH"` @@ -258,6 +260,7 @@ func (s *Server) Serve(ctx context.Context) error { ProviderFuncs: providerFuncs, Basepath: basepath, PrefixRoutes: s.PrefixRoutes, + StatusFeedURL: s.StatusFeedURL, }, service) // Add chronograf's version header to all requests