func TestMeasureQueues(t *testing.T) { // Setup Listen apiConfig := coco.ApiConfig{ Bind: "127.0.0.1:26081", } var tiers []coco.Tier blacklisted := map[string]map[string]int64{} go coco.Api(apiConfig, &tiers, &blacklisted) poll(t, apiConfig.Bind) // Setup Measure chans := map[string]chan collectd.Packet{ "a": make(chan collectd.Packet, 1000), "b": make(chan collectd.Packet, 1000), "c": make(chan collectd.Packet, 1000), } measureConfig := coco.MeasureConfig{ TickInterval: *new(coco.Duration), } measureConfig.TickInterval.UnmarshalText([]byte("10ms")) go coco.Measure(measureConfig, chans, &tiers) // Test pushing packets for _, c := range chans { for i := 0; i < 950; i++ { c <- collectd.Packet{} } } time.Sleep(10 * time.Millisecond) // Test resp, err := http.Get("http://127.0.0.1:26081/debug/vars") if err != nil { t.Fatalf("HTTP GET failed: %s", err) } body, err := ioutil.ReadAll(resp.Body) var result map[string]interface{} err = json.Unmarshal(body, &result) if err != nil { t.Errorf("Error when decoding JSON %+v.", err) t.Errorf("Response body: %s", string(body)) t.FailNow() } counts := result["coco"].(map[string]interface{})["queues"].(map[string]interface{}) expected := 950 for k, v := range counts { c := int(v.(float64)) if c != expected { t.Errorf("Expected %s to equal %d, got %.2f", k, expected, v) } } }
func TestBlacklisted(t *testing.T) { // Setup Filter config := coco.FilterConfig{ Blacklist: "/(vmem|irq|entropy|users)/", } raw := make(chan collectd.Packet) filtered := make(chan collectd.Packet) items := make(chan coco.BlacklistItem, 1000000) go coco.Filter(config, raw, filtered, items) // Setup blacklist blacklisted := map[string]map[string]int64{} go coco.Blacklist(items, &blacklisted) // Setup Api apiConfig := coco.ApiConfig{ Bind: "127.0.0.1:26082", } var tiers []coco.Tier go coco.Api(apiConfig, &tiers, &blacklisted) poll(t, apiConfig.Bind) // Push 10 metrics through that should be blacklisted for i := 0; i < 10; i++ { raw <- collectd.Packet{ Hostname: "foo", Plugin: "irq", Type: "irq", TypeInstance: strconv.Itoa(i), } } // Fetch blacklisted metrics resp, err := http.Get("http://127.0.0.1:26082/blacklisted") if err != nil { t.Fatalf("HTTP GET failed: %s", err) } body, err := ioutil.ReadAll(resp.Body) var result map[string]interface{} err = json.Unmarshal(body, &result) if err != nil { t.Errorf("Error when decoding JSON %+v.", err) t.Errorf("Response body: %s", string(body)) t.FailNow() } // Test the metrics have been blacklisted count := len(result["foo"].(map[string]interface{})) expected := 10 if count != expected { t.Errorf("Expected %d blacklisted metrics, got %d", count, expected) } }
func TestSendWhenMissingConnection(t *testing.T) { // Setup tiers tierConfig := make(map[string]coco.TierConfig) tierConfig["a"] = coco.TierConfig{Targets: []string{"127.0.0.1:29000", "a.example:29000", "b.example:29000", "c.example:29000"}} var tiers []coco.Tier for k, v := range tierConfig { tier := coco.Tier{Name: k, Targets: v.Targets} tiers = append(tiers, tier) } // Launch Api so we can query expvars apiConfig := coco.ApiConfig{ Bind: "127.0.0.1:26880", } blacklisted := map[string]map[string]int64{} go coco.Api(apiConfig, &tiers, &blacklisted) poll(t, apiConfig.Bind) // Launch Send so we can test dispatch behaviour filtered := make(chan collectd.Packet) go coco.Send(&tiers, filtered) // Query the expvars var actual float64 var expected float64 var vars map[string]interface{} vars = fetchExpvar(t, apiConfig.Bind) actual = vars["coco"].(map[string]interface{})["errors"].(map[string]interface{})["send.disconnected"].(float64) expected = 0 if actual != expected { t.Fatalf("Expected coco.errors.send.disconnected to be %d, was %d", expected, actual) } // Dispatch a single packet send := collectd.Packet{ Hostname: "foo", Plugin: "load", Type: "load", } filtered <- send // Check the failure count has increased vars = fetchExpvar(t, apiConfig.Bind) actual = vars["coco"].(map[string]interface{})["errors"].(map[string]interface{})["send.disconnected"].(float64) expected = 1 if actual != expected { t.Fatalf("Expected coco.errors.send.disconnected to be %d, was %d", expected, actual) } }
func TestVirtualReplicasMagic(t *testing.T) { // Setup tiers tierConfig := make(map[string]coco.TierConfig) tierConfig["a"] = coco.TierConfig{Targets: []string{"127.0.0.1:25811", "127.0.0.1:25812", "127.0.0.1:25813"}} tierConfig["b"] = coco.TierConfig{Targets: []string{"127.0.0.1:25821", "127.0.0.1:25822", "127.0.0.1:25823"}} tierConfig["c"] = coco.TierConfig{Targets: []string{"127.0.0.1:25831", "127.0.0.1:25832", "127.0.0.1:25833"}} var tiers []coco.Tier for k, v := range tierConfig { tier := coco.Tier{Name: k, Targets: v.Targets} tiers = append(tiers, tier) } // Setup Send filtered := make(chan collectd.Packet) go coco.Send(&tiers, filtered) // Setup Api apiConfig := coco.ApiConfig{ Bind: "127.0.0.1:26840", } blacklisted := map[string]map[string]int64{} go coco.Api(apiConfig, &tiers, &blacklisted) poll(t, apiConfig.Bind) // Fetch exposed tiers resp, err := http.Get("http://127.0.0.1:26840/tiers") if err != nil { t.Fatalf("HTTP GET failed: %s", err) } body, err := ioutil.ReadAll(resp.Body) //var result []coco.Tier var result []map[string]interface{} err = json.Unmarshal(body, &result) if err != nil { t.Errorf("Error when decoding JSON %+v.", err) t.Errorf("Response body: %s", string(body)) t.FailNow() } for _, tier := range result { if tier["virtual_replicas"] == nil { t.Errorf("tier '%s' doesn't expose virtual replicas", tier["name"]) } t.Logf("tier '%s' has %.0f virtual replicas", tier["name"], tier["virtual_replicas"]) } }
func TestTierLookup(t *testing.T) { // Setup sender tierConfig := make(map[string]coco.TierConfig) tierConfig["a"] = coco.TierConfig{Targets: []string{"127.0.0.1:25887"}} tierConfig["b"] = coco.TierConfig{Targets: []string{"127.0.0.1:25888"}} tierConfig["c"] = coco.TierConfig{Targets: []string{"127.0.0.1:25889"}} var tiers []coco.Tier for k, v := range tierConfig { tier := coco.Tier{Name: k, Targets: v.Targets} tiers = append(tiers, tier) } filtered := make(chan collectd.Packet) go coco.Send(&tiers, filtered) // Setup API apiConfig := coco.ApiConfig{ Bind: "0.0.0.0:25999", } blacklisted := map[string]map[string]int64{} go coco.Api(apiConfig, &tiers, &blacklisted) poll(t, apiConfig.Bind) // Test resp, err := http.Get("http://127.0.0.1:25999/lookup?name=abc") if err != nil { t.Fatalf("HTTP GET failed: %s", err) } body, err := ioutil.ReadAll(resp.Body) var result map[string]string err = json.Unmarshal(body, &result) if err != nil { t.Fatalf("Error when decoding JSON %+v. Response body: %s", err, string(body)) } for k, v := range tierConfig { if result[k] != v.Targets[0] { t.Errorf("Couldn't find tier %s in response: %s", k, string(body)) } } }
func main() { kingpin.Version("1.0.0") kingpin.Parse() var config coco.Config if _, err := toml.DecodeFile(*configPath, &config); err != nil { log.Fatalln("fatal:", err) return } // Setup data structures to be shared across components blacklisted := map[string]map[string]int64{} raw := make(chan collectd.Packet, 1000000) filtered := make(chan collectd.Packet, 1000000) items := make(chan coco.BlacklistItem, 1000000) var tiers []coco.Tier for k, v := range config.Tiers { tier := coco.Tier{Name: k, Targets: v.Targets} tiers = append(tiers, tier) } if len(tiers) == 0 { log.Fatal("No tiers configured. Exiting.") } chans := map[string]chan collectd.Packet{ "raw": raw, "filtered": filtered, //"blacklist_items": items, } go coco.Measure(config.Measure, chans, &tiers) // Launch components to do the work go coco.Listen(config.Listen, raw) for i := 0; i < 4; i++ { go coco.Filter(config.Filter, raw, filtered, items) } go coco.Blacklist(items, &blacklisted) go coco.Send(&tiers, filtered) coco.Api(config.Api, &tiers, &blacklisted) }
func TestExpvars(t *testing.T) { // Setup API tierConfig := make(map[string]coco.TierConfig) tierConfig["a"] = coco.TierConfig{Targets: []string{"127.0.0.1:25887"}} var tiers []coco.Tier for k, v := range tierConfig { tier := coco.Tier{Name: k, Targets: v.Targets} tiers = append(tiers, tier) } apiConfig := coco.ApiConfig{ Bind: "127.0.0.1:26080", } blacklisted := map[string]map[string]int64{} go coco.Api(apiConfig, &tiers, &blacklisted) poll(t, apiConfig.Bind) // Test resp, err := http.Get("http://127.0.0.1:26080/debug/vars") if err != nil { t.Fatalf("HTTP GET failed: %s", err) } body, err := ioutil.ReadAll(resp.Body) var result map[string]interface{} err = json.Unmarshal(body, &result) if err != nil { t.Errorf("Error when decoding JSON %+v.", err) t.Errorf("Response body: %s", string(body)) t.FailNow() } if result["cmdline"] == nil { t.Errorf("Couldn't find 'cmdline' key in JSON.") t.Errorf("JSON object: %+v", result) t.FailNow() } }
func TestMeasureDistributionSummaryStats(t *testing.T) { // Setup tiers tierConfig := make(map[string]coco.TierConfig) tierConfig["a"] = coco.TierConfig{Targets: []string{"127.0.0.1:25811", "127.0.0.1:25812", "127.0.0.1:25813"}} tierConfig["b"] = coco.TierConfig{Targets: []string{"127.0.0.1:25821", "127.0.0.1:25822", "127.0.0.1:25823"}} tierConfig["c"] = coco.TierConfig{Targets: []string{"127.0.0.1:25831", "127.0.0.1:25832", "127.0.0.1:25833"}} for _, v := range tierConfig { for _, target := range v.Targets { go MockListener(t, target) } } var tiers []coco.Tier for k, v := range tierConfig { tier := coco.Tier{Name: k, Targets: v.Targets} tiers = append(tiers, tier) } // Setup Api apiConfig := coco.ApiConfig{ Bind: "127.0.0.1:26810", } blacklisted := map[string]map[string]int64{} go coco.Api(apiConfig, &tiers, &blacklisted) poll(t, apiConfig.Bind) // Setup Measure chans := map[string]chan collectd.Packet{} measureConfig := coco.MeasureConfig{ TickInterval: *new(coco.Duration), } measureConfig.TickInterval.UnmarshalText([]byte("100ms")) go coco.Measure(measureConfig, chans, &tiers) // Setup Send filtered := make(chan collectd.Packet) go coco.Send(&tiers, filtered) // Push packets to Send // 1000 hosts for i := 0; i < 1000; i++ { // up to 24 cpus per host iter := rand.Intn(24) for n := 0; n < iter; n++ { types := []string{"user", "system", "steal", "wait"} for _, typ := range types { filtered <- collectd.Packet{ Hostname: "foo" + string(i), Plugin: "cpu-" + strconv.Itoa(n), Type: "cpu-" + typ, } } } } time.Sleep(10 * time.Millisecond) // Fetch exposed expvars resp, err := http.Get("http://127.0.0.1:26810/debug/vars") if err != nil { t.Fatalf("HTTP GET failed: %s", err) } body, err := ioutil.ReadAll(resp.Body) var result map[string]interface{} err = json.Unmarshal(body, &result) if err != nil { t.Errorf("Error when decoding JSON %+v.", err) t.Errorf("Response body: %s", string(body)) t.FailNow() } // Test the exposed expvars data looks sane tierProps := result["coco"].(map[string]interface{})["hash.metrics_per_host"].(map[string]interface{}) t.Logf("Expvar tier props: %+v\n", tierProps) if len(tiers) != len(tierProps) { t.Errorf("Expected %d tiers to be exposed, got %d\n", len(tiers), len(tierProps)) //t.Errorf("Tiers: %+v\n", tiers) t.Errorf("Exposed tiers: %+v\n", tierProps) } for _, tier := range tiers { targetProps := tierProps[tier.Name].(map[string]interface{}) if len(targetProps) != len(tier.Targets)+1 { t.Errorf("Expected %d targets to be exposed, got %d\n", len(tier.Targets)+1, len(targetProps)) t.Errorf("Tier targets: %+v\n", tier.Targets) t.Errorf("Exposed target properties: %+v\n", targetProps) } for _, target := range tier.Targets { props := targetProps[target].(map[string]interface{}) for _, k := range []string{"95e", "avg", "max", "min", "sum"} { if props[k] == nil { t.Errorf("Expected %s metric was not exposed on %s\n", k, target) } } } } }