func TestMetricCopy(t *testing.T) { want := report.MakeMetric() have := want.Copy() if !reflect.DeepEqual(want, have) { t.Errorf("diff: %s", test.Diff(want, have)) } want = report.MakeMetric().Add(time.Now(), 1) have = want.Copy() if !reflect.DeepEqual(want, have) { t.Errorf("diff: %s", test.Diff(want, have)) } }
func (c *container) memoryUsageMetric(stats []docker.Stats) report.Metric { result := report.MakeMetric() for _, s := range stats { result = result.Add(s.Read, float64(s.MemoryStats.Usage)).WithMax(float64(s.MemoryStats.Limit)) } return result }
func TestMetricRowSummary(t *testing.T) { var ( now = time.Now() metric = report.MakeMetric().Add(now, 1.234) row = report.MetricRow{ ID: "id", Format: "format", Group: "group", Value: 1.234, Priority: 1, Metric: &metric, } summary = row.Summary() ) // summary should not have any samples if summary.Metric.Len() != 0 { t.Errorf("Expected summary to have no samples, but had %d", summary.Metric.Len()) } // original metric should still have its samples if metric.Len() != 1 { t.Errorf("Expected original metric to still have it's samples, but had %d", metric.Len()) } // summary should have all the same fields (minus the metric) summary.Metric = nil row.Metric = nil if !reflect.DeepEqual(summary, row) { t.Errorf("Expected summary to have same fields as original: %s", test.Diff(summary, row)) } }
func TestMetricDiv(t *testing.T) { t1 := time.Now() t2 := time.Now().Add(1 * time.Minute) want := report.MakeMetric(). Add(t1, -2). Add(t2, 2) beforeDiv := report.MakeMetric(). Add(t1, -2048). Add(t2, 2048) have := beforeDiv.Div(1024) if !reflect.DeepEqual(want, have) { t.Errorf("diff: %s", test.Diff(want, have)) } // Check the original was unmodified checkMetric(t, beforeDiv, t1, t2, -2048, 2048) }
func TestMetricMerge(t *testing.T) { t1 := time.Now() t2 := time.Now().Add(1 * time.Minute) t3 := time.Now().Add(2 * time.Minute) t4 := time.Now().Add(3 * time.Minute) metric1 := report.MakeMetric(). Add(t2, 0.2). Add(t3, 0.31) metric2 := report.MakeMetric(). Add(t1, -0.1). Add(t3, 0.3). Add(t4, 0.4) want := report.MakeMetric(). Add(t1, -0.1). Add(t2, 0.2). Add(t3, 0.31). Add(t4, 0.4) have := metric1.Merge(metric2) if !reflect.DeepEqual(want, have) { t.Errorf("diff: %s", test.Diff(want, have)) } // Check it didn't modify metric1 if !metric1.First.Equal(t2) { t.Errorf("Expected metric1.First == %q, but was: %q", t2, metric1.First) } if !metric1.Last.Equal(t3) { t.Errorf("Expected metric1.Last == %q, but was: %q", t3, metric1.Last) } if metric1.Min != 0.0 { t.Errorf("Expected metric1.Min == %f, but was: %f", 0.0, metric1.Min) } if metric1.Max != 0.31 { t.Errorf("Expected metric1.Max == %f, but was: %f", 0.31, metric1.Max) } // Check the result is not the same instance as metric1 if &metric1 == &have { t.Errorf("Expected different pointers for metric1 and have, but both were: %p", &have) } }
func (r *Reporter) processTopology() (report.Topology, error) { t := report.MakeTopology(). WithMetadataTemplates(MetadataTemplates). WithMetricTemplates(MetricTemplates) now := mtime.Now() deltaTotal, maxCPU, err := r.jiffies() if err != nil { return t, err } err = r.walker.Walk(func(p, prev Process) { pidstr := strconv.Itoa(p.PID) nodeID := report.MakeProcessNodeID(r.scope, pidstr) node := report.MakeNode(nodeID) for _, tuple := range []struct{ key, value string }{ {PID, pidstr}, {Name, p.Name}, {Cmdline, p.Cmdline}, {Threads, strconv.Itoa(p.Threads)}, } { if tuple.value != "" { node = node.WithLatests(map[string]string{tuple.key: tuple.value}) } } if p.PPID > 0 { node = node.WithLatests(map[string]string{PPID: strconv.Itoa(p.PPID)}) } if deltaTotal > 0 { cpuUsage := float64(p.Jiffies-prev.Jiffies) / float64(deltaTotal) * 100. node = node.WithMetric(CPUUsage, report.MakeMetric().Add(now, cpuUsage).WithMax(maxCPU)) } node = node.WithMetric(MemoryUsage, report.MakeMetric().Add(now, float64(p.RSSBytes)).WithMax(float64(p.RSSBytesLimit))) node = node.WithMetric(OpenFilesCount, report.MakeMetric().Add(now, float64(p.OpenFilesCount)).WithMax(float64(p.OpenFilesLimit))) t.AddNode(node) }) return t, err }
func TestMetricsCopy(t *testing.T) { t1 := time.Now() want := report.Metrics{ "metric1": report.MakeMetric().Add(t1, 0.1), } delete(want.Copy(), "metric1") // Modify a copy have := want.Copy() // Check the original wasn't affected if !reflect.DeepEqual(want, have) { t.Errorf("diff: %s", test.Diff(want, have)) } }
func TestMetricFirstLastMinMax(t *testing.T) { metric := report.MakeMetric() var zero time.Time t1 := time.Now() t2 := time.Now().Add(1 * time.Minute) t3 := time.Now().Add(2 * time.Minute) t4 := time.Now().Add(3 * time.Minute) other := report.MakeMetric() other.Max = 5 other.Min = -5 other.First = t1.Add(-1 * time.Minute) other.Last = t4.Add(1 * time.Minute) tests := []struct { f func(report.Metric) report.Metric first, last time.Time min, max float64 }{ {nil, zero, zero, 0, 0}, {func(m report.Metric) report.Metric { return m.Add(t2, 2) }, t2, t2, 0, 2}, {func(m report.Metric) report.Metric { return m.Add(t1, 1) }, t1, t2, 0, 2}, {func(m report.Metric) report.Metric { return m.Add(t3, -1) }, t1, t3, -1, 2}, {func(m report.Metric) report.Metric { return m.Add(t4, 3) }, t1, t4, -1, 3}, {func(m report.Metric) report.Metric { return m.Merge(other) }, t1.Add(-1 * time.Minute), t4.Add(1 * time.Minute), -5, 5}, } for _, test := range tests { oldFirst, oldLast, oldMin, oldMax := metric.First, metric.Last, metric.Min, metric.Max oldMetric := metric if test.f != nil { metric = test.f(metric) } // Check it didn't modify the old one checkMetric(t, oldMetric, oldFirst, oldLast, oldMin, oldMax) // Check the new one is as expected checkMetric(t, metric, test.first, test.last, test.min, test.max) } }
func TestMetricAdd(t *testing.T) { s := []report.Sample{ {time.Now(), 0.1}, {time.Now().Add(1 * time.Minute), 0.2}, {time.Now().Add(2 * time.Minute), 0.3}, } have := report.MakeMetric(). Add(s[0].Timestamp, s[0].Value). Add(s[2].Timestamp, s[2].Value). // Keeps sorted Add(s[1].Timestamp, s[1].Value). Add(s[2].Timestamp, 0.5) // Overwrites duplicate timestamps want := report.MakeMetric(). Add(s[0].Timestamp, s[0].Value). Add(s[1].Timestamp, s[1].Value). Add(s[2].Timestamp, 0.5) if !reflect.DeepEqual(want, have) { t.Errorf("diff: %s", test.Diff(want, have)) } }
func (c *container) cpuPercentMetric(stats []docker.Stats) report.Metric { result := report.MakeMetric() if len(stats) < 2 { return result } previous := stats[0] for _, s := range stats[1:] { // Copies from docker/api/client/stats.go#L205 cpuDelta := float64(s.CPUStats.CPUUsage.TotalUsage - previous.CPUStats.CPUUsage.TotalUsage) systemDelta := float64(s.CPUStats.SystemCPUUsage - previous.CPUStats.SystemCPUUsage) cpuPercent := 0.0 if systemDelta > 0.0 && cpuDelta > 0.0 { cpuPercent = (cpuDelta / systemDelta) * float64(len(s.CPUStats.CPUUsage.PercpuUsage)) * 100.0 } result = result.Add(s.Read, cpuPercent) available := float64(len(s.CPUStats.CPUUsage.PercpuUsage)) * 100.0 if available >= result.Max { result.Max = available } previous = s } return result }
func TestMetricsMerge(t *testing.T) { t1 := time.Now() t2 := time.Now().Add(1 * time.Minute) t3 := time.Now().Add(2 * time.Minute) t4 := time.Now().Add(3 * time.Minute) metrics1 := report.Metrics{ "metric1": report.MakeMetric().Add(t1, 0.1).Add(t2, 0.2), "metric2": report.MakeMetric().Add(t3, 0.3), } metrics2 := report.Metrics{ "metric2": report.MakeMetric().Add(t4, 0.4), "metric3": report.MakeMetric().Add(t1, 0.1).Add(t2, 0.2), } want := report.Metrics{ "metric1": report.MakeMetric().Add(t1, 0.1).Add(t2, 0.2), "metric2": report.MakeMetric().Add(t3, 0.3).Add(t4, 0.4), "metric3": report.MakeMetric().Add(t1, 0.1).Add(t2, 0.2), } have := metrics1.Merge(metrics2) if !reflect.DeepEqual(want, have) { t.Errorf("diff: %s", test.Diff(want, have)) } }
ClientContainerImageName = "image/client" ServerContainerImageName = "image/server" KubernetesNamespace = "ping" ClientPodID = "ping/pong-a" ServerPodID = "ping/pong-b" ClientPodUID = "5d4c3b2a1" ServerPodUID = "i9h8g7f6e" ClientPodNodeID = report.MakePodNodeID(ClientPodUID) ServerPodNodeID = report.MakePodNodeID(ServerPodUID) ServiceName = "pongservice" ServiceID = "ping/pongservice" ServiceUID = "service1234" ServiceNodeID = report.MakeServiceNodeID(ServiceUID) ClientProcess1CPUMetric = report.MakeMetric().Add(Now, 0.01).WithFirst(Now.Add(-1 * time.Second)) ClientProcess1MemoryMetric = report.MakeMetric().Add(Now, 0.02).WithFirst(Now.Add(-2 * time.Second)) ClientContainerCPUMetric = report.MakeMetric().Add(Now, 0.03).WithFirst(Now.Add(-3 * time.Second)) ClientContainerMemoryMetric = report.MakeMetric().Add(Now, 0.04).WithFirst(Now.Add(-4 * time.Second)) ServerContainerCPUMetric = report.MakeMetric().Add(Now, 0.05).WithFirst(Now.Add(-5 * time.Second)) ServerContainerMemoryMetric = report.MakeMetric().Add(Now, 0.06).WithFirst(Now.Add(-6 * time.Second)) ClientHostCPUMetric = report.MakeMetric().Add(Now, 0.07).WithFirst(Now.Add(-7 * time.Second)) ClientHostMemoryMetric = report.MakeMetric().Add(Now, 0.08).WithFirst(Now.Add(-8 * time.Second)) ClientHostLoad1Metric = report.MakeMetric().Add(Now, 0.09).WithFirst(Now.Add(-9 * time.Second)) ServerHostCPUMetric = report.MakeMetric().Add(Now, 0.12).WithFirst(Now.Add(-12 * time.Second)) ServerHostMemoryMetric = report.MakeMetric().Add(Now, 0.13).WithFirst(Now.Add(-13 * time.Second)) ServerHostLoad1Metric = report.MakeMetric().Add(Now, 0.14).WithFirst(Now.Add(-14 * time.Second))
var GetLoad = func(now time.Time) report.Metrics { out, err := exec.Command("w").CombinedOutput() if err != nil { return nil } matches := loadRe.FindAllStringSubmatch(string(out), -1) if matches == nil || len(matches) < 1 || len(matches[0]) < 4 { return nil } one, err := strconv.ParseFloat(matches[0][1], 64) if err != nil { return nil } return report.Metrics{ Load1: report.MakeMetric().Add(now, one), } } // GetUptime returns the uptime of the host. var GetUptime = func() (time.Duration, error) { out, err := exec.Command("w").CombinedOutput() if err != nil { return 0, err } matches := uptimeRe.FindAllStringSubmatch(string(out), -1) if matches == nil || len(matches) < 1 || len(matches[0]) < 4 { return 0, err } d, err := strconv.Atoi(matches[0][1]) if err != nil {
func TestSummaries(t *testing.T) { { // Just a convenient source of some rendered nodes have := detailed.Summaries(fixture.Report, render.ProcessRenderer.Render(fixture.Report, render.FilterNoop)) // The ids of the processes rendered above expectedIDs := []string{ fixture.ClientProcess1NodeID, fixture.ClientProcess2NodeID, fixture.ServerProcessNodeID, fixture.NonContainerProcessNodeID, render.IncomingInternetID, render.OutgoingInternetID, } sort.Strings(expectedIDs) // It should summarize each node ids := []string{} for id := range have { ids = append(ids, id) } sort.Strings(ids) if !reflect.DeepEqual(expectedIDs, ids) { t.Fatalf("Expected Summaries to have summarized every node in the process renderer: %v, but got %v", expectedIDs, ids) } } // It should summarize nodes' metrics { t1, t2 := mtime.Now().Add(-1*time.Minute), mtime.Now() metric := report.MakeMetric().Add(t1, 1).Add(t2, 2) input := fixture.Report.Copy() input.Process.Nodes[fixture.ClientProcess1NodeID] = input.Process.Nodes[fixture.ClientProcess1NodeID].WithMetrics(report.Metrics{process.CPUUsage: metric}) have := detailed.Summaries(input, render.ProcessRenderer.Render(input, render.FilterNoop)) node, ok := have[fixture.ClientProcess1NodeID] if !ok { t.Fatalf("Expected output to have the node we added the metric to") } var row report.MetricRow ok = false for _, metric := range node.Metrics { if metric.ID == process.CPUUsage { row = metric ok = true break } } if !ok { t.Fatalf("Expected node to have the metric we added") } // Our summarized MetricRow want := report.MetricRow{ ID: process.CPUUsage, Label: "CPU", Format: "percent", Value: 2, Priority: 1, Metric: &report.Metric{ Samples: nil, Min: metric.Min, Max: metric.Max, First: metric.First, Last: metric.Last, }, } if !reflect.DeepEqual(want, row) { t.Fatalf("Expected to have summarized the node's metrics: %s", test.Diff(want, row)) } } }
func TestMetricMarshalling(t *testing.T) { t1 := time.Now().UTC() t2 := time.Now().UTC().Add(1 * time.Minute) t3 := time.Now().UTC().Add(2 * time.Minute) t4 := time.Now().UTC().Add(3 * time.Minute) wantSamples := []report.Sample{ {Timestamp: t1, Value: 0.1}, {Timestamp: t2, Value: 0.2}, {Timestamp: t3, Value: 0.3}, {Timestamp: t4, Value: 0.4}, } want := report.MakeMetric() for _, sample := range wantSamples { want = want.Add(sample.Timestamp, sample.Value) } // gob { gobs, err := want.GobEncode() if err != nil { t.Fatal(err) } var have report.Metric have.GobDecode(gobs) if !reflect.DeepEqual(want, have) { t.Error(test.Diff(want, have)) } } // others { for _, h := range []codec.Handle{ codec.Handle(&codec.MsgpackHandle{}), codec.Handle(&codec.JsonHandle{}), } { buf := &bytes.Buffer{} encoder := codec.NewEncoder(buf, h) want.CodecEncodeSelf(encoder) bufCopy := bytes.NewBuffer(buf.Bytes()) decoder := codec.NewDecoder(buf, h) var have report.Metric have.CodecDecodeSelf(decoder) if !reflect.DeepEqual(want, have) { t.Error(test.Diff(want, have)) } // extra check for samples decoder = codec.NewDecoder(bufCopy, h) var wire struct { Samples []report.Sample `json:"samples"` } if err := decoder.Decode(&wire); err != nil { t.Error(err) } if !reflect.DeepEqual(wantSamples, wire.Samples) { t.Error(test.Diff(wantSamples, wire.Samples)) } } } }
func TestContainer(t *testing.T) { log.SetOutput(ioutil.Discard) oldDialStub, oldNewClientConnStub := docker.DialStub, docker.NewClientConnStub defer func() { docker.DialStub, docker.NewClientConnStub = oldDialStub, oldNewClientConnStub }() docker.DialStub = func(network, address string) (net.Conn, error) { return nil, nil } reader, writer := io.Pipe() connection := &mockConnection{reader} docker.NewClientConnStub = func(c net.Conn, r *bufio.Reader) docker.ClientConn { return connection } now := time.Unix(12345, 67890).UTC() mtime.NowForce(now) defer mtime.NowReset() const hostID = "scope" c := docker.NewContainer(container1, hostID) err := c.StartGatheringStats() if err != nil { t.Errorf("%v", err) } defer c.StopGatheringStats() // Send some stats to the docker container stats := &client.Stats{} stats.Read = now stats.MemoryStats.Usage = 12345 stats.MemoryStats.Limit = 45678 encoder := codec.NewEncoder(writer, &codec.JsonHandle{}) if err = encoder.Encode(&stats); err != nil { t.Error(err) } // Now see if we go them { uptime := (now.Sub(startTime) / time.Second) * time.Second want := report.MakeNodeWith("ping;<container>", map[string]string{ "docker_container_command": " ", "docker_container_created": "01 Jan 01 00:00 UTC", "docker_container_id": "ping", "docker_container_name": "pong", "docker_image_id": "baz", "docker_label_foo1": "bar1", "docker_label_foo2": "bar2", "docker_container_state": "running", "docker_container_state_human": "Up 6 years", "docker_container_uptime": uptime.String(), }). WithControls( docker.RestartContainer, docker.StopContainer, docker.PauseContainer, docker.AttachContainer, docker.ExecContainer, ).WithMetrics(report.Metrics{ "docker_cpu_total_usage": report.MakeMetric(), "docker_memory_usage": report.MakeMetric().Add(now, 12345).WithMax(45678), }).WithParents(report.EmptySets. Add(report.ContainerImage, report.MakeStringSet(report.MakeContainerImageNodeID("baz"))), ) test.Poll(t, 100*time.Millisecond, want, func() interface{} { node := c.GetNode() node.Latest.ForEach(func(k, v string) { if v == "0" || v == "" { node.Latest = node.Latest.Delete(k) } }) return node }) } { want := report.EmptySets. Add("docker_container_ports", report.MakeStringSet("1.2.3.4:80->80/tcp", "81/tcp")). Add("docker_container_ips", report.MakeStringSet("1.2.3.4")). Add("docker_container_ips_with_scopes", report.MakeStringSet(";1.2.3.4")) test.Poll(t, 100*time.Millisecond, want, func() interface{} { return c.NetworkInfo([]net.IP{}) }) } if c.Image() != "baz" { t.Errorf("%s != baz", c.Image()) } if c.PID() != 2 { t.Errorf("%d != 2", c.PID()) } node := c.GetNode().WithSets(c.NetworkInfo([]net.IP{})) if have := docker.ExtractContainerIPs(node); !reflect.DeepEqual(have, []string{"1.2.3.4"}) { t.Errorf("%v != %v", have, []string{"1.2.3.4"}) } }
func TestReporter(t *testing.T) { var ( release = "release" version = "version" network = "192.168.0.0/16" hostID = "hostid" hostname = "hostname" timestamp = time.Now() metrics = report.Metrics{ host.Load1: report.MakeMetric().Add(timestamp, 1.0), host.CPUUsage: report.MakeMetric().Add(timestamp, 30.0).WithMax(100.0), host.MemoryUsage: report.MakeMetric().Add(timestamp, 60.0).WithMax(100.0), } uptime = "278h55m43s" kernel = "release version" _, ipnet, _ = net.ParseCIDR(network) ) mtime.NowForce(timestamp) defer mtime.NowReset() var ( oldGetKernelVersion = host.GetKernelVersion oldGetLoad = host.GetLoad oldGetUptime = host.GetUptime oldGetCPUUsagePercent = host.GetCPUUsagePercent oldGetMemoryUsageBytes = host.GetMemoryUsageBytes oldGetLocalNetworks = host.GetLocalNetworks ) defer func() { host.GetKernelVersion = oldGetKernelVersion host.GetLoad = oldGetLoad host.GetUptime = oldGetUptime host.GetCPUUsagePercent = oldGetCPUUsagePercent host.GetMemoryUsageBytes = oldGetMemoryUsageBytes host.GetLocalNetworks = oldGetLocalNetworks }() host.GetKernelVersion = func() (string, error) { return release + " " + version, nil } host.GetLoad = func(time.Time) report.Metrics { return metrics } host.GetUptime = func() (time.Duration, error) { return time.ParseDuration(uptime) } host.GetCPUUsagePercent = func() (float64, float64) { return 30.0, 100.0 } host.GetMemoryUsageBytes = func() (float64, float64) { return 60.0, 100.0 } host.GetLocalNetworks = func() ([]*net.IPNet, error) { return []*net.IPNet{ipnet}, nil } rpt, err := host.NewReporter(hostID, hostname, "", "", nil).Report() if err != nil { t.Fatal(err) } nodeID := report.MakeHostNodeID(hostID) node, ok := rpt.Host.Nodes[nodeID] if !ok { t.Errorf("Expected host node %q, but not found", nodeID) } // Should have a bunch of expected latest keys for _, tuple := range []struct { key, want string }{ {host.Timestamp, timestamp.UTC().Format(time.RFC3339Nano)}, {host.HostName, hostname}, {host.OS, runtime.GOOS}, {host.Uptime, uptime}, {host.KernelVersion, kernel}, } { if have, ok := node.Latest.Lookup(tuple.key); !ok || have != tuple.want { t.Errorf("Expected %s %q, got %q", tuple.key, tuple.want, have) } } // Should have the local network if have, ok := node.Sets.Lookup(host.LocalNetworks); !ok || !have.Contains(network) { t.Errorf("Expected host.LocalNetworks to include %q, got %q", network, have) } // Should have metrics for key, want := range metrics { wantSample := want.LastSample() if metric, ok := node.Metrics[key]; !ok { t.Errorf("Expected %s metric, but not found", key) } else if sample := metric.LastSample(); sample == nil { t.Errorf("Expected %s metric to have a sample, but there were none", key) } else if sample.Value != wantSample.Value { t.Errorf("Expected %s metric sample %f, got %f", key, wantSample, sample.Value) } } }