func Test_DecodeNameAndTags(t *testing.T) { var tests = []struct { test string str string name string tags map[string]string position string separator string err string }{ {test: "metric only", str: "cpu", name: "cpu"}, {test: "metric with single series", str: "cpu.hostname.server01", name: "cpu", tags: map[string]string{"hostname": "server01"}}, {test: "metric with multiple series", str: "cpu.region.us-west.hostname.server01", name: "cpu", tags: map[string]string{"hostname": "server01", "region": "us-west"}}, {test: "no metric", tags: make(map[string]string), err: `no name specified for metric. ""`}, {test: "wrong metric format", str: "foo.cpu", tags: make(map[string]string), err: `received "foo.cpu" which doesn't conform to format of key.value.key.value.name or name`}, } for _, test := range tests { t.Logf("testing %q...", test.test) p := graphite.NewParser() if test.separator != "" { p.Separator = test.separator } name, tags, err := p.DecodeNameAndTags(test.str) if errstr(err) != test.err { t.Fatalf("err does not match. expected %v, got %v", test.err, err) } if name != test.name { t.Fatalf("name parse failer. expected %v, got %v", test.name, name) } if len(tags) != len(test.tags) { t.Fatalf("unexpected number of tags. expected %d, got %d", len(test.tags), len(tags)) } for k, v := range test.tags { if tags[k] != v { t.Fatalf("unexpected tag value for tags[%s]. expected %q, got %q", k, v, tags[k]) } } } }
func Test_DecodeMetric(t *testing.T) { testTime := time.Now() epochTime := testTime.UnixNano() / 1000000 // nanos to milliseconds strTime := strconv.FormatInt(epochTime, 10) var tests = []struct { test string line string name string tags map[string]string isInt bool iv int64 fv float64 timestamp time.Time position, separator string err string }{ { test: "position first by default", line: `cpu.foo.bar 50 ` + strTime, name: "cpu", tags: map[string]string{"foo": "bar"}, isInt: true, iv: 50, timestamp: testTime, }, { test: "position first if unable to determine", position: "foo", line: `cpu.foo.bar 50 ` + strTime, name: "cpu", tags: map[string]string{"foo": "bar"}, isInt: true, iv: 50, timestamp: testTime, }, { test: "position last if specified", position: "last", line: `foo.bar.cpu 50 ` + strTime, name: "cpu", tags: map[string]string{"foo": "bar"}, isInt: true, iv: 50, timestamp: testTime, }, { test: "position first if specified with no series", position: "first", line: `cpu 50 ` + strTime, name: "cpu", tags: map[string]string{}, isInt: true, iv: 50, timestamp: testTime, }, { test: "position last if specified with no series", position: "last", line: `cpu 50 ` + strTime, name: "cpu", tags: map[string]string{}, isInt: true, iv: 50, timestamp: testTime, }, { test: "sepeartor is . by default", line: `cpu.foo.bar 50 ` + strTime, name: "cpu", tags: map[string]string{"foo": "bar"}, isInt: true, iv: 50, timestamp: testTime, }, { test: "sepeartor is . if specified", separator: ".", line: `cpu.foo.bar 50 ` + strTime, name: "cpu", tags: map[string]string{"foo": "bar"}, isInt: true, iv: 50, timestamp: testTime, }, { test: "sepeartor is - if specified", separator: "-", line: `cpu-foo-bar 50 ` + strTime, name: "cpu", tags: map[string]string{"foo": "bar"}, isInt: true, iv: 50, timestamp: testTime, }, { test: "sepeartor is boo if specified", separator: "boo", line: `cpuboofooboobar 50 ` + strTime, name: "cpu", tags: map[string]string{"foo": "bar"}, isInt: true, iv: 50, timestamp: testTime, }, { test: "series + metric + integer value", line: `cpu.foo.bar 50 ` + strTime, name: "cpu", tags: map[string]string{"foo": "bar"}, isInt: true, iv: 50, timestamp: testTime, }, { test: "metric only with float value", line: `cpu 50.554 ` + strTime, name: "cpu", isInt: false, fv: 50.554, timestamp: testTime, }, { test: "missing metric", line: `50.554 1419972457825`, err: `received "50.554 1419972457825" which doesn't have three fields`, }, { test: "should fail on invalid key", line: `foo.cpu 50.554 1419972457825`, err: `received "foo.cpu" which doesn't conform to format of key.value.key.value.name or name`, }, { test: "should fail parsing invalid float", line: `cpu 50.554z 1419972457825`, err: `strconv.ParseFloat: parsing "50.554z": invalid syntax`, }, { test: "should fail parsing invalid int", line: `cpu 50z 1419972457825`, err: `strconv.ParseFloat: parsing "50z": invalid syntax`, }, { test: "should fail parsing invalid time", line: `cpu 50.554 14199724z57825`, err: `strconv.ParseInt: parsing "14199724z57825": invalid syntax`, }, } for _, test := range tests { t.Logf("testing %q...", test.test) p := graphite.NewParser() if test.separator != "" { p.Separator = test.separator } p.LastEnabled = (test.position == "last") point, err := p.Parse(test.line) if errstr(err) != test.err { t.Fatalf("err does not match. expected %v, got %v", test.err, err) } if err != nil { // If we erred out,it was intended and the following tests won't work continue } if point.Name != test.name { t.Fatalf("name parse failer. expected %v, got %v", test.name, point.Name) } if len(point.Tags) != len(test.tags) { t.Fatalf("tags len mismatch. expected %d, got %d", len(test.tags), len(point.Tags)) } if test.isInt { i := point.Fields[point.Name].(int64) if i != test.iv { t.Fatalf("integerValue value mismatch. expected %v, got %v", test.iv, point.Fields[point.Name]) } } else { f := point.Fields[point.Name].(float64) if point.Fields[point.Name] != f { t.Fatalf("floatValue value mismatch. expected %v, got %v", test.fv, f) } } if point.Timestamp.UnixNano()/1000000 != test.timestamp.UnixNano()/1000000 { t.Fatalf("timestamp value mismatch. expected %v, got %v", test.timestamp.UnixNano(), point.Timestamp.UnixNano()) } } }
// 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) // Print sweet InfluxDB logo and write the process id to file. log.Print(logo) log.SetPrefix(`[srvr] `) log.SetFlags(log.LstdFlags) writePIDFile(*pidPath) // Parse the configuration and determine if a broker and/or server exist. config := parseConfig(*configPath, *hostname) configExists := *configPath != "" initializing := !fileExists(config.BrokerDir()) && !fileExists(config.DataDir()) // Parse join urls from the --join flag. var joinURLs []*url.URL if *join == "" { joinURLs = parseURLs(config.JoinURLs()) } else { joinURLs = parseURLs(*join) } // Open broker, initialize or join as necessary. b := openBroker(config.BrokerDir(), config.BrokerURL(), initializing, joinURLs) // Start the broker handler. var h *Handler if b != nil { h = &Handler{brokerHandler: messaging.NewHandler(b)} go func() { log.Fatal(http.ListenAndServe(config.BrokerAddr(), h)) }() log.Printf("broker listening on %s", config.BrokerAddr()) } // Open server, initialize or join as necessary. s := openServer(config.DataDir(), config.DataURL(), b, initializing, configExists, joinURLs) // Start the server handler. Attach to broker if listening on the same port. if s != nil { sh := httpd.NewHandler(s, config.Authentication.Enabled, version) if h != nil && config.BrokerAddr() == config.DataAddr() { h.serverHandler = sh } else { go func() { log.Fatal(http.ListenAndServe(config.DataAddr(), sh)) }() } log.Printf("data node #%d listening on %s", s.ID(), config.DataAddr()) // 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 } // Configure Graphite parsing. parser := graphite.NewParser() parser.Separator = c.NameSeparatorString() parser.LastEnabled = c.LastEnabled() // 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()) } } 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()) } } else { log.Fatalf("unrecognized Graphite Server prototcol %s", c.Protocol) } } } // Wait indefinitely. <-(chan struct{})(nil) }
func Run(config *Config, join, version string, logWriter *os.File) (*messaging.Broker, *influxdb.Server) { log.Printf("influxdb started, version %s, commit %s", version, commit) // Parse the configuration and determine if a broker and/or server exist. configExists := config != nil if config == nil { config = NewConfig() } var initBroker, initServer bool if initBroker = !fileExists(config.BrokerDir()); initBroker { log.Printf("Broker directory missing. Need to create a broker.") } if initServer = !fileExists(config.DataDir()); initServer { log.Printf("Data directory missing. Need to create data directory.") } initServer = initServer || initBroker // Parse join urls from the --join flag. var joinURLs []*url.URL if join == "" { joinURLs = parseURLs(config.JoinURLs()) } else { joinURLs = parseURLs(join) } // Open broker, initialize or join as necessary. b := openBroker(config.BrokerDir(), config.BrokerURL(), initBroker, joinURLs, logWriter) // Configure debug of Raft module. b.EnableRaftDebug(config.Logging.RaftTracing) // Start the broker handler. var h *Handler if b != nil { h = &Handler{brokerHandler: messaging.NewHandler(b.Broker)} // We want to make sure we are spun up before we exit this function, so we manually listen and serve listener, err := net.Listen("tcp", config.BrokerAddr()) if err != nil { log.Fatalf("Broker failed to listen on %s. %s ", config.BrokerAddr(), err) } go func() { err := http.Serve(listener, h) if err != nil { log.Fatalf("Broker failed to server on %s.: %s", config.BrokerAddr(), err) } }() log.Printf("broker listening on %s", config.BrokerAddr()) // have it occasionally tell a data node in the cluster to run continuous queries if config.ContinuousQuery.Disable { log.Printf("Not running continuous queries. [continuous_queries].disable is set to true.") } else { b.RunContinuousQueryLoop() } } // Open server, initialize or join as necessary. s := openServer(config, b, initServer, initBroker, configExists, joinURLs, logWriter) s.SetAuthenticationEnabled(config.Authentication.Enabled) // Enable retention policy enforcement if requested. if config.Data.RetentionCheckEnabled { interval := time.Duration(config.Data.RetentionCheckPeriod) if err := s.StartRetentionPolicyEnforcement(interval); err != nil { log.Fatalf("retention policy enforcement failed: %s", err.Error()) } log.Printf("broker enforcing retention policies with check interval of %s", interval) } // Start shard group pre-create interval := config.ShardGroupPreCreateCheckPeriod() if err := s.StartShardGroupsPreCreate(interval); err != nil { log.Fatalf("shard group pre-create failed: %s", err.Error()) } log.Printf("shard group pre-create with check interval of %s", interval) // Start the server handler. Attach to broker if listening on the same port. if s != nil { sh := httpd.NewHandler(s, config.Authentication.Enabled, version) sh.SetLogOutput(logWriter) sh.WriteTrace = config.Logging.WriteTracing if h != nil && config.BrokerAddr() == config.DataAddr() { h.serverHandler = sh } else { // We want to make sure we are spun up before we exit this function, so we manually listen and serve listener, err := net.Listen("tcp", config.DataAddr()) if err != nil { log.Fatal(err) } go func() { log.Fatal(http.Serve(listener, sh)) }() } log.Printf("data node #%d listening on %s", s.ID(), config.DataAddr()) // Start the admin interface on the default port if config.Admin.Enabled { port := fmt.Sprintf(":%d", config.Admin.Port) log.Printf("starting admin server on %s", port) a := admin.NewServer(port) go a.ListenAndServe() } // 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()) } } // Start the server bound to a UDP listener if config.UDP.Enabled { log.Printf("Starting UDP listener on %s", config.DataAddrUDP()) u := udp.NewUDPServer(s) if err := u.ListenAndServe(config.DataAddrUDP()); err != nil { log.Printf("Failed to start UDP listener on %s: %s", config.DataAddrUDP(), err) } } // Spin up any Graphite servers for _, c := range config.Graphites { if !c.Enabled { continue } // Configure Graphite parsing. parser := graphite.NewParser() parser.Separator = c.NameSeparatorString() parser.LastEnabled = c.LastEnabled() if err := s.CreateDatabaseIfNotExists(c.DatabaseString()); err != nil { log.Fatalf("failed to create database for %s Graphite server: %s", c.Protocol, err.Error()) } // Spin up the server. var g graphite.Server g, err := graphite.NewServer(c.Protocol, parser, s, c.DatabaseString()) if err != nil { log.Fatalf("failed to initialize %s Graphite server: %s", c.Protocol, err.Error()) } g.SetLogOutput(logWriter) err = g.ListenAndServe(c.ConnectionString(config.BindAddress)) if err != nil { log.Fatalf("failed to start %s Graphite server: %s", c.Protocol, err.Error()) } } } // unless disabled, start the loop to report anonymous usage stats every 24h if !config.ReportingDisabled { // Make sure we have a config object b4 we try to use it. if configObj := b.Broker.Log().Config(); configObj != nil { clusterID := configObj.ClusterID go s.StartReportingLoop(version, clusterID) } } return b.Broker, s }