2016-10-25 15:20:06 +00:00
package server
import (
2016-10-24 17:08:36 +00:00
"math/rand"
2016-10-25 15:20:06 +00:00
"net"
"net/http"
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"
"github.com/influxdata/chronograf/canned"
"github.com/influxdata/chronograf/influx"
"github.com/influxdata/chronograf/layouts"
clog "github.com/influxdata/chronograf/log"
"github.com/influxdata/chronograf/uuid"
2016-10-24 17:08:36 +00:00
client "github.com/influxdata/usage-client/v1"
2016-10-25 15:20:06 +00:00
"github.com/tylerb/graceful"
)
// Server for the chronograf API
type Server struct {
2016-11-08 02:50:06 +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
2016-11-07 17:09:51 +00:00
/ * TODO : add in support for TLS
2016-10-25 15:20:06 +00:00
TLSHost string ` long:"tls-host" description:"the IP to listen on for tls, when not specified it's the same as --host" env:"TLS_HOST" `
TLSPort int ` long:"tls-port" description:"the port to listen on for secure connections, defaults to a random value" env:"TLS_PORT" `
TLSCertificate flags . Filename ` long:"tls-certificate" description:"the certificate to use for secure connections" env:"TLS_CERTIFICATE" `
TLSCertificateKey flags . Filename ` long:"tls-key" description:"the private key to use for secure conections" env:"TLS_PRIVATE_KEY" `
2016-11-07 17:09:51 +00:00
* /
2016-10-25 15:20:06 +00:00
Develop bool ` short:"d" long:"develop" description:"Run server in develop mode." `
2016-11-08 02:50:06 +00:00
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" `
2016-11-07 23:23:21 +00:00
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" `
2016-10-25 15:20:06 +00:00
TokenSecret string ` short:"t" long:"token-secret" description:"Secret to sign tokens" env:"TOKEN_SECRET" `
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" `
2016-10-24 17:08:36 +00:00
ReportingDisabled bool ` short:"r" long:"reporting-disabled" description:"Disable reporting of usage stats (os,arch,version,cluster_id) once every 24hr" env:"REPORTING_DISABLED" `
LogLevel string ` short:"l" long:"log-level" value-name:"choice" choice:"debug" choice:"info" choice:"warn" choice:"error" choice:"fatal" choice:"panic" default:"info" description:"Set the logging level" env:"LOG_LEVEL" `
2016-11-09 21:24:46 +00:00
ShowVersion bool ` short:"v" long:"version" description:"Show Chronograf version info" `
BuildInfo BuildInfo
2016-10-25 15:20:06 +00:00
2016-10-28 16:27:06 +00:00
Listener net . Listener
handler http . Handler
2016-10-25 15:20:06 +00:00
}
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 {
return s . TokenSecret != "" && s . GithubClientID != "" && s . GithubClientSecret != ""
}
2016-10-25 15:20:06 +00:00
2016-10-28 16:27:06 +00:00
// Serve starts and runs the chronograf server
func ( s * Server ) Serve ( ) error {
2016-10-24 17:08:36 +00:00
logger := clog . New ( clog . ParseLevel ( s . LogLevel ) )
service := openService ( s . BoltPath , s . CannedPath , logger )
2016-10-25 15:20:06 +00:00
s . handler = NewMux ( MuxOpts {
Develop : s . Develop ,
TokenSecret : s . TokenSecret ,
GithubClientID : s . GithubClientID ,
GithubClientSecret : s . GithubClientSecret ,
Logger : logger ,
2016-10-28 16:27:06 +00:00
UseAuth : s . useAuth ( ) ,
} , service )
2016-10-25 15:20:06 +00:00
2016-10-28 16:27:06 +00:00
var err error
s . Listener , err = net . Listen ( "tcp" , net . JoinHostPort ( s . Host , strconv . Itoa ( s . Port ) ) )
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
}
httpServer := & graceful . Server { Server : new ( http . Server ) }
httpServer . SetKeepAlivesEnabled ( true )
2016-10-28 16:27:06 +00:00
httpServer . TCPKeepAlive = 1 * time . Minute
2016-10-25 15:20:06 +00:00
httpServer . Handler = s . handler
2016-10-24 17:08:36 +00:00
if ! s . ReportingDisabled {
go reportUsageStats ( s . BuildInfo , logger )
}
2016-10-28 16:27:06 +00:00
logger .
WithField ( "component" , "server" ) .
2016-10-24 17:08:36 +00:00
Info ( "Serving chronograf at http://" , 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" ) .
2016-10-24 17:08:36 +00:00
Info ( "Stopped serving chronograf at http://" , 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
2016-10-24 17:08:36 +00:00
func openService ( boltPath , cannedPath string , logger chronograf . Logger ) Service {
2016-10-28 16:27:06 +00:00
db := bolt . NewClient ( )
db . Path = boltPath
if err := db . Open ( ) ; err != nil {
logger .
WithField ( "component" , "boltstore" ) .
2016-11-08 02:50:06 +00:00
Fatal ( "Unable to open boltdb; is there a chronograf already running? " , err )
2016-10-28 16:27:06 +00:00
}
2016-11-07 16:10:26 +00:00
apps := canned . NewApps ( cannedPath , & uuid . V4 { } , logger )
2016-10-28 16:27:06 +00:00
// 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 ,
2016-11-04 00:44:28 +00:00
AlertRulesStore : db . AlertsStore ,
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 ( "" )
u := & client . Usage {
Product : "chronograf" ,
Data : [ ] client . UsageData {
{
Values : client . Values {
"os" : runtime . GOOS ,
"arch" : runtime . GOARCH ,
"version" : bi . Version ,
"cluster_id" : serverID ,
} ,
} ,
} ,
}
l := logger . WithField ( "component" , "usage" ) .
WithField ( "reporting_addr" , reporter . URL ) .
WithField ( "freq" , "24h" ) .
WithField ( "stats" , "os,arch,version,cluster_id" )
l . Info ( "Reporting usage stats" )
reporter . Save ( u )
ticker := time . NewTicker ( 24 * time . Hour )
defer ticker . Stop ( )
for {
select {
case <- ticker . C :
l . Debug ( "Reporting usage stats" )
go reporter . Save ( u )
}
}
}