influxdb/cmd/influxd/run.go

364 lines
9.9 KiB
Go
Raw Normal View History

package main
import (
"flag"
2015-01-29 23:07:58 +00:00
"io"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/influxdb/influxdb"
"github.com/influxdb/influxdb/collectd"
"github.com/influxdb/influxdb/graphite"
"github.com/influxdb/influxdb/httpd"
"github.com/influxdb/influxdb/messaging"
)
// execRun runs the "run" command.
func execRun(args []string) {
// Parse command flags.
fs := flag.NewFlagSet("", flag.ExitOnError)
var (
configPath = fs.String("config", "", "")
pidPath = fs.String("pidfile", "", "")
hostname = fs.String("hostname", "", "")
join = fs.String("join", "", "")
)
fs.Usage = printRunUsage
fs.Parse(args)
2014-12-30 22:46:50 +00:00
// Print sweet InfluxDB logo and write the process id to file.
log.Print(logo)
2015-01-29 23:07:58 +00:00
log.SetPrefix(`[influxd] `)
log.SetFlags(log.LstdFlags)
2014-12-30 22:46:50 +00:00
writePIDFile(*pidPath)
2014-12-31 19:42:53 +00:00
// Parse the configuration and determine if a broker and/or server exist.
2014-12-30 22:46:50 +00:00
config := parseConfig(*configPath, *hostname)
configExists := *configPath != ""
initializing := !fileExists(config.BrokerDir()) && !fileExists(config.DataDir())
2015-01-29 22:11:10 +00:00
// Create a logging writer.
logWriter := os.Stderr
if config.Logging.File != "" {
var err error
logWriter, err = os.OpenFile(config.Logging.File, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0660)
if err != nil {
log.Fatalf("unable to open log file %s: %s", config.Logging.File, err.Error())
}
2015-01-29 22:11:10 +00:00
}
log.SetOutput(logWriter)
2015-01-29 22:11:10 +00:00
// Parse join urls from the --join flag.
var joinURLs []*url.URL
if *join == "" {
joinURLs = parseURLs(config.JoinURLs())
} else {
joinURLs = parseURLs(*join)
}
2014-12-31 19:42:53 +00:00
2015-01-29 23:07:58 +00:00
// Mark the start of the log.
log.Printf("influxd starting up")
// Open broker, initialize or join as necessary.
2015-01-29 23:07:58 +00:00
b := openBroker(config.BrokerDir(), config.BrokerURL(), initializing, joinURLs, logWriter)
2014-12-30 22:46:50 +00:00
// Start the broker handler.
var h *Handler
if b != nil {
2014-12-31 19:42:53 +00:00
h = &Handler{brokerHandler: messaging.NewHandler(b)}
go func() { log.Fatal(http.ListenAndServe(config.BrokerAddr(), h)) }()
2015-01-10 16:08:00 +00:00
log.Printf("broker listening on %s", config.BrokerAddr())
2014-12-31 19:42:53 +00:00
}
2014-12-30 22:46:50 +00:00
// Open server, initialize or join as necessary.
2015-01-29 23:07:58 +00:00
s := openServer(config.DataDir(), config.DataURL(), b, initializing, configExists, joinURLs, logWriter)
2014-12-31 19:42:53 +00:00
2015-01-10 16:08:00 +00:00
// Start the server handler. Attach to broker if listening on the same port.
if s != nil {
2015-01-22 01:13:51 +00:00
sh := httpd.NewHandler(s, config.Authentication.Enabled, version)
if h != nil && config.BrokerAddr() == config.DataAddr() {
2014-12-31 19:42:53 +00:00
h.serverHandler = sh
} else {
go func() { log.Fatal(http.ListenAndServe(config.DataAddr(), sh)) }()
2014-12-31 19:42:53 +00:00
}
2015-01-10 16:08:00 +00:00
log.Printf("data node #%d listening on %s", s.ID(), config.DataAddr())
2015-01-02 16:09:11 +00:00
// Spin up the collectd server
if config.Collectd.Enabled {
c := config.Collectd
cs := collectd.NewServer(s, c.TypesDB)
cs.Database = c.Database
err := collectd.ListenAndServe(cs, c.ConnectionString(config.BindAddress))
if err != nil {
log.Printf("failed to start collectd Server: %v\n", err.Error())
}
}
// Spin up any Graphite servers
for _, c := range config.Graphites {
if !c.Enabled {
continue
}
2015-01-02 16:09:11 +00:00
// Configure Graphite parsing.
parser := graphite.NewParser()
parser.Separator = c.NameSeparatorString()
2015-01-08 20:23:48 +00:00
parser.LastEnabled = c.LastEnabled()
2015-01-07 08:04:56 +00:00
// Start the relevant server.
if strings.ToLower(c.Protocol) == "tcp" {
g := graphite.NewTCPServer(parser, s)
g.Database = c.Database
err := g.ListenAndServe(c.ConnectionString(config.BindAddress))
if err != nil {
log.Printf("failed to start TCP Graphite Server: %v\n", err.Error())
}
2015-01-08 19:10:38 +00:00
} else if strings.ToLower(c.Protocol) == "udp" {
g := graphite.NewUDPServer(parser, s)
g.Database = c.Database
err := g.ListenAndServe(c.ConnectionString(config.BindAddress))
if err != nil {
log.Printf("failed to start UDP Graphite Server: %v\n", err.Error())
}
2015-01-08 19:10:38 +00:00
} else {
2015-01-11 23:14:22 +00:00
log.Fatalf("unrecognized Graphite Server prototcol %s", c.Protocol)
}
2015-01-02 16:09:11 +00:00
}
2014-12-31 19:42:53 +00:00
}
2014-12-30 22:46:50 +00:00
// Wait indefinitely.
<-(chan struct{})(nil)
}
// write the current process id to a file specified by path.
func writePIDFile(path string) {
if path == "" {
return
}
2015-01-27 02:33:06 +00:00
// Ensure the required directory structure exists.
err := os.MkdirAll(filepath.Dir(path), 0700)
if err != nil {
log.Fatal(err)
}
2014-12-30 22:46:50 +00:00
// Retrieve the PID and write it.
pid := strconv.Itoa(os.Getpid())
if err := ioutil.WriteFile(path, []byte(pid), 0644); err != nil {
log.Fatal(err)
}
}
// parses the configuration from a given path. Sets overrides as needed.
func parseConfig(path, hostname string) *Config {
2015-01-15 20:42:56 +00:00
if path == "" {
log.Println("No config provided, using default settings")
2015-01-15 20:42:56 +00:00
return NewConfig()
}
// Parse configuration.
2014-12-30 22:46:50 +00:00
config, err := ParseConfigFile(path)
2015-01-15 20:42:56 +00:00
if err != nil {
log.Fatalf("config: %s", err)
}
// Override config properties.
2014-12-30 22:46:50 +00:00
if hostname != "" {
config.Hostname = hostname
}
2014-12-30 22:46:50 +00:00
return config
}
// creates and initializes a broker.
2015-01-29 23:07:58 +00:00
func openBroker(path string, u *url.URL, initializing bool, joinURLs []*url.URL, w io.Writer) *messaging.Broker {
// Ignore if there's no existing broker and we're not initializing or joining.
if !fileExists(path) && !initializing && len(joinURLs) == 0 {
return nil
}
// Create broker.
2014-12-30 22:46:50 +00:00
b := messaging.NewBroker()
2015-01-29 23:07:58 +00:00
b.SetLogOutput(w)
if err := b.Open(path, u); err != nil {
2014-12-31 19:42:53 +00:00
log.Fatalf("failed to open broker: %s", err)
}
// If this is a new broker then we can initialie two ways:
// 1) Start a brand new cluster.
// 2) Join an existing cluster.
if initializing {
if len(joinURLs) == 0 {
initializeBroker(b)
} else {
joinBroker(b, joinURLs)
}
}
2014-12-30 22:46:50 +00:00
return b
}
// initializes a new broker.
func initializeBroker(b *messaging.Broker) {
if err := b.Initialize(); err != nil {
log.Fatalf("initialize: %s", err)
}
}
// joins a broker to an existing cluster.
func joinBroker(b *messaging.Broker, joinURLs []*url.URL) {
// Attempts to join each server until successful.
for _, u := range joinURLs {
if err := b.Join(u); err != nil {
log.Printf("join: failed to connect to broker: %s: %s", u, err)
} else {
log.Printf("join: connected broker to %s", u)
return
}
}
log.Fatalf("join: failed to connect broker to any specified server")
}
// creates and initializes a server.
2015-01-29 23:07:58 +00:00
func openServer(path string, u *url.URL, b *messaging.Broker, initializing, configExists bool, joinURLs []*url.URL, w io.Writer) *influxdb.Server {
// Ignore if there's no existing server and we're not initializing or joining.
if !fileExists(path) && !initializing && len(joinURLs) == 0 {
return nil
}
// Create and open the server.
2015-01-22 01:13:51 +00:00
s := influxdb.NewServer()
2015-01-29 23:07:58 +00:00
s.SetLogOutput(w)
2014-12-30 22:46:50 +00:00
if err := s.Open(path); err != nil {
2014-12-31 21:47:10 +00:00
log.Fatalf("failed to open data server: %v", err.Error())
}
// If the server is uninitialized then initialize or join it.
if initializing {
if len(joinURLs) == 0 {
2015-01-29 23:07:58 +00:00
initializeServer(s, b, w)
} else {
joinServer(s, u, joinURLs)
2015-01-29 23:07:58 +00:00
openServerClient(s, joinURLs, w)
}
} else if !configExists {
// We are spining up a server that has no config,
// but already has an initialized data directory
joinURLs = []*url.URL{b.URL()}
2015-01-29 23:07:58 +00:00
openServerClient(s, joinURLs, w)
} else {
if len(joinURLs) == 0 {
// If a config exists, but no joinUrls are specified, fall back to the broker URL
// TODO: Make sure we have a leader, and then spin up the server
joinURLs = []*url.URL{b.URL()}
}
2015-01-29 23:07:58 +00:00
openServerClient(s, joinURLs, w)
}
2014-12-31 19:42:53 +00:00
return s
}
2014-12-31 19:42:53 +00:00
// initializes a new server that does not yet have an ID.
2015-01-29 23:07:58 +00:00
func initializeServer(s *influxdb.Server, b *messaging.Broker, w io.Writer) {
// TODO: Create replica using the messaging client.
2014-12-31 19:55:27 +00:00
2014-12-31 19:42:53 +00:00
// Create replica on broker.
if err := b.CreateReplica(1); err != nil {
2014-12-31 21:47:10 +00:00
log.Fatalf("replica creation error: %s", err)
}
// Create messaging client.
2014-12-31 19:42:53 +00:00
c := messaging.NewClient(1)
2015-01-29 23:07:58 +00:00
c.SetLogOutput(w)
2014-12-31 19:42:53 +00:00
if err := c.Open(filepath.Join(s.Path(), messagingClientFile), []*url.URL{b.URL()}); err != nil {
log.Fatalf("messaging client error: %s", err)
2014-12-30 22:46:50 +00:00
}
if err := s.SetClient(c); err != nil {
2014-12-31 19:42:53 +00:00
log.Fatalf("set client error: %s", err)
}
2014-12-31 19:42:53 +00:00
// Initialize the server.
if err := s.Initialize(b.URL()); err != nil {
log.Fatalf("server initialization error: %s", err)
}
2014-12-30 22:46:50 +00:00
}
// joins a server to an existing cluster.
func joinServer(s *influxdb.Server, u *url.URL, joinURLs []*url.URL) {
// TODO: Use separate broker and data join urls.
// Create data node on an existing data node.
for _, joinURL := range joinURLs {
if err := s.Join(u, joinURL); err != nil {
log.Printf("join: failed to connect data node: %s: %s", u, err)
} else {
log.Printf("join: connected data node to %s", u)
return
}
}
log.Fatalf("join: failed to connect data node to any specified server")
}
2014-12-31 19:42:53 +00:00
// opens the messaging client and attaches it to the server.
2015-01-29 23:07:58 +00:00
func openServerClient(s *influxdb.Server, joinURLs []*url.URL, w io.Writer) {
2014-12-31 19:42:53 +00:00
c := messaging.NewClient(s.ID())
2015-01-29 23:07:58 +00:00
c.SetLogOutput(w)
if err := c.Open(filepath.Join(s.Path(), messagingClientFile), joinURLs); err != nil {
2014-12-31 19:42:53 +00:00
log.Fatalf("messaging client error: %s", err)
2014-12-30 22:46:50 +00:00
}
2014-12-31 19:42:53 +00:00
if err := s.SetClient(c); err != nil {
log.Fatalf("set client error: %s", err)
}
}
2014-12-30 22:46:50 +00:00
2014-12-31 19:42:53 +00:00
// parses a comma-delimited list of URLs.
func parseURLs(s string) (a []*url.URL) {
if s == "" {
return nil
}
2014-12-31 19:42:53 +00:00
for _, s := range strings.Split(s, ",") {
u, err := url.Parse(s)
if err != nil {
log.Fatalf("cannot parse urls: %s", err)
2014-12-31 19:42:53 +00:00
}
a = append(a, u)
}
2014-12-31 19:42:53 +00:00
return
}
2014-12-31 19:42:53 +00:00
// returns true if the file exists.
func fileExists(path string) bool {
if _, err := os.Stat(path); os.IsNotExist(err) {
return false
}
return true
}
func printRunUsage() {
log.Printf(`usage: run [flags]
2015-01-10 16:08:00 +00:00
run starts the broker and data node server. If this is the first time running
the command then a new cluster will be initialized unless the -join argument
is used.
-config <path>
Set the path to the configuration file.
-hostname <name>
Override the hostname, the 'hostname' configuration
option will be overridden.
2015-01-10 16:08:00 +00:00
-join <url>
Joins the server to an existing cluster.
-pidfile <path>
Write process ID to a file.
`)
}