func TestTagger(t *testing.T) { oldProcessTree := docker.NewProcessTreeStub defer func() { docker.NewProcessTreeStub = oldProcessTree }() docker.NewProcessTreeStub = func(procRoot string) (process.Tree, error) { return &mockProcessTree{map[int]int{2: 1}}, nil } var ( pid1NodeID = report.MakeProcessNodeID("somehost.com", "1") pid2NodeID = report.MakeProcessNodeID("somehost.com", "2") wantNodeMetadata = report.NodeMetadata{docker.ContainerID: "ping"} ) input := report.MakeReport() input.Process.NodeMetadatas[pid1NodeID] = report.NodeMetadata{"pid": "1"} input.Process.NodeMetadatas[pid2NodeID] = report.NodeMetadata{"pid": "2"} want := report.MakeReport() want.Process.NodeMetadatas[pid1NodeID] = report.NodeMetadata{"pid": "1"}.Merge(wantNodeMetadata) want.Process.NodeMetadatas[pid2NodeID] = report.NodeMetadata{"pid": "2"}.Merge(wantNodeMetadata) tagger := docker.NewTagger(mockRegistryInstance, "/irrelevant") have, err := tagger.Tag(input) if err != nil { t.Errorf("%v", err) } if !reflect.DeepEqual(want, have) { t.Errorf("%s", test.Diff(want, have)) } }
func TestTagger(t *testing.T) { oldProcessTree := docker.NewProcessTreeStub defer func() { docker.NewProcessTreeStub = oldProcessTree }() docker.NewProcessTreeStub = func(_ process.Walker) (process.Tree, error) { return &mockProcessTree{map[int]int{3: 2}}, nil } var ( pid1NodeID = report.MakeProcessNodeID("somehost.com", "2") pid2NodeID = report.MakeProcessNodeID("somehost.com", "3") wantNode = report.MakeNodeWith(map[string]string{docker.ContainerID: "ping"}) ) input := report.MakeReport() input.Process.AddNode(pid1NodeID, report.MakeNodeWith(map[string]string{process.PID: "2"})) input.Process.AddNode(pid2NodeID, report.MakeNodeWith(map[string]string{process.PID: "3"})) want := report.MakeReport() want.Process.AddNode(pid1NodeID, report.MakeNodeWith(map[string]string{process.PID: "2"}).Merge(wantNode)) want.Process.AddNode(pid2NodeID, report.MakeNodeWith(map[string]string{process.PID: "3"}).Merge(wantNode)) tagger := docker.NewTagger(mockRegistryInstance, nil) have, err := tagger.Tag(input) if err != nil { t.Errorf("%v", err) } if !reflect.DeepEqual(want, have) { t.Errorf("%s", test.Diff(want, have)) } }
func TestReporter(t *testing.T) { walker := &mockWalker{ processes: []process.Process{ {PID: 1, PPID: 0, Name: "init"}, {PID: 2, PPID: 1, Name: "bash"}, {PID: 3, PPID: 1, Name: "apache", Threads: 2}, {PID: 4, PPID: 2, Name: "ping", Cmdline: "ping foo.bar.local"}, {PID: 5, PPID: 1, Cmdline: "tail -f /var/log/syslog"}, }, } getDeltaTotalJiffies := func() (uint64, float64, error) { return 0, 0., nil } now := time.Now() mtime.NowForce(now) defer mtime.NowReset() reporter := process.NewReporter(walker, "", getDeltaTotalJiffies) want := report.MakeReport() want.Process = report.MakeTopology().AddNode( report.MakeProcessNodeID("", "1"), report.MakeNodeWith(map[string]string{ process.PID: "1", process.Name: "init", process.Threads: "0", }).WithMetric(process.MemoryUsage, report.MakeMetric().Add(now, 0.)), ).AddNode( report.MakeProcessNodeID("", "2"), report.MakeNodeWith(map[string]string{ process.PID: "2", process.Name: "bash", process.PPID: "1", process.Threads: "0", }).WithMetric(process.MemoryUsage, report.MakeMetric().Add(now, 0.)), ).AddNode( report.MakeProcessNodeID("", "3"), report.MakeNodeWith(map[string]string{ process.PID: "3", process.Name: "apache", process.PPID: "1", process.Threads: "2", }).WithMetric(process.MemoryUsage, report.MakeMetric().Add(now, 0.)), ).AddNode( report.MakeProcessNodeID("", "4"), report.MakeNodeWith(map[string]string{ process.PID: "4", process.Name: "ping", process.PPID: "2", process.Cmdline: "ping foo.bar.local", process.Threads: "0", }).WithMetric(process.MemoryUsage, report.MakeMetric().Add(now, 0.)), ).AddNode( report.MakeProcessNodeID("", "5"), report.MakeNodeWith(map[string]string{ process.PID: "5", process.PPID: "1", process.Cmdline: "tail -f /var/log/syslog", process.Threads: "0", }).WithMetric(process.MemoryUsage, report.MakeMetric().Add(now, 0.)), ) have, err := reporter.Report() if err != nil || !reflect.DeepEqual(want, have) { t.Errorf("%s (%v)", test.Diff(want, have), err) } }
func TestReporter(t *testing.T) { oldWalk := process.Walk defer func() { process.Walk = oldWalk }() process.Walk = func(_ string, f func(*process.Process)) error { for _, p := range []*process.Process{ {PID: 1, PPID: 0, Comm: "init"}, {PID: 2, PPID: 1, Comm: "bash"}, {PID: 3, PPID: 1, Comm: "apache", Threads: 2}, {PID: 4, PPID: 2, Comm: "ping", Cmdline: "ping foo.bar.local"}, } { f(p) } return nil } reporter := process.NewReporter("", "") want := report.MakeReport() want.Process = report.Topology{ Adjacency: report.Adjacency{}, EdgeMetadatas: report.EdgeMetadatas{}, NodeMetadatas: report.NodeMetadatas{ report.MakeProcessNodeID("", "1"): report.NodeMetadata{ process.PID: "1", process.Comm: "init", process.Cmdline: "", process.Threads: "0", }, report.MakeProcessNodeID("", "2"): report.NodeMetadata{ process.PID: "2", process.Comm: "bash", process.PPID: "1", process.Cmdline: "", process.Threads: "0", }, report.MakeProcessNodeID("", "3"): report.NodeMetadata{ process.PID: "3", process.Comm: "apache", process.PPID: "1", process.Cmdline: "", process.Threads: "2", }, report.MakeProcessNodeID("", "4"): report.NodeMetadata{ process.PID: "4", process.Comm: "ping", process.PPID: "2", process.Cmdline: "ping foo.bar.local", process.Threads: "0", }, }, } have, err := reporter.Report() if err != nil || !reflect.DeepEqual(want, have) { t.Errorf("%s (%v)", test.Diff(want, have), err) } }
func TestReporter(t *testing.T) { walker := &mockWalker{ processes: []process.Process{ {PID: 1, PPID: 0, Comm: "init"}, {PID: 2, PPID: 1, Comm: "bash"}, {PID: 3, PPID: 1, Comm: "apache", Threads: 2}, {PID: 4, PPID: 2, Comm: "ping", Cmdline: "ping foo.bar.local"}, {PID: 5, PPID: 1, Cmdline: "tail -f /var/log/syslog"}, }, } reporter := process.NewReporter(walker, "") want := report.MakeReport() want.Process = report.Topology{ Adjacency: report.Adjacency{}, EdgeMetadatas: report.EdgeMetadatas{}, NodeMetadatas: report.NodeMetadatas{ report.MakeProcessNodeID("", "1"): report.MakeNodeMetadataWith(map[string]string{ process.PID: "1", process.Comm: "init", process.Threads: "0", }), report.MakeProcessNodeID("", "2"): report.MakeNodeMetadataWith(map[string]string{ process.PID: "2", process.Comm: "bash", process.PPID: "1", process.Threads: "0", }), report.MakeProcessNodeID("", "3"): report.MakeNodeMetadataWith(map[string]string{ process.PID: "3", process.Comm: "apache", process.PPID: "1", process.Threads: "2", }), report.MakeProcessNodeID("", "4"): report.MakeNodeMetadataWith(map[string]string{ process.PID: "4", process.Comm: "ping", process.PPID: "2", process.Cmdline: "ping foo.bar.local", process.Threads: "0", }), report.MakeProcessNodeID("", "5"): report.MakeNodeMetadataWith(map[string]string{ process.PID: "5", process.PPID: "1", process.Cmdline: "tail -f /var/log/syslog", process.Threads: "0", }), }, } have, err := reporter.Report() if err != nil || !reflect.DeepEqual(want, have) { t.Errorf("%s (%v)", test.Diff(want, have), err) } }
func TestTagger(t *testing.T) { mtime.NowForce(time.Now()) defer mtime.NowReset() oldProcessTree := docker.NewProcessTreeStub defer func() { docker.NewProcessTreeStub = oldProcessTree }() docker.NewProcessTreeStub = func(_ process.Walker) (process.Tree, error) { return &mockProcessTree{map[int]int{3: 2}}, nil } var ( pid1NodeID = report.MakeProcessNodeID("somehost.com", "2") pid2NodeID = report.MakeProcessNodeID("somehost.com", "3") ) input := report.MakeReport() input.Process.AddNode(pid1NodeID, report.MakeNodeWith(map[string]string{process.PID: "2"})) input.Process.AddNode(pid2NodeID, report.MakeNodeWith(map[string]string{process.PID: "3"})) have, err := docker.NewTagger(mockRegistryInstance, nil).Tag(input) if err != nil { t.Errorf("%v", err) } // Processes should be tagged with their container ID, and parents for _, nodeID := range []string{pid1NodeID, pid2NodeID} { node, ok := have.Process.Nodes[nodeID] if !ok { t.Errorf("Expected process node %s, but not found", nodeID) } // node should have the container id added if have, ok := node.Latest.Lookup(docker.ContainerID); !ok || have != "ping" { t.Errorf("Expected process node %s to have container id %q, got %q", nodeID, "ping", have) } // node should have the container as a parent if have, ok := node.Parents.Lookup(report.Container); !ok || !have.Contains(report.MakeContainerNodeID("ping")) { t.Errorf("Expected process node %s to have container %q as a parent, got %q", nodeID, "ping", have) } // node should have the container image as a parent if have, ok := node.Parents.Lookup(report.ContainerImage); !ok || !have.Contains(report.MakeContainerImageNodeID("baz")) { t.Errorf("Expected process node %s to have container image %q as a parent, got %q", nodeID, "baz", have) } } }
func TestEdgeID(t *testing.T) { for _, bad := range []string{ client54001EndpointNodeID, client54002EndpointNodeID, unknown1EndpointNodeID, unknown2EndpointNodeID, unknown3EndpointNodeID, clientAddressNodeID, serverAddressNodeID, unknownAddressNodeID, clientHostNodeID, serverHostNodeID, ">1.2.3.4", ">", ";", "", } { if srcNodeID, dstNodeID, ok := report.ParseEdgeID(bad); ok { t.Errorf("%q: expected failure, but got (%q, %q)", bad, srcNodeID, dstNodeID) } } for input, want := range map[string]struct{ srcNodeID, dstNodeID string }{ report.MakeEdgeID("a", report.MakeEndpointNodeID("a", "b", "c")): {"a", report.MakeEndpointNodeID("a", "b", "c")}, report.MakeEdgeID("a", report.MakeAddressNodeID("a", "b")): {"a", report.MakeAddressNodeID("a", "b")}, report.MakeEdgeID("a", report.MakeProcessNodeID("a", "b")): {"a", report.MakeProcessNodeID("a", "b")}, report.MakeEdgeID("a", report.MakeHostNodeID("a")): {"a", report.MakeHostNodeID("a")}, "host.com|1.2.3.4": {"host.com", "1.2.3.4"}, "a|b;c": {"a", "b;c"}, "a|b": {"a", "b"}, "a|": {"a", ""}, "|b": {"", "b"}, "|": {"", ""}, } { srcNodeID, dstNodeID, ok := report.ParseEdgeID(input) if !ok { t.Errorf("%q: not OK", input) continue } if want, have := want.srcNodeID, srcNodeID; want != have { t.Errorf("%q: want %q, have %q", input, want, have) } if want, have := want.dstNodeID, dstNodeID; want != have { t.Errorf("%q: want %q, have %q", input, want, have) } } }
// MapEndpoint2Process maps endpoint Nodes to process // Nodes. // // If this function is given a pseudo node, then it will just return it; // Pseudo nodes will never have pids in them, and therefore will never // be able to be turned into a Process node. // // Otherwise, this function will produce a node with the correct ID // format for a process, but without any Major or Minor labels. // It does not have enough info to do that, and the resulting graph // must be merged with a process graph to get that info. func MapEndpoint2Process(n report.Node, local report.Networks) report.Nodes { // Nodes without a hostid are treated as pseudo nodes if _, ok := n.Latest.Lookup(report.HostNodeID); !ok { return MapEndpoint2Pseudo(n, local) } pid, timestamp, ok := n.Latest.LookupEntry(process.PID) if !ok { return report.Nodes{} } id := report.MakeProcessNodeID(report.ExtractHostID(n), pid) node := NewDerivedNode(id, n).WithTopology(report.Process) node.Latest = node.Latest.Set(process.PID, timestamp, pid) node.Counters = node.Counters.Add(n.Topology, 1) return report.Nodes{id: node} }
func (r *Reporter) processTopology() (report.Topology, error) { t := report.NewTopology() err := r.walker.Walk(func(p Process) { pidstr := strconv.Itoa(p.PID) nodeID := report.MakeProcessNodeID(r.scope, pidstr) t.NodeMetadatas[nodeID] = report.MakeNodeMetadataWith(map[string]string{ PID: pidstr, Comm: p.Comm, Cmdline: p.Cmdline, Threads: strconv.Itoa(p.Threads), }) if p.PPID > 0 { t.NodeMetadatas[nodeID].Metadata[PPID] = strconv.Itoa(p.PPID) } }) return t, err }
func (r *reporter) processTopology() (report.Topology, error) { t := report.NewTopology() err := Walk(r.procRoot, func(p *Process) { pidstr := strconv.Itoa(p.PID) nodeID := report.MakeProcessNodeID(r.scope, pidstr) t.NodeMetadatas[nodeID] = report.NodeMetadata{ PID: pidstr, Comm: p.Comm, Cmdline: p.Cmdline, Threads: strconv.Itoa(p.Threads), } if p.PPID > 0 { t.NodeMetadatas[nodeID][PPID] = strconv.Itoa(p.PPID) } }) return t, err }
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 (r *Reporter) processTopology() (report.Topology, error) { t := report.MakeTopology() err := r.walker.Walk(func(p Process) { pidstr := strconv.Itoa(p.PID) nodeID := report.MakeProcessNodeID(r.scope, pidstr) t.Nodes[nodeID] = report.MakeNode() for _, tuple := range []struct{ key, value string }{ {PID, pidstr}, {Comm, p.Comm}, {Cmdline, p.Cmdline}, {Threads, strconv.Itoa(p.Threads)}, } { if tuple.value != "" { t.Nodes[nodeID].Metadata[tuple.key] = tuple.value } } if p.PPID > 0 { t.Nodes[nodeID].Metadata[PPID] = strconv.Itoa(p.PPID) } }) return t, err }
NonContainerComm = "bash" ClientHostNodeID = report.MakeHostNodeID(ClientHostID) ServerHostNodeID = report.MakeHostNodeID(ServerHostID) Client54001NodeID = report.MakeEndpointNodeID(ClientHostID, ClientIP, ClientPort54001) // curl (1) Client54002NodeID = report.MakeEndpointNodeID(ClientHostID, ClientIP, ClientPort54002) // curl (2) Server80NodeID = report.MakeEndpointNodeID(ServerHostID, ServerIP, ServerPort) // apache UnknownClient1NodeID = report.MakeEndpointNodeID(ServerHostID, UnknownClient1IP, UnknownClient1Port) // we want to ensure two unknown clients, connnected UnknownClient2NodeID = report.MakeEndpointNodeID(ServerHostID, UnknownClient2IP, UnknownClient2Port) // to the same server, are deduped. UnknownClient3NodeID = report.MakeEndpointNodeID(ServerHostID, UnknownClient3IP, UnknownClient3Port) // Check this one isn't deduped RandomClientNodeID = report.MakeEndpointNodeID(ServerHostID, RandomClientIP, RandomClientPort) // this should become an internet node NonContainerNodeID = report.MakeEndpointNodeID(ServerHostID, ServerIP, NonContainerClientPort) GoogleEndpointNodeID = report.MakeEndpointNodeID(ServerHostID, GoogleIP, GooglePort) ClientProcess1NodeID = report.MakeProcessNodeID(ClientHostID, Client1PID) ClientProcess2NodeID = report.MakeProcessNodeID(ClientHostID, Client2PID) ServerProcessNodeID = report.MakeProcessNodeID(ServerHostID, ServerPID) NonContainerProcessNodeID = report.MakeProcessNodeID(ServerHostID, NonContainerPID) ClientContainerID = "a1b2c3d4e5" ServerContainerID = "5e4d3c2b1a" ClientContainerNodeID = report.MakeContainerNodeID(ClientHostID, ClientContainerID) ServerContainerNodeID = report.MakeContainerNodeID(ServerHostID, ServerContainerID) ClientContainerImageID = "imageid123" ServerContainerImageID = "imageid456" ClientContainerImageNodeID = report.MakeContainerNodeID(ClientHostID, ClientContainerImageID) ServerContainerImageNodeID = report.MakeContainerNodeID(ServerHostID, ServerContainerImageID) ClientContainerImageName = "image/client" ServerContainerImageName = "image/server"
func TestReporter(t *testing.T) { processes := []process.Process{ {PID: 1, PPID: 0, Name: "init"}, {PID: 2, PPID: 1, Name: "bash"}, {PID: 3, PPID: 1, Name: "apache", Threads: 2}, {PID: 4, PPID: 2, Name: "ping", Cmdline: "ping foo.bar.local"}, {PID: 5, PPID: 1, Cmdline: "tail -f /var/log/syslog"}, } walker := &mockWalker{processes: processes} getDeltaTotalJiffies := func() (uint64, float64, error) { return 0, 0., nil } now := time.Now() mtime.NowForce(now) defer mtime.NowReset() rpt, err := process.NewReporter(walker, "", getDeltaTotalJiffies).Report() if err != nil { t.Error(err) } // It reports the init process node, ok := rpt.Process.Nodes[report.MakeProcessNodeID("", "1")] if !ok { t.Errorf("Expected report to include the pid 1 init") } if name, ok := node.Latest.Lookup(process.Name); !ok || name != processes[0].Name { t.Errorf("Expected %q got %q", processes[0].Name, name) } // It reports plain processes (with parent pid, and metrics) node, ok = rpt.Process.Nodes[report.MakeProcessNodeID("", "2")] if !ok { t.Errorf("Expected report to include the pid 2 bash") } if name, ok := node.Latest.Lookup(process.Name); !ok || name != processes[1].Name { t.Errorf("Expected %q got %q", processes[1].Name, name) } if ppid, ok := node.Latest.Lookup(process.PPID); !ok || ppid != fmt.Sprint(processes[1].PPID) { t.Errorf("Expected %d got %q", processes[1].PPID, ppid) } if memoryUsage, ok := node.Metrics[process.MemoryUsage]; !ok { t.Errorf("Expected memory usage metric, but not found") } else if sample := memoryUsage.LastSample(); sample == nil { t.Errorf("Expected memory usage metric to have a sample, but there were none") } else if sample.Value != 0. { t.Errorf("Expected memory usage metric sample %f, got %f", 0., sample.Value) } // It reports thread-counts node, ok = rpt.Process.Nodes[report.MakeProcessNodeID("", "3")] if !ok { t.Errorf("Expected report to include the pid 3 apache") } if threads, ok := node.Latest.Lookup(process.Threads); !ok || threads != fmt.Sprint(processes[2].Threads) { t.Errorf("Expected %d got %q", processes[2].Threads, threads) } // It reports the Cmdline node, ok = rpt.Process.Nodes[report.MakeProcessNodeID("", "4")] if !ok { t.Errorf("Expected report to include the pid 4 ping") } if cmdline, ok := node.Latest.Lookup(process.Cmdline); !ok || cmdline != fmt.Sprint(processes[3].Cmdline) { t.Errorf("Expected %q got %q", processes[3].Cmdline, cmdline) } // It reports processes without a Name node, ok = rpt.Process.Nodes[report.MakeProcessNodeID("", "5")] if !ok { t.Errorf("Expected report to include the pid 5 tail") } if name, ok := node.Latest.Lookup(process.Name); ok { t.Errorf("Expected no name, but got %q", name) } if cmdline, ok := node.Latest.Lookup(process.Cmdline); !ok || cmdline != fmt.Sprint(processes[4].Cmdline) { t.Errorf("Expected %q got %q", processes[4].Cmdline, cmdline) } }