2016-10-25 15:20:06 +00:00
package server
import (
Introduce ability to edit a dashboard cell
* Correct documentation for dashboards
* Exclude .git and use 'make run-dev' in 'make continuous'
* Fix dashboard deletion bug where id serialization was wrong
* Commence creation of overlay technology, add autoRefresh props to DashboardPage
* Enhance overlay magnitude of overlay technology
* Add confirm buttons to overlay technology
* Refactor ResizeContainer to accommodate arbitrary containers
* Refactor ResizeContainer to require explicit ResizeTop and ResizeBottom for clarity
* Add markup and styles for OverlayControls
* CellEditorOverlay needs a larger minimum bottom height to accommodate more things
* Revert Visualization to not use ResizeTop or flex-box
* Remove TODO and move to issue
* Refactor CellEditorOverlay to allow selection of graph type
* Style Overlay controls, move confirm buttons to own stylesheet
* Fix toggle buttons in overlay so active is actually active
* Block user-select on a few UI items
* Update cell query shape to support Visualization and LayoutRenderer
* Code cleanup
* Repair fixture schema; update props for affected components
* Wired up selectedGraphType and activeQueryID in CellEditorOverlay
* Wire up chooseMeasurements in QueryBuilder
Pass queryActions into QueryBuilder so that DataExplorer can provide
actionCreators and CellEditorOverlay can provide functions that
modify its component state
* semicolon cleanup
* Bind all queryModifier actions to component state with a stateReducer
* Overlay Technologies™ can add and delete a query from a cell
* Semicolon cleanup
* Add conversion of InfluxQL to QueryConfig for dashboards
* Update go deps to add influxdb at af72d9b0e4ebe95be30e89b160f43eabaf0529ed
* Updated docs for dashboard query config
* Update CHANGELOG to mention InfluxQL to QueryConfig
* Make reducer’s name more specific for clarity
* Remove 'table' as graphType
* Make graph renaming prettier
* Remove duplicate DashboardQuery in swagger.json
* Fix swagger to include name and links for Cell
* Refactor CellEditorOverlay to enable graph type selection
* Add link.self to all Dashboard cells; add bolt migrations
* Make dash graph names only hover on contents
* Consolidate timeRange format patterns, clean up
* Add cell endpoints to dashboards
* Include Line + Stat in Visualization Type list
* Add cell link to dashboards
* Enable step plot and stacked graph in Visualization
* Overlay Technologies are summonable and dismissable
* OverlayTechnologies saves changes to a cell
* Convert NameableGraph to createClass for state
This was converted from a pure function to encapsulate the state of the
buttons. An attempt was made previously to store this state in Redux,
but it proved too convoluted with the current state of the reducers for
cells and dashboards. Another effort must take place to separate a cell
reducer to manage the state of an individual cell in Redux in order for
this state to be sanely kept in Redux as well.
For the time being, this state is being kept in the component for the
sake of expeditiousness, since this is needed for Dashboards to be
released. A refactor of this will occur later.
* Cells should contain a links key in server response
* Clean up console logs
* Use live data instead of a cellQuery fixture
* Update docs for dashboard creation
* DB and RP are already present in the Command field
* Fix LayoutRenderer’s understanding of query schema
* Return a new object, rather that mutate in place
* Visualization doesn’t use activeQueryID
* Selected is an object, not a string
* QueryBuilder refactored to use query index instead of query id
* CellEditorOverlay refactored to use query index instead of query id
* ConfirmButtons doesn’t need to act on an item
* Rename functions to follow convention
* Queries are no longer guaranteed to have ids
* Omit WHERE and GROUP BY clauses when saving query
* Select new query on add in OverlayTechnologies
* Add click outside to dash graph menu, style menu also
* Change context menu from ... to a caret
More consistent with the rest of the UI, better affordance
* Hide graph context menu in presentation mode
Don’t want people editing a dashboard from presentation mode
* Move graph refreshing spinner so it does not overlap with context menu
* Wire up Cell Menu to Overlay Technologies
* Correct empty dashboard type
* Refactor dashboard spec fixtures
* Test syncDashboardCell reducer
* Remove Delete button from graph dropdown menu (for now)
* Update changelog
2017-03-24 00:12:33 +00:00
"context"
2017-02-13 03:33:27 +00:00
"crypto/tls"
2017-03-06 21:16:45 +00:00
"log"
2016-10-24 17:08:36 +00:00
"math/rand"
2016-10-25 15:20:06 +00:00
"net"
"net/http"
2017-06-21 04:19:21 +00:00
"net/url"
2017-03-06 21:16:45 +00:00
"os"
2017-06-18 02:47:02 +00:00
"path"
2016-10-24 17:08:36 +00:00
"runtime"
2016-10-25 15:20:06 +00:00
"strconv"
"time"
"github.com/influxdata/chronograf"
"github.com/influxdata/chronograf/bolt"
2017-03-30 16:48:04 +00:00
"github.com/influxdata/chronograf/influx"
2016-10-25 15:20:06 +00:00
clog "github.com/influxdata/chronograf/log"
2017-02-23 22:17:28 +00:00
"github.com/influxdata/chronograf/oauth2"
2016-10-25 15:20:06 +00:00
"github.com/influxdata/chronograf/uuid"
2016-10-24 17:08:36 +00:00
client "github.com/influxdata/usage-client/v1"
2017-02-13 03:33:27 +00:00
flags "github.com/jessevdk/go-flags"
2016-10-25 15:20:06 +00:00
"github.com/tylerb/graceful"
)
2017-01-27 21:47:46 +00:00
var (
startTime time . Time
basepath string
)
2016-12-19 20:09:59 +00:00
func init ( ) {
startTime = time . Now ( ) . UTC ( )
}
2016-10-25 15:20:06 +00:00
// Server for the chronograf API
type Server struct {
2017-02-13 03:33:27 +00:00
Host string ` long:"host" description:"The IP to listen on" default:"0.0.0.0" env:"HOST" `
Port int ` long:"port" description:"The port to listen on for insecure connections, defaults to a random value" default:"8888" env:"PORT" `
2016-10-25 15:20:06 +00:00
2017-02-13 03:33:27 +00:00
Cert flags . Filename ` long:"cert" description:"Path to PEM encoded public key certificate. " env:"TLS_CERTIFICATE" `
Key flags . Filename ` long:"key" description:"Path to private key associated with given certificate. " env:"TLS_PRIVATE_KEY" `
2016-10-25 15:20:06 +00:00
2017-03-30 16:48:04 +00:00
InfluxDBURL string ` long:"influxdb-url" description:"Location of your InfluxDB instance" env:"INFLUXDB_URL" `
InfluxDBUsername string ` long:"influxdb-username" description:"Username for your InfluxDB instance" env:"INFLUXDB_USERNAME" `
InfluxDBPassword string ` long:"influxdb-password" description:"Password for your InfluxDB instance" env:"INFLUXDB_PASSWORD" `
KapacitorURL string ` long:"kapacitor-url" description:"Location of your Kapacitor instance" env:"KAPACITOR_URL" `
KapacitorUsername string ` long:"kapacitor-username" description:"Username of your Kapacitor instance" env:"KAPACITOR_USERNAME" `
KapacitorPassword string ` long:"kapacitor-password" description:"Password of your Kapacitor instance" env:"KAPACITOR_PASSWORD" `
2017-04-06 18:40:57 +00:00
Develop bool ` short:"d" long:"develop" description:"Run server in develop mode." `
BoltPath string ` short:"b" long:"bolt-path" description:"Full path to boltDB file (/var/lib/chronograf/chronograf-v1.db)" env:"BOLT_PATH" default:"chronograf-v1.db" `
CannedPath string ` short:"c" long:"canned-path" description:"Path to directory of pre-canned application layouts (/usr/share/chronograf/canned)" env:"CANNED_PATH" default:"canned" `
TokenSecret string ` short:"t" long:"token-secret" description:"Secret to sign tokens" env:"TOKEN_SECRET" `
AuthDuration time . Duration ` long:"auth-duration" default:"720h" description:"Total duration of cookie life for authentication (in hours). 0 means authentication expires on browser close." env:"AUTH_DURATION" `
2017-02-16 17:56:59 +00:00
2017-01-05 17:29:26 +00:00
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" `
GithubOrgs [ ] string ` short:"o" long:"github-organization" description:"Github organization user is required to have active membership" env:"GH_ORGS" env-delim:"," `
2017-02-16 17:56:59 +00:00
2017-02-15 05:11:11 +00:00
GoogleClientID string ` long:"google-client-id" description:"Google Client ID for OAuth 2 support" env:"GOOGLE_CLIENT_ID" `
2017-04-11 13:15:46 +00:00
GoogleClientSecret string ` long:"google-client-secret" description:"Google Client Secret for OAuth 2 support" env:"GOOGLE_CLIENT_SECRET" `
2017-02-15 05:11:11 +00:00
GoogleDomains [ ] string ` long:"google-domains" description:"Google email domain user is required to have active membership" env:"GOOGLE_DOMAINS" env-delim:"," `
2017-06-16 09:35:57 +00:00
PublicURL string ` long:"public-url" description:"Full public URL used to access Chronograf from a web browser. Used for OAuth2 authentication. (http://localhost:8888)" env:"PUBLIC_URL" `
2017-02-15 05:11:11 +00:00
2017-02-21 18:04:17 +00:00
HerokuClientID string ` long:"heroku-client-id" description:"Heroku Client ID for OAuth 2 support" env:"HEROKU_CLIENT_ID" `
HerokuSecret string ` long:"heroku-secret" description:"Heroku Secret for OAuth 2 support" env:"HEROKU_SECRET" `
HerokuOrganizations [ ] string ` long:"heroku-organization" description:"Heroku Organization Memberships a user is required to have for access to Chronograf (comma separated)" env:"HEROKU_ORGS" env-delim:"," `
2017-02-16 17:56:59 +00:00
2017-04-06 21:45:33 +00:00
GenericName string ` long:"generic-name" description:"Generic OAuth2 name presented on the login page" env:"GENERIC_NAME" `
GenericClientID string ` long:"generic-client-id" description:"Generic OAuth2 Client ID. Can be used own OAuth2 service." env:"GENERIC_CLIENT_ID" `
GenericClientSecret string ` long:"generic-client-secret" description:"Generic OAuth2 Client Secret" env:"GENERIC_CLIENT_SECRET" `
2017-04-07 19:58:35 +00:00
GenericScopes [ ] string ` long:"generic-scopes" description:"Scopes requested by provider of web client." default:"user:email" env:"GENERIC_SCOPES" env-delim:"," `
2017-04-06 21:45:33 +00:00
GenericDomains [ ] string ` long:"generic-domains" description:"Email domain users' email address to have (example.com)" env:"GENERIC_DOMAINS" env-delim:"," `
GenericAuthURL string ` long:"generic-auth-url" description:"OAuth 2.0 provider's authorization endpoint URL" env:"GENERIC_AUTH_URL" `
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" `
2017-06-16 21:23:46 +00:00
StatusFeedURL string ` long:"status-feed-url" description:"URL of a JSON Feed to display as a News Feed on the client Status page." default:"https://www.influxdata.com/feed/json" env:"STATUS_FEED_URL" `
2017-06-13 20:36:53 +00:00
2017-06-23 23:45:02 +00:00
CustomLinks map [ string ] string ` long:"custom-link" description:"Custom link to be added to the client User menu" env:"CUSTOM_LINKS" env-delim:"," `
2017-06-23 22:12:02 +00:00
2017-06-19 20:27:55 +00:00
Auth0Domain string ` long:"auth0-domain" description:"Subdomain of auth0.com used for Auth0 OAuth2 authentication" env:"AUTH0_DOMAIN" `
Auth0ClientID string ` long:"auth0-client-id" description:"Auth0 Client ID for OAuth2 support" env:"AUTH0_CLIENT_ID" `
Auth0ClientSecret string ` long:"auth0-client-secret" description:"Auth0 Client Secret for OAuth2 support" env:"AUTH0_CLIENT_SECRET" `
2017-02-15 05:11:11 +00:00
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" `
2017-03-06 21:16:45 +00:00
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" `
2017-02-15 05:11:11 +00:00
Basepath string ` short:"p" long:"basepath" description:"A URL path prefix under which all chronograf routes will be mounted" env:"BASE_PATH" `
2017-04-03 20:41:05 +00:00
PrefixRoutes bool ` long:"prefix-routes" description:"Force chronograf server to require that all requests to it are prefixed with the value set in --basepath" env:"PREFIX_ROUTES" `
2017-02-15 05:11:11 +00:00
ShowVersion bool ` short:"v" long:"version" description:"Show Chronograf version info" `
BuildInfo BuildInfo
Listener net . Listener
handler http . Handler
2016-10-25 15:20:06 +00:00
}
2017-06-23 23:45:02 +00:00
// generateCustomLinks transforms CLI CustomLinks into a data structure that the client will expect
func generateCustomLinks ( links map [ string ] string ) ( [ ] CustomLink , error ) {
2017-06-23 22:12:02 +00:00
var customLinks [ ] CustomLink
2017-06-23 23:45:02 +00:00
for name , link := range links {
customLinks = append ( customLinks , CustomLink {
Name : name ,
Url : link ,
} )
}
return customLinks , nil
2017-06-23 22:12:02 +00:00
}
2017-02-23 22:17:28 +00:00
func provide ( p oauth2 . Provider , m oauth2 . Mux , ok func ( ) bool ) func ( func ( oauth2 . Provider , oauth2 . Mux ) ) {
return func ( configure func ( oauth2 . Provider , oauth2 . Mux ) ) {
if ok ( ) {
configure ( p , m )
}
}
}
2017-03-06 16:11:52 +00:00
// UseGithub validates the CLI parameters to enable github oauth support
2017-02-23 22:17:28 +00:00
func ( s * Server ) UseGithub ( ) bool {
return s . TokenSecret != "" && s . GithubClientID != "" && s . GithubClientSecret != ""
}
2017-03-06 16:11:52 +00:00
// UseGoogle validates the CLI parameters to enable google oauth support
2017-02-23 22:17:28 +00:00
func ( s * Server ) UseGoogle ( ) bool {
return s . TokenSecret != "" && s . GoogleClientID != "" && s . GoogleClientSecret != "" && s . PublicURL != ""
}
2017-03-06 16:11:52 +00:00
// UseHeroku validates the CLI parameters to enable heroku oauth support
2017-02-23 22:17:28 +00:00
func ( s * Server ) UseHeroku ( ) bool {
return s . TokenSecret != "" && s . HerokuClientID != "" && s . HerokuSecret != ""
}
2017-06-19 20:27:55 +00:00
func ( s * Server ) UseAuth0 ( ) bool {
return s . Auth0ClientID != "" && s . Auth0ClientSecret != ""
}
2017-04-06 21:45:33 +00:00
// UseGenericOAuth2 validates the CLI parameters to enable generic oauth support
func ( s * Server ) UseGenericOAuth2 ( ) bool {
return s . TokenSecret != "" && s . GenericClientID != "" &&
s . GenericClientSecret != "" && s . GenericAuthURL != "" &&
s . GenericTokenURL != ""
}
2017-02-23 22:17:28 +00:00
func ( s * Server ) githubOAuth ( logger chronograf . Logger , auth oauth2 . Authenticator ) ( oauth2 . Provider , oauth2 . Mux , func ( ) bool ) {
gh := oauth2 . Github {
ClientID : s . GithubClientID ,
ClientSecret : s . GithubClientSecret ,
Orgs : s . GithubOrgs ,
Logger : logger ,
}
2017-04-06 18:40:57 +00:00
jwt := oauth2 . NewJWT ( s . TokenSecret )
2017-05-03 23:34:05 +00:00
ghMux := oauth2 . NewAuthMux ( & gh , auth , jwt , s . Basepath , logger )
2017-02-23 22:17:28 +00:00
return & gh , ghMux , s . UseGithub
}
func ( s * Server ) googleOAuth ( logger chronograf . Logger , auth oauth2 . Authenticator ) ( oauth2 . Provider , oauth2 . Mux , func ( ) bool ) {
redirectURL := s . PublicURL + s . Basepath + "/oauth/google/callback"
google := oauth2 . Google {
ClientID : s . GoogleClientID ,
ClientSecret : s . GoogleClientSecret ,
Domains : s . GoogleDomains ,
RedirectURL : redirectURL ,
Logger : logger ,
}
2017-04-06 18:40:57 +00:00
jwt := oauth2 . NewJWT ( s . TokenSecret )
2017-05-03 23:34:05 +00:00
goMux := oauth2 . NewAuthMux ( & google , auth , jwt , s . Basepath , logger )
2017-02-23 22:17:28 +00:00
return & google , goMux , s . UseGoogle
}
func ( s * Server ) herokuOAuth ( logger chronograf . Logger , auth oauth2 . Authenticator ) ( oauth2 . Provider , oauth2 . Mux , func ( ) bool ) {
heroku := oauth2 . Heroku {
ClientID : s . HerokuClientID ,
ClientSecret : s . HerokuSecret ,
Organizations : s . HerokuOrganizations ,
Logger : logger ,
}
2017-04-06 18:40:57 +00:00
jwt := oauth2 . NewJWT ( s . TokenSecret )
2017-05-03 23:34:05 +00:00
hMux := oauth2 . NewAuthMux ( & heroku , auth , jwt , s . Basepath , logger )
2017-02-23 22:17:28 +00:00
return & heroku , hMux , s . UseHeroku
2016-10-25 15:20:06 +00:00
}
2017-04-06 21:45:33 +00:00
func ( s * Server ) genericOAuth ( logger chronograf . Logger , auth oauth2 . Authenticator ) ( oauth2 . Provider , oauth2 . Mux , func ( ) bool ) {
gen := oauth2 . Generic {
2017-04-07 19:58:35 +00:00
PageName : s . GenericName ,
ClientID : s . GenericClientID ,
ClientSecret : s . GenericClientSecret ,
RequiredScopes : s . GenericScopes ,
Domains : s . GenericDomains ,
2017-06-18 02:40:07 +00:00
RedirectURL : s . genericRedirectURL ( ) ,
2017-04-07 19:58:35 +00:00
AuthURL : s . GenericAuthURL ,
TokenURL : s . GenericTokenURL ,
APIURL : s . GenericAPIURL ,
Logger : logger ,
2017-04-06 21:45:33 +00:00
}
jwt := oauth2 . NewJWT ( s . TokenSecret )
2017-05-03 23:34:05 +00:00
genMux := oauth2 . NewAuthMux ( & gen , auth , jwt , s . Basepath , logger )
2017-04-06 21:45:33 +00:00
return & gen , genMux , s . UseGenericOAuth2
}
2017-06-19 20:27:55 +00:00
func ( s * Server ) auth0OAuth ( logger chronograf . Logger , auth oauth2 . Authenticator ) ( oauth2 . Provider , oauth2 . Mux , func ( ) bool ) {
redirectPath := path . Join ( s . Basepath , "oauth" , "auth0" , "callback" )
redirectURL , err := url . Parse ( s . PublicURL )
if err != nil {
logger . Error ( "Error parsing public URL: err:" , err )
return & oauth2 . Auth0 { } , & oauth2 . AuthMux { } , func ( ) bool { return false }
}
redirectURL . Path = redirectPath
auth0 , err := oauth2 . NewAuth0 ( s . Auth0Domain , s . Auth0ClientID , s . Auth0ClientSecret , redirectURL . String ( ) , logger )
jwt := oauth2 . NewJWT ( s . TokenSecret )
genMux := oauth2 . NewAuthMux ( & auth0 , auth , jwt , s . Basepath , logger )
if err != nil {
logger . Error ( "Error parsing Auth0 domain: err:" , err )
return & auth0 , genMux , func ( ) bool { return false }
}
return & auth0 , genMux , s . UseAuth0
}
2017-06-18 02:40:07 +00:00
func ( s * Server ) genericRedirectURL ( ) string {
if s . PublicURL == "" {
return ""
}
genericName := "generic"
if s . GenericName != "" {
genericName = s . GenericName
}
2017-06-21 04:19:21 +00:00
publicURL , err := url . Parse ( s . PublicURL )
if err != nil {
return ""
}
publicURL . Path = path . Join ( publicURL . Path , s . Basepath , "oauth" , genericName , "callback" )
return publicURL . String ( )
2017-06-18 02:40:07 +00:00
}
2016-11-19 17:41:06 +00:00
// BuildInfo is sent to the usage client to track versions and commits
2016-10-24 17:08:36 +00:00
type BuildInfo struct {
Version string
Commit string
}
2016-10-28 16:27:06 +00:00
func ( s * Server ) useAuth ( ) bool {
2017-06-19 20:27:55 +00:00
return s . UseGithub ( ) || s . UseGoogle ( ) || s . UseHeroku ( ) || s . UseGenericOAuth2 ( ) || s . UseAuth0 ( )
2016-10-28 16:27:06 +00:00
}
2016-10-25 15:20:06 +00:00
2017-02-13 03:33:27 +00:00
func ( s * Server ) useTLS ( ) bool {
return s . Cert != ""
}
2017-02-13 03:48:12 +00:00
// NewListener will an http or https listener depending useTLS()
2017-02-13 03:33:27 +00:00
func ( s * Server ) NewListener ( ) ( net . Listener , error ) {
addr := net . JoinHostPort ( s . Host , strconv . Itoa ( s . Port ) )
if ! s . useTLS ( ) {
listener , err := net . Listen ( "tcp" , addr )
if err != nil {
return nil , err
}
return listener , nil
}
// If no key specified, therefore, we assume it is in the cert
if s . Key == "" {
s . Key = s . Cert
}
cert , err := tls . LoadX509KeyPair ( string ( s . Cert ) , string ( s . Key ) )
if err != nil {
return nil , err
}
listener , err := tls . Listen ( "tcp" , addr , & tls . Config {
Certificates : [ ] tls . Certificate { cert } ,
} )
if err != nil {
return nil , err
}
return listener , nil
}
2016-10-28 16:27:06 +00:00
// Serve starts and runs the chronograf server
Introduce ability to edit a dashboard cell
* Correct documentation for dashboards
* Exclude .git and use 'make run-dev' in 'make continuous'
* Fix dashboard deletion bug where id serialization was wrong
* Commence creation of overlay technology, add autoRefresh props to DashboardPage
* Enhance overlay magnitude of overlay technology
* Add confirm buttons to overlay technology
* Refactor ResizeContainer to accommodate arbitrary containers
* Refactor ResizeContainer to require explicit ResizeTop and ResizeBottom for clarity
* Add markup and styles for OverlayControls
* CellEditorOverlay needs a larger minimum bottom height to accommodate more things
* Revert Visualization to not use ResizeTop or flex-box
* Remove TODO and move to issue
* Refactor CellEditorOverlay to allow selection of graph type
* Style Overlay controls, move confirm buttons to own stylesheet
* Fix toggle buttons in overlay so active is actually active
* Block user-select on a few UI items
* Update cell query shape to support Visualization and LayoutRenderer
* Code cleanup
* Repair fixture schema; update props for affected components
* Wired up selectedGraphType and activeQueryID in CellEditorOverlay
* Wire up chooseMeasurements in QueryBuilder
Pass queryActions into QueryBuilder so that DataExplorer can provide
actionCreators and CellEditorOverlay can provide functions that
modify its component state
* semicolon cleanup
* Bind all queryModifier actions to component state with a stateReducer
* Overlay Technologies™ can add and delete a query from a cell
* Semicolon cleanup
* Add conversion of InfluxQL to QueryConfig for dashboards
* Update go deps to add influxdb at af72d9b0e4ebe95be30e89b160f43eabaf0529ed
* Updated docs for dashboard query config
* Update CHANGELOG to mention InfluxQL to QueryConfig
* Make reducer’s name more specific for clarity
* Remove 'table' as graphType
* Make graph renaming prettier
* Remove duplicate DashboardQuery in swagger.json
* Fix swagger to include name and links for Cell
* Refactor CellEditorOverlay to enable graph type selection
* Add link.self to all Dashboard cells; add bolt migrations
* Make dash graph names only hover on contents
* Consolidate timeRange format patterns, clean up
* Add cell endpoints to dashboards
* Include Line + Stat in Visualization Type list
* Add cell link to dashboards
* Enable step plot and stacked graph in Visualization
* Overlay Technologies are summonable and dismissable
* OverlayTechnologies saves changes to a cell
* Convert NameableGraph to createClass for state
This was converted from a pure function to encapsulate the state of the
buttons. An attempt was made previously to store this state in Redux,
but it proved too convoluted with the current state of the reducers for
cells and dashboards. Another effort must take place to separate a cell
reducer to manage the state of an individual cell in Redux in order for
this state to be sanely kept in Redux as well.
For the time being, this state is being kept in the component for the
sake of expeditiousness, since this is needed for Dashboards to be
released. A refactor of this will occur later.
* Cells should contain a links key in server response
* Clean up console logs
* Use live data instead of a cellQuery fixture
* Update docs for dashboard creation
* DB and RP are already present in the Command field
* Fix LayoutRenderer’s understanding of query schema
* Return a new object, rather that mutate in place
* Visualization doesn’t use activeQueryID
* Selected is an object, not a string
* QueryBuilder refactored to use query index instead of query id
* CellEditorOverlay refactored to use query index instead of query id
* ConfirmButtons doesn’t need to act on an item
* Rename functions to follow convention
* Queries are no longer guaranteed to have ids
* Omit WHERE and GROUP BY clauses when saving query
* Select new query on add in OverlayTechnologies
* Add click outside to dash graph menu, style menu also
* Change context menu from ... to a caret
More consistent with the rest of the UI, better affordance
* Hide graph context menu in presentation mode
Don’t want people editing a dashboard from presentation mode
* Move graph refreshing spinner so it does not overlap with context menu
* Wire up Cell Menu to Overlay Technologies
* Correct empty dashboard type
* Refactor dashboard spec fixtures
* Test syncDashboardCell reducer
* Remove Delete button from graph dropdown menu (for now)
* Update changelog
2017-03-24 00:12:33 +00:00
func ( s * Server ) Serve ( ctx context . Context ) error {
2016-10-24 17:08:36 +00:00
logger := clog . New ( clog . ParseLevel ( s . LogLevel ) )
2017-03-30 16:48:04 +00:00
layoutBuilder := & MultiLayoutBuilder {
Logger : logger ,
UUID : & uuid . V4 { } ,
CannedPath : s . CannedPath ,
}
sourcesBuilder := & MultiSourceBuilder {
InfluxDBURL : s . InfluxDBURL ,
InfluxDBUsername : s . InfluxDBUsername ,
InfluxDBPassword : s . InfluxDBPassword ,
}
kapacitorBuilder := & MultiKapacitorBuilder {
KapacitorURL : s . KapacitorURL ,
KapacitorUsername : s . KapacitorUsername ,
KapacitorPassword : s . KapacitorPassword ,
}
service := openService ( ctx , s . BoltPath , layoutBuilder , sourcesBuilder , kapacitorBuilder , logger , s . useAuth ( ) )
2017-01-27 21:47:46 +00:00
basepath = s . Basepath
2017-05-03 19:14:44 +00:00
if basepath != "" && s . PrefixRoutes == false {
logger .
WithField ( "component" , "server" ) .
Info ( "Note: you may want to use --prefix-routes with --basepath. Try `./chronograf --help` for more info." )
}
2017-02-13 03:33:27 +00:00
2017-02-23 22:17:28 +00:00
providerFuncs := [ ] func ( func ( oauth2 . Provider , oauth2 . Mux ) ) { }
2017-04-06 18:40:57 +00:00
auth := oauth2 . NewCookieJWT ( s . TokenSecret , s . AuthDuration )
providerFuncs = append ( providerFuncs , provide ( s . githubOAuth ( logger , auth ) ) )
providerFuncs = append ( providerFuncs , provide ( s . googleOAuth ( logger , auth ) ) )
providerFuncs = append ( providerFuncs , provide ( s . herokuOAuth ( logger , auth ) ) )
2017-04-06 21:45:33 +00:00
providerFuncs = append ( providerFuncs , provide ( s . genericOAuth ( logger , auth ) ) )
2017-06-19 20:27:55 +00:00
providerFuncs = append ( providerFuncs , provide ( s . auth0OAuth ( logger , auth ) ) )
2017-02-23 22:17:28 +00:00
2017-06-23 23:45:02 +00:00
customLinks , err := generateCustomLinks ( s . CustomLinks )
if err != nil {
return err
}
2017-06-23 22:12:02 +00:00
2016-10-25 15:20:06 +00:00
s . handler = NewMux ( MuxOpts {
2017-02-23 22:17:28 +00:00
Develop : s . Develop ,
2017-04-06 18:40:57 +00:00
Auth : auth ,
2017-02-23 22:17:28 +00:00
Logger : logger ,
UseAuth : s . useAuth ( ) ,
ProviderFuncs : providerFuncs ,
2017-03-31 20:14:46 +00:00
Basepath : basepath ,
2017-04-03 20:41:05 +00:00
PrefixRoutes : s . PrefixRoutes ,
2017-06-13 20:36:53 +00:00
StatusFeedURL : s . StatusFeedURL ,
2017-06-23 22:12:02 +00:00
CustomLinks : customLinks ,
2016-10-28 16:27:06 +00:00
} , service )
2016-10-25 15:20:06 +00:00
2017-02-13 03:33:27 +00:00
// Add chronograf's version header to all requests
2017-01-13 23:10:50 +00:00
s . handler = Version ( s . BuildInfo . Version , s . handler )
2017-02-13 03:33:27 +00:00
if s . useTLS ( ) {
// Add HSTS to instruct all browsers to change from http to https
s . handler = HSTS ( s . handler )
}
listener , err := s . NewListener ( )
2016-10-25 15:20:06 +00:00
if err != nil {
2016-10-28 16:27:06 +00:00
logger .
WithField ( "component" , "server" ) .
Error ( err )
2016-10-25 15:20:06 +00:00
return err
}
2017-02-13 03:33:27 +00:00
s . Listener = listener
2016-10-25 15:20:06 +00:00
2017-03-06 21:16:45 +00:00
// Using a log writer for http server logging
w := logger . Writer ( )
defer w . Close ( )
stdLog := log . New ( w , "" , 0 )
// TODO: Remove graceful when changing to go 1.8
httpServer := & graceful . Server {
Server : & http . Server {
ErrorLog : stdLog ,
Handler : s . handler ,
} ,
Logger : stdLog ,
TCPKeepAlive : 5 * time . Second ,
}
2016-10-25 15:20:06 +00:00
httpServer . SetKeepAlivesEnabled ( true )
2016-10-24 17:08:36 +00:00
if ! s . ReportingDisabled {
go reportUsageStats ( s . BuildInfo , logger )
}
2017-02-13 03:33:27 +00:00
scheme := "http"
if s . useTLS ( ) {
scheme = "https"
}
2016-10-28 16:27:06 +00:00
logger .
WithField ( "component" , "server" ) .
2017-02-13 03:33:27 +00:00
Info ( "Serving chronograf at " , scheme , "://" , s . Listener . Addr ( ) )
2016-10-28 16:27:06 +00:00
if err := httpServer . Serve ( s . Listener ) ; err != nil {
logger .
WithField ( "component" , "server" ) .
Error ( err )
return err
}
logger .
WithField ( "component" , "server" ) .
2017-02-13 03:33:27 +00:00
Info ( "Stopped serving chronograf at " , scheme , "://" , s . Listener . Addr ( ) )
2016-10-28 16:27:06 +00:00
2016-10-25 15:20:06 +00:00
return nil
}
2016-10-28 16:27:06 +00:00
2017-03-30 16:48:04 +00:00
func openService ( ctx context . Context , boltPath string , lBuilder LayoutBuilder , sBuilder SourcesBuilder , kapBuilder KapacitorBuilder , logger chronograf . Logger , useAuth bool ) Service {
2016-10-28 16:27:06 +00:00
db := bolt . NewClient ( )
db . Path = boltPath
Introduce ability to edit a dashboard cell
* Correct documentation for dashboards
* Exclude .git and use 'make run-dev' in 'make continuous'
* Fix dashboard deletion bug where id serialization was wrong
* Commence creation of overlay technology, add autoRefresh props to DashboardPage
* Enhance overlay magnitude of overlay technology
* Add confirm buttons to overlay technology
* Refactor ResizeContainer to accommodate arbitrary containers
* Refactor ResizeContainer to require explicit ResizeTop and ResizeBottom for clarity
* Add markup and styles for OverlayControls
* CellEditorOverlay needs a larger minimum bottom height to accommodate more things
* Revert Visualization to not use ResizeTop or flex-box
* Remove TODO and move to issue
* Refactor CellEditorOverlay to allow selection of graph type
* Style Overlay controls, move confirm buttons to own stylesheet
* Fix toggle buttons in overlay so active is actually active
* Block user-select on a few UI items
* Update cell query shape to support Visualization and LayoutRenderer
* Code cleanup
* Repair fixture schema; update props for affected components
* Wired up selectedGraphType and activeQueryID in CellEditorOverlay
* Wire up chooseMeasurements in QueryBuilder
Pass queryActions into QueryBuilder so that DataExplorer can provide
actionCreators and CellEditorOverlay can provide functions that
modify its component state
* semicolon cleanup
* Bind all queryModifier actions to component state with a stateReducer
* Overlay Technologies™ can add and delete a query from a cell
* Semicolon cleanup
* Add conversion of InfluxQL to QueryConfig for dashboards
* Update go deps to add influxdb at af72d9b0e4ebe95be30e89b160f43eabaf0529ed
* Updated docs for dashboard query config
* Update CHANGELOG to mention InfluxQL to QueryConfig
* Make reducer’s name more specific for clarity
* Remove 'table' as graphType
* Make graph renaming prettier
* Remove duplicate DashboardQuery in swagger.json
* Fix swagger to include name and links for Cell
* Refactor CellEditorOverlay to enable graph type selection
* Add link.self to all Dashboard cells; add bolt migrations
* Make dash graph names only hover on contents
* Consolidate timeRange format patterns, clean up
* Add cell endpoints to dashboards
* Include Line + Stat in Visualization Type list
* Add cell link to dashboards
* Enable step plot and stacked graph in Visualization
* Overlay Technologies are summonable and dismissable
* OverlayTechnologies saves changes to a cell
* Convert NameableGraph to createClass for state
This was converted from a pure function to encapsulate the state of the
buttons. An attempt was made previously to store this state in Redux,
but it proved too convoluted with the current state of the reducers for
cells and dashboards. Another effort must take place to separate a cell
reducer to manage the state of an individual cell in Redux in order for
this state to be sanely kept in Redux as well.
For the time being, this state is being kept in the component for the
sake of expeditiousness, since this is needed for Dashboards to be
released. A refactor of this will occur later.
* Cells should contain a links key in server response
* Clean up console logs
* Use live data instead of a cellQuery fixture
* Update docs for dashboard creation
* DB and RP are already present in the Command field
* Fix LayoutRenderer’s understanding of query schema
* Return a new object, rather that mutate in place
* Visualization doesn’t use activeQueryID
* Selected is an object, not a string
* QueryBuilder refactored to use query index instead of query id
* CellEditorOverlay refactored to use query index instead of query id
* ConfirmButtons doesn’t need to act on an item
* Rename functions to follow convention
* Queries are no longer guaranteed to have ids
* Omit WHERE and GROUP BY clauses when saving query
* Select new query on add in OverlayTechnologies
* Add click outside to dash graph menu, style menu also
* Change context menu from ... to a caret
More consistent with the rest of the UI, better affordance
* Hide graph context menu in presentation mode
Don’t want people editing a dashboard from presentation mode
* Move graph refreshing spinner so it does not overlap with context menu
* Wire up Cell Menu to Overlay Technologies
* Correct empty dashboard type
* Refactor dashboard spec fixtures
* Test syncDashboardCell reducer
* Remove Delete button from graph dropdown menu (for now)
* Update changelog
2017-03-24 00:12:33 +00:00
if err := db . Open ( ctx ) ; err != nil {
2016-10-28 16:27:06 +00:00
logger .
WithField ( "component" , "boltstore" ) .
2017-03-06 21:16:45 +00:00
Error ( "Unable to open boltdb; is there a chronograf already running? " , err )
os . Exit ( 1 )
2016-10-28 16:27:06 +00:00
}
2017-03-30 16:48:04 +00:00
layouts , err := lBuilder . Build ( db . LayoutStore )
if err != nil {
logger .
WithField ( "component" , "LayoutStore" ) .
Error ( "Unable to construct a MultiLayoutStore" , err )
os . Exit ( 1 )
2016-11-15 01:07:38 +00:00
}
2017-03-30 16:48:04 +00:00
sources , err := sBuilder . Build ( db . SourcesStore )
if err != nil {
logger .
WithField ( "component" , "SourcesStore" ) .
Error ( "Unable to construct a MultiSourcesStore" , err )
os . Exit ( 1 )
}
kapacitors , err := kapBuilder . Build ( db . ServersStore )
if err != nil {
logger .
WithField ( "component" , "KapacitorStore" ) .
Error ( "Unable to construct a MultiKapacitorStore" , err )
os . Exit ( 1 )
2016-10-28 16:27:06 +00:00
}
return Service {
2017-02-24 03:54:20 +00:00
TimeSeriesClient : & InfluxClient { } ,
2017-03-30 16:48:04 +00:00
SourcesStore : sources ,
ServersStore : kapacitors ,
2017-02-24 03:54:20 +00:00
UsersStore : db . UsersStore ,
LayoutStore : layouts ,
DashboardsStore : db . DashboardsStore ,
Logger : logger ,
UseAuth : useAuth ,
2017-03-22 20:27:36 +00:00
Databases : & influx . Client { Logger : logger } ,
2016-10-28 16:27:06 +00:00
}
}
2016-10-24 17:08:36 +00:00
// reportUsageStats starts periodic server reporting.
func reportUsageStats ( bi BuildInfo , logger chronograf . Logger ) {
rand . Seed ( time . Now ( ) . UTC ( ) . UnixNano ( ) )
serverID := strconv . FormatUint ( uint64 ( rand . Int63 ( ) ) , 10 )
reporter := client . New ( "" )
2017-03-17 14:59:52 +00:00
values := client . Values {
"os" : runtime . GOOS ,
"arch" : runtime . GOARCH ,
"version" : bi . Version ,
"cluster_id" : serverID ,
"uptime" : time . Since ( startTime ) . Seconds ( ) ,
2016-10-24 17:08:36 +00:00
}
l := logger . WithField ( "component" , "usage" ) .
WithField ( "reporting_addr" , reporter . URL ) .
WithField ( "freq" , "24h" ) .
2016-12-19 21:29:33 +00:00
WithField ( "stats" , "os,arch,version,cluster_id,uptime" )
2016-10-24 17:08:36 +00:00
l . Info ( "Reporting usage stats" )
2017-03-17 14:59:52 +00:00
_ , _ = reporter . Save ( clientUsage ( values ) )
2016-10-24 17:08:36 +00:00
ticker := time . NewTicker ( 24 * time . Hour )
defer ticker . Stop ( )
for {
2016-12-20 20:59:56 +00:00
<- ticker . C
2017-03-17 14:59:52 +00:00
values [ "uptime" ] = time . Since ( startTime ) . Seconds ( )
2016-12-20 20:59:56 +00:00
l . Debug ( "Reporting usage stats" )
2017-03-17 14:59:52 +00:00
go reporter . Save ( clientUsage ( values ) )
}
}
func clientUsage ( values client . Values ) * client . Usage {
return & client . Usage {
Product : "chronograf-ng" ,
Data : [ ] client . UsageData {
{
Values : values ,
} ,
} ,
2016-10-24 17:08:36 +00:00
}
}