diff --git a/app/api_topology.go b/app/api_topology.go index 0f8af047bf..e44b3e3299 100644 --- a/app/api_topology.go +++ b/app/api_topology.go @@ -66,7 +66,7 @@ func handleNode(rep Reporter, t topologyView, w http.ResponseWriter, r *http.Req http.NotFound(w, r) return } - originHostFunc := func(id string) (OriginHost, bool) { return getOriginHost(rpt.HostMetadatas, id) } + originHostFunc := func(id string) (OriginHost, bool) { return getOriginHost(rpt.Host, id) } originNodeFunc := func(id string) (OriginNode, bool) { return getOriginNode(t.selector(rpt), id) } respondWith(w, http.StatusOK, APINode{Node: makeDetailed(node, originHostFunc, originNodeFunc)}) } diff --git a/app/detail_pane.go b/app/detail_pane.go index 2f607f2f27..aaa1f84359 100644 --- a/app/detail_pane.go +++ b/app/detail_pane.go @@ -56,9 +56,8 @@ outer: Numeric: false, Rows: []report.Row{ {"Hostname", host.Hostname, ""}, - {"Load", fmt.Sprintf("%.2f %.2f %.2f", host.LoadOne, host.LoadFive, host.LoadFifteen), ""}, + {"Load", host.Load, ""}, {"OS", host.OS, ""}, - //{"Addresses", strings.Join(host.Addresses, ", "), ""}, {"ID", id, ""}, }, }) @@ -75,12 +74,10 @@ outer: func unknownOriginHost(id string) OriginHost { return OriginHost{ - Hostname: fmt.Sprintf("[%s]", id), - OS: "unknown", - Addresses: []string{}, - LoadOne: 0.0, - LoadFive: 0.0, - LoadFifteen: 0.0, + Hostname: fmt.Sprintf("[%s]", id), + OS: "unknown", + Networks: []string{}, + Load: "", } } diff --git a/app/mock_reporter_test.go b/app/mock_reporter_test.go index 41ad030b74..ad7463677c 100644 --- a/app/mock_reporter_test.go +++ b/app/mock_reporter_test.go @@ -115,21 +115,23 @@ func (s StaticReport) Report() report.Report { }, }, - HostMetadatas: report.HostMetadatas{ - "hostA": report.HostMetadata{ - Hostname: "node-a.local", - LocalNets: []*net.IPNet{localNet}, - OS: "Linux", - LoadOne: 3.1415, - LoadFive: 2.7182, - LoadFifteen: 1.6180, - }, - "hostB": report.HostMetadata{ - Hostname: "node-b.local", - LocalNets: []*net.IPNet{localNet}, - OS: "Linux", + Host: report.Topology{ + Adjacency: report.Adjacency{}, + EdgeMetadatas: report.EdgeMetadatas{}, + NodeMetadatas: report.NodeMetadatas{ + report.MakeHostNodeID("hostA"): report.NodeMetadata{ + "host_name": "node-a.local", + "os": "Linux", + "local_networks": localNet.String(), + "load": "3.14 2.71 1.61", + }, + report.MakeHostNodeID("hostB"): report.NodeMetadata{ + "host_name": "node-b.local", + "os": "Linux", + "local_networks": localNet.String(), + }, }, }, } - return testReport.SquashRemote() + return testReport.Squash() } diff --git a/app/origin_host.go b/app/origin_host.go index a7b722ee60..d1d4d37573 100644 --- a/app/origin_host.go +++ b/app/origin_host.go @@ -2,6 +2,7 @@ package main import ( "net/http" + "strings" "github.com/gorilla/mux" @@ -12,32 +13,23 @@ import ( // some data in the system. The struct is returned by the /api/origin/{id} // handler. type OriginHost struct { - Hostname string `json:"hostname"` - OS string `json:"os"` - Addresses []string `json:"addresses"` - LoadOne float64 `json:"load_one"` - LoadFive float64 `json:"load_five"` - LoadFifteen float64 `json:"load_fifteen"` + Hostname string `json:"hostname"` + OS string `json:"os"` + Networks []string `json:"networks"` + Load string `json:"load"` } -func getOriginHost(mds report.HostMetadatas, nodeID string) (OriginHost, bool) { - host, ok := mds[nodeID] +func getOriginHost(t report.Topology, nodeID string) (OriginHost, bool) { + host, ok := t.NodeMetadatas[nodeID] if !ok { return OriginHost{}, false } - var addrs []string - for _, l := range host.LocalNets { - addrs = append(addrs, l.String()) - } - return OriginHost{ - Hostname: host.Hostname, - OS: host.OS, - Addresses: addrs, - LoadOne: host.LoadOne, - LoadFive: host.LoadFive, - LoadFifteen: host.LoadFifteen, + Hostname: host["host_name"], + OS: host["os"], + Networks: strings.Split(host["local_networks"], " "), + Load: host["load"], }, true } @@ -48,7 +40,7 @@ func makeOriginHostHandler(rep Reporter) http.HandlerFunc { vars = mux.Vars(r) nodeID = vars["id"] ) - origin, ok := getOriginHost(rep.Report().HostMetadatas, nodeID) + origin, ok := getOriginHost(rep.Report().Host, nodeID) if !ok { http.NotFound(w, r) return diff --git a/app/origin_host_test.go b/app/origin_host_test.go index 6ff9830413..c814b95e13 100644 --- a/app/origin_host_test.go +++ b/app/origin_host_test.go @@ -15,7 +15,7 @@ func TestAPIOriginHost(t *testing.T) { { // Origin - body := getRawJSON(t, ts, "/api/origin/host/hostA") + body := getRawJSON(t, ts, "/api/origin/host/hostA;") // TODO MakeHostNodeID var o OriginHost if err := json.Unmarshal(body, &o); err != nil { t.Fatalf("JSON parse error: %s", err) @@ -23,13 +23,7 @@ func TestAPIOriginHost(t *testing.T) { if want, have := "Linux", o.OS; want != have { t.Errorf("Origin error. Want %v, have %v", want, have) } - if want, have := 3.1415, o.LoadOne; want != have { - t.Errorf("Origin error. Want %v, have %v", want, have) - } - if want, have := 2.7182, o.LoadFive; want != have { - t.Errorf("Origin error. Want %v, have %v", want, have) - } - if want, have := 1.6180, o.LoadFifteen; want != have { + if want, have := "3.14 2.71 1.61", o.Load; want != have { t.Errorf("Origin error. Want %v, have %v", want, have) } } diff --git a/app/report_lifo.go b/app/report_lifo.go index 398015a2f8..94c594e746 100644 --- a/app/report_lifo.go +++ b/app/report_lifo.go @@ -41,7 +41,7 @@ func NewReportLIFO(r reporter, maxAge time.Duration) *ReportLIFO { select { case report := <-r.Reports(): // Incoming report from the collecter. - report = report.SquashRemote() // TODO?: make this a CLI argument. + report = report.Squash() // TODO?: make this a CLI argument. tr := timedReport{ Timestamp: time.Now(), Report: report, diff --git a/app/scope_test.go b/app/scope_test.go index 68d350ae18..9cfb35212f 100644 --- a/app/scope_test.go +++ b/app/scope_test.go @@ -71,17 +71,19 @@ func checkRequest(t *testing.T, ts *httptest.Server, method, path string, body [ func getRawJSON(t *testing.T, ts *httptest.Server, path string) []byte { res, body := checkGet(t, ts, path) + _, file, line, _ := runtime.Caller(1) + file = filepath.Base(file) if res.StatusCode != 200 { - t.Fatalf("Expected status %d, got %d. Path: %s", 200, res.StatusCode, path) + t.Fatalf("%s:%d: Expected status %d, got %d. Path: %s", file, line, 200, res.StatusCode, path) } foundCtype := res.Header.Get("content-type") if foundCtype != "application/json" { - t.Errorf("Wrong Content-type for JSON: %s", foundCtype) + t.Errorf("%s:%d: Wrong Content-type for JSON: %s", file, line, foundCtype) } if len(body) == 0 { - t.Errorf("No response body") + t.Errorf("%s:%d: No response body", file, line) } // fmt.Printf("Body: %s", body) diff --git a/experimental/bridge/main.go b/experimental/bridge/main.go index cbd2860b5c..020c89a2cb 100644 --- a/experimental/bridge/main.go +++ b/experimental/bridge/main.go @@ -127,7 +127,7 @@ func discover(c collector, p publisher, fixed []string) { var ( now = time.Now() - localNets = r.LocalNets() + localNets = r.LocalNetworks() ) for _, adjacent := range r.Address.Adjacency { diff --git a/experimental/demoprobe/generate.go b/experimental/demoprobe/generate.go index b8f732f3c9..90fae1ad14 100644 --- a/experimental/demoprobe/generate.go +++ b/experimental/demoprobe/generate.go @@ -112,11 +112,11 @@ func DemoReport(nodeCount int) report.Report { r.Address.Adjacency[nodeDstAddressID] = r.Address.Adjacency[nodeDstAddressID].Add(srcAddressID) // Host data - r.HostMetadatas["hostX"] = report.HostMetadata{ - Timestamp: time.Now().UTC(), - Hostname: "host-x", - LocalNets: []*net.IPNet{localNet}, - OS: "linux", + r.Host.NodeMetadatas["hostX"] = report.NodeMetadata{ + "ts": time.Now().UTC().Format(time.RFC3339Nano), + "host_name": "host-x", + "local_networks": localNet.String(), + "os": "linux", } } diff --git a/experimental/genreport/generate.go b/experimental/genreport/generate.go index a2f395210c..1aafe1ee95 100644 --- a/experimental/genreport/generate.go +++ b/experimental/genreport/generate.go @@ -112,11 +112,11 @@ func DemoReport(nodeCount int) report.Report { r.Address.Adjacency[nodeDstAddressID] = r.Address.Adjacency[nodeDstAddressID].Add(srcAddressID) // Host data - r.HostMetadatas["hostX"] = report.HostMetadata{ - Timestamp: time.Now().UTC(), - Hostname: "host-x", - LocalNets: []*net.IPNet{localNet}, - OS: "linux", + r.Host.NodeMetadatas["hostX"] = report.NodeMetadata{ + "ts": time.Now().UTC().Format(time.RFC3339Nano), + "host_name": "host-x", + "local_networks": localNet.String(), + "os": "linux", } } diff --git a/experimental/graphviz/report_lifo.go b/experimental/graphviz/report_lifo.go index 1f7ba6aa00..d28572e457 100644 --- a/experimental/graphviz/report_lifo.go +++ b/experimental/graphviz/report_lifo.go @@ -41,7 +41,7 @@ func NewReportLIFO(r reporter, maxAge time.Duration) *ReportLIFO { for { select { case report := <-r.Reports(): - report = report.SquashRemote() + report = report.Squash() tr := timedReport{ Timestamp: time.Now(), Report: report, diff --git a/probe/main.go b/probe/main.go index 58b8afc3f9..c6fb602a2a 100644 --- a/probe/main.go +++ b/probe/main.go @@ -9,6 +9,7 @@ import ( "os/signal" "runtime" "strconv" + "strings" "syscall" "time" @@ -79,8 +80,8 @@ func main() { defer close(quit) go func() { var ( - hostname = hostname() - nodeID = hostname // TODO: we should sanitize the hostname + hostName = hostname() + hostID = hostName // TODO: we should sanitize the hostname pubTick = time.Tick(*publishInterval) spyTick = time.Tick(*spyInterval) r = report.MakeReport() @@ -90,12 +91,12 @@ func main() { select { case <-pubTick: publishTicks.WithLabelValues().Add(1) - r.HostMetadatas[nodeID] = hostMetadata(hostname) + r.Host = hostTopology(hostID, hostName) publisher.Publish(r) r = report.MakeReport() case <-spyTick: - r.Merge(spy(hostname, hostname, *spyProcs)) + r.Merge(spy(hostID, hostName, *spyProcs)) r = tag.Apply(r, taggers) // log.Printf("merged report:\n%#v\n", r) @@ -108,34 +109,31 @@ func main() { log.Printf("%s", <-interrupt()) } -func interrupt() chan os.Signal { - c := make(chan os.Signal) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - return c -} - -// hostMetadata produces an instantaneous HostMetadata for this host. No need -// to do this more than once per published report. -func hostMetadata(hostname string) report.HostMetadata { - loadOne, loadFive, loadFifteen := getLoads() - - host := report.HostMetadata{ - Timestamp: time.Now().UTC(), - Hostname: hostname, - OS: runtime.GOOS, - LoadOne: loadOne, - LoadFive: loadFive, - LoadFifteen: loadFifteen, - } - +// hostTopology produces a host topology for this host. No need to do this +// more than once per published report. +func hostTopology(hostID, hostName string) report.Topology { + var localCIDRs []string if localNets, err := net.InterfaceAddrs(); err == nil { // Not all networks are IP networks. for _, localNet := range localNets { - if net, ok := localNet.(*net.IPNet); ok { - host.LocalNets = append(host.LocalNets, net) + if ipNet, ok := localNet.(*net.IPNet); ok { + localCIDRs = append(localCIDRs, ipNet.String()) } } } + t := report.NewTopology() + t.NodeMetadatas[report.MakeHostNodeID(hostID)] = report.NodeMetadata{ + "ts": time.Now().UTC().Format(time.RFC3339Nano), + "host_name": hostName, + "local_networks": strings.Join(localCIDRs, " "), + "os": runtime.GOOS, + "load": getLoad(), + } + return t +} - return host +func interrupt() chan os.Signal { + c := make(chan os.Signal) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + return c } diff --git a/probe/system_darwin.go b/probe/system_darwin.go index 33432e0ad9..81874c322a 100644 --- a/probe/system_darwin.go +++ b/probe/system_darwin.go @@ -1,33 +1,21 @@ package main import ( + "fmt" "os/exec" - "strconv" - "strings" + "regexp" ) -func getLoads() (float64, float64, float64) { +var loadRe = regexp.MustCompile(`load average\: ([0-9\.]+), ([0-9\.]+), ([0-9\.]+)`) + +func getLoad() string { out, err := exec.Command("w").CombinedOutput() if err != nil { - return -1, -1, -1 - } - noCommas := strings.NewReplacer(",", "") - firstLine := strings.Split(string(out), "\n")[0] - toks := strings.Fields(firstLine) - if len(toks) < 5 { - return -1, -1, -1 - } - one, err := strconv.ParseFloat(noCommas.Replace(toks[len(toks)-3]), 64) - if err != nil { - return -1, -1, -1 + return "unknown" } - five, err := strconv.ParseFloat(noCommas.Replace(toks[len(toks)-2]), 64) - if err != nil { - return -1, -1, -1 - } - fifteen, err := strconv.ParseFloat(noCommas.Replace(toks[len(toks)-1]), 64) - if err != nil { - return -1, -1, -1 + matches := loadRe.FindAllStringSubmatch(string(out), -1) + if matches == nil || len(matches) < 1 || len(matches[0]) < 4 { + return "unknown" } - return one, five, fifteen + return fmt.Sprintf("%s %s %s", matches[0][1], matches[0][2], matches[0][3]) } diff --git a/probe/system_linux.go b/probe/system_linux.go index 418aebef40..257abf125e 100644 --- a/probe/system_linux.go +++ b/probe/system_linux.go @@ -1,31 +1,32 @@ package main import ( + "fmt" "io/ioutil" "strconv" "strings" ) -func getLoads() (float64, float64, float64) { +func getLoad() string { buf, err := ioutil.ReadFile("/proc/loadavg") if err != nil { - return -1, -1, -1 + return "unknown" } toks := strings.Fields(string(buf)) if len(toks) < 3 { - return -1, -1, -1 + return "unknown" } one, err := strconv.ParseFloat(toks[0], 64) if err != nil { - return -1, -1, -1 + return "unknown" } five, err := strconv.ParseFloat(toks[1], 64) if err != nil { - return -1, -1, -1 + return "unknown" } fifteen, err := strconv.ParseFloat(toks[2], 64) if err != nil { - return -1, -1, -1 + return "unknown" } - return one, five, fifteen + return fmt.Sprintf("%.2f %.2f %.2f", one, five, fifteen) } diff --git a/report/diff_test.go b/report/diff_test.go new file mode 100644 index 0000000000..88d8640cce --- /dev/null +++ b/report/diff_test.go @@ -0,0 +1,21 @@ +package report_test + +import ( + "github.com/davecgh/go-spew/spew" + "github.com/pmezard/go-difflib/difflib" +) + +func init() { + spew.Config.SortKeys = true // :\ +} + +func diff(want, have interface{}) string { + text, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ + A: difflib.SplitLines(spew.Sdump(want)), + B: difflib.SplitLines(spew.Sdump(have)), + FromFile: "want", + ToFile: "have", + Context: 5, + }) + return "\n" + text +} diff --git a/report/fixture_test.go b/report/fixture_test.go deleted file mode 100644 index 9aa92c23fd..0000000000 --- a/report/fixture_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package report_test - -import ( - "github.com/weaveworks/scope/report" -) - -var ( - clientHostID = "client.host.com" - clientHostName = clientHostID - clientHostNodeID = report.MakeHostNodeID(clientHostID) - clientAddress = "10.10.10.20" - serverHostID = "server.host.com" - serverHostName = serverHostID - serverHostNodeID = report.MakeHostNodeID(serverHostID) - serverAddress = "10.10.10.1" - unknownHostID = "" // by definition, we don't know it - unknownAddress = "172.16.93.112" // will be a pseudonode, no corresponding host - - client54001EndpointNodeID = report.MakeEndpointNodeID(clientHostID, clientAddress, "54001") // i.e. curl - client54002EndpointNodeID = report.MakeEndpointNodeID(clientHostID, clientAddress, "54002") // also curl - server80EndpointNodeID = report.MakeEndpointNodeID(serverHostID, serverAddress, "80") // i.e. apache - unknown1EndpointNodeID = report.MakeEndpointNodeID(unknownHostID, unknownAddress, "10001") - unknown2EndpointNodeID = report.MakeEndpointNodeID(unknownHostID, unknownAddress, "10002") - unknown3EndpointNodeID = report.MakeEndpointNodeID(unknownHostID, unknownAddress, "10003") - - clientAddressNodeID = report.MakeAddressNodeID(clientHostID, clientAddress) - serverAddressNodeID = report.MakeAddressNodeID(serverHostID, serverAddress) - unknownAddressNodeID = report.MakeAddressNodeID(unknownHostID, unknownAddress) -) diff --git a/report/merge.go b/report/merge.go index 2235c92ae6..9bce1a8631 100644 --- a/report/merge.go +++ b/report/merge.go @@ -8,7 +8,7 @@ package report func (r *Report) Merge(other Report) { r.Endpoint.Merge(other.Endpoint) r.Address.Merge(other.Address) - r.HostMetadatas.Merge(other.HostMetadatas) + r.Host.Merge(other.Host) } // Merge merges another Topology into the receiver. @@ -45,20 +45,6 @@ func (e *EdgeMetadatas) Merge(other EdgeMetadatas) { } } -// Merge merges another HostMetadata into the receiver. -// It'll takes the lastest version if there are conflicts. -func (e *HostMetadatas) Merge(other HostMetadatas) { - for hostID, meta := range other { - if existing, ok := (*e)[hostID]; ok { - // Conflict. Take the newest. - if existing.Timestamp.After(meta.Timestamp) { - continue - } - } - (*e)[hostID] = meta - } -} - // Merge merges another EdgeMetadata into the receiver. The two edge metadatas // should represent the same edge on different times. func (m *EdgeMetadata) Merge(other EdgeMetadata) { diff --git a/report/merge_test.go b/report/merge_test.go index 5b34b46872..78007a59b8 100644 --- a/report/merge_test.go +++ b/report/merge_test.go @@ -3,7 +3,6 @@ package report import ( "reflect" "testing" - "time" ) func TestMergeAdjacency(t *testing.T) { @@ -215,107 +214,6 @@ func TestMergeEdgeMetadatas(t *testing.T) { } } -func TestMergeHostMetadatas(t *testing.T) { - now := time.Now() - - for name, c := range map[string]struct { - a, b, want HostMetadatas - }{ - "Empty a": { - a: HostMetadatas{}, - b: HostMetadatas{ - "hostA": HostMetadata{ - Timestamp: now, - Hostname: "host-a", - OS: "linux", - }, - }, - want: HostMetadatas{ - "hostA": HostMetadata{ - Timestamp: now, - Hostname: "host-a", - OS: "linux", - }, - }, - }, - "Empty b": { - a: HostMetadatas{ - "hostA": HostMetadata{ - Timestamp: now, - Hostname: "host-a", - OS: "linux", - }, - }, - b: HostMetadatas{}, - want: HostMetadatas{ - "hostA": HostMetadata{ - Timestamp: now, - Hostname: "host-a", - OS: "linux", - }, - }, - }, - "Host merge": { - a: HostMetadatas{ - "hostA": HostMetadata{ - Timestamp: now, - Hostname: "host-a", - OS: "linux", - }, - }, - b: HostMetadatas{ - "hostB": HostMetadata{ - Timestamp: now, - Hostname: "host-b", - OS: "freedos", - }, - }, - want: HostMetadatas{ - "hostB": HostMetadata{ - Timestamp: now, - Hostname: "host-b", - OS: "freedos", - }, - "hostA": HostMetadata{ - Timestamp: now, - Hostname: "host-a", - OS: "linux", - }, - }, - }, - "Host conflict": { - a: HostMetadatas{ - "hostA": HostMetadata{ - Timestamp: now, - Hostname: "host-a", - OS: "linux1", - }, - }, - b: HostMetadatas{ - "hostA": HostMetadata{ - Timestamp: now.Add(-10 * time.Second), - Hostname: "host-a", - OS: "linux0", - }, - }, - want: HostMetadatas{ - "hostA": HostMetadata{ - Timestamp: now, - Hostname: "host-a", - OS: "linux1", - }, - }, - }, - } { - have := c.a - have.Merge(c.b) - - if !reflect.DeepEqual(c.want, have) { - t.Errorf("%s: want\n\t%#v, have\n\t%#v", name, c.want, have) - } - } -} - func TestMergeNodeMetadatas(t *testing.T) { for name, c := range map[string]struct { a, b, want NodeMetadatas diff --git a/report/report.go b/report/report.go index 5c5621c7ff..be63a555a6 100644 --- a/report/report.go +++ b/report/report.go @@ -1,9 +1,8 @@ package report import ( - "encoding/json" "net" - "time" + "strings" ) // Report is the core data type. It's produced by probes, and consumed and @@ -20,20 +19,10 @@ type Report struct { // endpoints (e.g. ICMP). Edges are present. Address Topology - HostMetadatas -} - -// HostMetadatas contains metadata about the host(s) represented in the Report. -type HostMetadatas map[string]HostMetadata - -// HostMetadata describes metadata that probes can collect about the host that -// they run on. It has a timestamp when the measurement was made. -type HostMetadata struct { - Timestamp time.Time - Hostname string - LocalNets []*net.IPNet - OS string - LoadOne, LoadFive, LoadFifteen float64 + // Host nodes are physical hosts that run probes. Metadata includes things + // like operating system, load, etc. The information is scraped by the + // probes with each published report. Edges are not present. + Host Topology } // RenderableNode is the data type that's yielded to the JavaScript layer as @@ -79,69 +68,46 @@ type Row struct { // MakeReport makes a clean report, ready to Merge() other reports into. func MakeReport() Report { return Report{ - Endpoint: NewTopology(), - Address: NewTopology(), - HostMetadatas: map[string]HostMetadata{}, + Endpoint: NewTopology(), + Address: NewTopology(), + Host: NewTopology(), } } -// SquashRemote folds all remote nodes into a special supernode. It uses the -// LocalNets of the hosts in HostMetadata to determine which addresses are -// local. -func (r Report) SquashRemote() Report { - localNets := r.HostMetadatas.LocalNets() - return Report{ - Endpoint: Squash(r.Endpoint, EndpointIDAddresser, localNets), - Address: Squash(r.Address, AddressIDAddresser, localNets), - HostMetadatas: r.HostMetadatas, - } +// Squash squashes all non-local nodes in the report to a super-node called +// the Internet. +func (r Report) Squash() Report { + localNetworks := r.LocalNetworks() + r.Endpoint = r.Endpoint.Squash(EndpointIDAddresser, localNetworks) + r.Address = r.Address.Squash(AddressIDAddresser, localNetworks) + r.Host = r.Host.Squash(PanicIDAddresser, localNetworks) + return r } -// LocalNets gives the union of all local network IPNets for all hosts -// represented in the HostMetadatas. -func (m HostMetadatas) LocalNets() []*net.IPNet { - var nets []*net.IPNet - for _, node := range m { - OUTER: - for _, local := range node.LocalNets { - for _, existing := range nets { - if existing == local { - continue OUTER +// LocalNetworks returns a superset of the networks (think: CIDRs) that are +// "local" from the perspective of each host represented in the report. It's +// used to determine which nodes in the report are "remote", i.e. outside of +// our infrastructure. +func (r Report) LocalNetworks() []*net.IPNet { + var ipNets []*net.IPNet + for _, md := range r.Host.NodeMetadatas { + val, ok := md["local_networks"] + if !ok { + continue + } + outer: + for _, s := range strings.Fields(val) { + _, ipNet, err := net.ParseCIDR(s) + if err != nil { + continue + } + for _, existing := range ipNets { + if ipNet.String() == existing.String() { + continue outer } } - nets = append(nets, local) + ipNets = append(ipNets, ipNet) } } - return nets -} - -// UnmarshalJSON is a custom JSON deserializer for HostMetadata to deal with -// the Localnets. -func (m *HostMetadata) UnmarshalJSON(data []byte) error { - type netmask struct { - IP net.IP - Mask []byte - } - tmpHMD := struct { - Timestamp time.Time - Hostname string - LocalNets []*netmask - OS string - LoadOne, LoadFive, LoadFifteen float64 - }{} - err := json.Unmarshal(data, &tmpHMD) - if err != nil { - return err - } - - m.Timestamp = tmpHMD.Timestamp - m.Hostname = tmpHMD.Hostname - m.OS = tmpHMD.OS - m.LoadOne = tmpHMD.LoadOne - m.LoadFive = tmpHMD.LoadFive - m.LoadFifteen = tmpHMD.LoadFifteen - for _, ln := range tmpHMD.LocalNets { - m.LocalNets = append(m.LocalNets, &net.IPNet{IP: ln.IP, Mask: ln.Mask}) - } - return nil + return ipNets } diff --git a/report/report_fixture_test.go b/report/report_fixture_test.go new file mode 100644 index 0000000000..010f3651b6 --- /dev/null +++ b/report/report_fixture_test.go @@ -0,0 +1,166 @@ +package report_test + +import ( + "github.com/weaveworks/scope/report" +) + +var reportFixture = report.Report{ + Endpoint: report.Topology{ + Adjacency: report.Adjacency{ + report.MakeAdjacencyID(clientHostID, client54001EndpointNodeID): report.MakeIDList(server80EndpointNodeID), + report.MakeAdjacencyID(clientHostID, client54002EndpointNodeID): report.MakeIDList(server80EndpointNodeID), + report.MakeAdjacencyID(serverHostID, server80EndpointNodeID): report.MakeIDList(client54001EndpointNodeID, client54002EndpointNodeID, unknown1EndpointNodeID, unknown2EndpointNodeID, unknown3EndpointNodeID), + }, + NodeMetadatas: report.NodeMetadatas{ + client54001EndpointNodeID: report.NodeMetadata{ + "process_node_id": report.MakeProcessNodeID(clientHostID, "4242"), + "address_node_id": report.MakeAddressNodeID(clientHostID, clientAddress), + }, + client54002EndpointNodeID: report.NodeMetadata{ + //"process_node_id": report.MakeProcessNodeID(clientHostID, "4242"), // leave it out, to test a branch in Render + "address_node_id": report.MakeAddressNodeID(clientHostID, clientAddress), + }, + server80EndpointNodeID: report.NodeMetadata{ + "process_node_id": report.MakeProcessNodeID(serverHostID, "215"), + "address_node_id": report.MakeAddressNodeID(serverHostID, serverAddress), + }, + + "process-not-available": report.NodeMetadata{}, // for TestProcess{PID,Name,Container[Name]} + "process-badly-linked": report.NodeMetadata{"process_node_id": "none"}, // for TestProcess{PID,Name,Container[Name]} + "process-no-container": report.NodeMetadata{"process_node_id": "no-container"}, // for TestProcessContainer[Name] + "address-not-available": report.NodeMetadata{}, // for TestAddressHostname + "address-badly-linked": report.NodeMetadata{"address_node_id": "none"}, // for TestAddressHostname + }, + EdgeMetadatas: report.EdgeMetadatas{ + report.MakeEdgeID(client54001EndpointNodeID, server80EndpointNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 10, // src -> dst + BytesIngress: 100, // src <- dst + }, + report.MakeEdgeID(client54002EndpointNodeID, server80EndpointNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 20, + BytesIngress: 200, + }, + report.MakeEdgeID(server80EndpointNodeID, client54001EndpointNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 100, + BytesIngress: 10, + }, + report.MakeEdgeID(server80EndpointNodeID, client54002EndpointNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 200, + BytesIngress: 20, + }, + report.MakeEdgeID(server80EndpointNodeID, unknown1EndpointNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 400, + BytesIngress: 40, + }, + report.MakeEdgeID(server80EndpointNodeID, unknown2EndpointNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 800, + BytesIngress: 80, + }, + report.MakeEdgeID(server80EndpointNodeID, unknown3EndpointNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 1600, + BytesIngress: 160, + }, + }, + }, + Address: report.Topology{ + Adjacency: report.Adjacency{ + report.MakeAdjacencyID(clientHostID, clientAddressNodeID): report.MakeIDList(serverAddressNodeID), + report.MakeAdjacencyID(serverHostID, serverAddressNodeID): report.MakeIDList(clientAddressNodeID, unknownAddressNodeID), + }, + NodeMetadatas: report.NodeMetadatas{ + clientAddressNodeID: report.NodeMetadata{ + "host_name": "client.host.com", + }, + serverAddressNodeID: report.NodeMetadata{}, + + "no-host-name": report.NodeMetadata{}, + }, + EdgeMetadatas: report.EdgeMetadatas{ + report.MakeEdgeID(clientAddressNodeID, serverAddressNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 10 + 20 + 1, + BytesIngress: 100 + 200 + 2, + }, + report.MakeEdgeID(serverAddressNodeID, clientAddressNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 100 + 200 + 3, + BytesIngress: 10 + 20 + 4, + }, + report.MakeEdgeID(serverAddressNodeID, unknownAddressNodeID): report.EdgeMetadata{ + WithBytes: true, + BytesEgress: 400 + 800 + 1600 + 5, + BytesIngress: 40 + 80 + 160 + 6, + }, + }, + }, + //Process: report.Topology{ + // Adjacency: report.Adjacency{}, + // NodeMetadatas: report.NodeMetadatas{ + // report.MakeProcessNodeID(clientHostID, "4242"): report.NodeMetadata{ + // "host_name": "client.host.com", + // "pid": "4242", + // "process_name": "curl", + // "docker_container_id": "a1b2c3d4e5", + // "docker_container_name": "fixture-container", + // "docker_image_id": "0000000000", + // "docker_image_name": "fixture/container:latest", + // }, + // report.MakeProcessNodeID(serverHostID, "215"): report.NodeMetadata{ + // "pid": "215", + // "process_name": "apache", + // }, + // + // "no-container": report.NodeMetadata{}, + // }, + // EdgeMetadatas: report.EdgeMetadatas{}, + //}, + Host: report.Topology{ + Adjacency: report.Adjacency{}, + NodeMetadatas: report.NodeMetadatas{ + report.MakeHostNodeID(clientHostID): report.NodeMetadata{ + "host_name": clientHostName, + "local_networks": "10.10.10.0/24", + "os": "OS/2", + "load": "0.11 0.22 0.33", + }, + report.MakeHostNodeID(serverHostID): report.NodeMetadata{ + "host_name": serverHostName, + "local_networks": "10.10.10.0/24", + "os": "Linux", + "load": "0.01 0.01 0.01", + }, + }, + EdgeMetadatas: report.EdgeMetadatas{}, + }, +} + +var ( + clientHostID = "client.host.com" + clientHostName = clientHostID + clientHostNodeID = report.MakeHostNodeID(clientHostID) + clientAddress = "10.10.10.20" + serverHostID = "server.host.com" + serverHostName = serverHostID + serverHostNodeID = report.MakeHostNodeID(serverHostID) + serverAddress = "10.10.10.1" + unknownHostID = "" // by definition, we don't know it + unknownAddress = "172.16.93.112" // will be a pseudonode, no corresponding host + + client54001EndpointNodeID = report.MakeEndpointNodeID(clientHostID, clientAddress, "54001") // i.e. curl + client54002EndpointNodeID = report.MakeEndpointNodeID(clientHostID, clientAddress, "54002") // also curl + server80EndpointNodeID = report.MakeEndpointNodeID(serverHostID, serverAddress, "80") // i.e. apache + unknown1EndpointNodeID = report.MakeEndpointNodeID(unknownHostID, unknownAddress, "10001") + unknown2EndpointNodeID = report.MakeEndpointNodeID(unknownHostID, unknownAddress, "10002") + unknown3EndpointNodeID = report.MakeEndpointNodeID(unknownHostID, unknownAddress, "10003") + + clientAddressNodeID = report.MakeAddressNodeID(clientHostID, clientAddress) + serverAddressNodeID = report.MakeAddressNodeID(serverHostID, serverAddress) + unknownAddressNodeID = report.MakeAddressNodeID(unknownHostID, unknownAddress) +) diff --git a/report/report_test.go b/report/report_test.go index 9852115dea..a2b5ce4be5 100644 --- a/report/report_test.go +++ b/report/report_test.go @@ -1,37 +1,55 @@ -package report +package report_test import ( - "encoding/json" - "fmt" "net" + "reflect" "testing" - "time" + + "github.com/weaveworks/scope/report" ) -func TestHostJSON(t *testing.T) { - _, localNet, _ := net.ParseCIDR("192.168.1.2/16") - host := HostMetadata{ - Timestamp: time.Now(), - Hostname: "euclid", - LocalNets: []*net.IPNet{localNet}, - OS: "linux", - } - e, err := json.Marshal(host) - if err != nil { - t.Fatalf("Marshal error: %v", err) +func TestReportLocalNetworks(t *testing.T) { + r := report.MakeReport() + r.Merge(report.Report{Host: report.Topology{NodeMetadatas: report.NodeMetadatas{ + "nonets": {}, + "foo": {"local_networks": "10.0.0.1/8 192.168.1.1/24 10.0.0.1/8 badnet/33"}, + }}}) + if want, have := []*net.IPNet{ + mustParseCIDR("10.0.0.1/8"), + mustParseCIDR("192.168.1.1/24"), + }, r.LocalNetworks(); !reflect.DeepEqual(want, have) { + t.Errorf("want %+v, have %+v", want, have) } +} - var hostAgain HostMetadata - err = json.Unmarshal(e, &hostAgain) - if err != nil { - t.Fatalf("Unarshal error: %v", err) +func TestReportSquash(t *testing.T) { + { + want := report.Adjacency{ + report.MakeAdjacencyID(clientHostID, client54001EndpointNodeID): report.MakeIDList(server80EndpointNodeID), + report.MakeAdjacencyID(clientHostID, client54002EndpointNodeID): report.MakeIDList(server80EndpointNodeID), + report.MakeAdjacencyID(serverHostID, server80EndpointNodeID): report.MakeIDList(client54001EndpointNodeID, client54002EndpointNodeID, report.TheInternet), + } + have := reportFixture.Squash().Endpoint.Adjacency + if !reflect.DeepEqual(want, have) { + t.Error(diff(want, have)) + } } - - // need to compare pointers. No fun. - want := fmt.Sprintf("%+v", host) - got := fmt.Sprintf("%+v", hostAgain) - if want != got { - t.Errorf("Host not the same. Want \n%+v, got \n%+v", want, got) + { + want := report.Adjacency{ + report.MakeAdjacencyID(clientHostID, clientAddressNodeID): report.MakeIDList(serverAddressNodeID), + report.MakeAdjacencyID(serverHostID, serverAddressNodeID): report.MakeIDList(clientAddressNodeID, report.TheInternet), + } + have := reportFixture.Squash().Address.Adjacency + if !reflect.DeepEqual(want, have) { + t.Error(diff(want, have)) + } } +} +func mustParseCIDR(s string) *net.IPNet { + _, ipNet, err := net.ParseCIDR(s) + if err != nil { + panic(err) + } + return ipNet } diff --git a/report/squash.go b/report/squash.go deleted file mode 100644 index a25158d45a..0000000000 --- a/report/squash.go +++ /dev/null @@ -1,57 +0,0 @@ -package report - -import ( - "log" - "net" -) - -// Squash takes a Topology, and folds all remote nodes into a supernode. -func Squash(t Topology, f IDAddresser, localNets []*net.IPNet) Topology { - newTopo := NewTopology() - isRemote := func(ip net.IP) bool { return !netsContain(localNets, ip) } - - // If any node ID on the right-hand (destination) side of an adjacency - // list is remote, rename it to TheInternet. (We'll never have remote - // nodes on the left-hand (source) side of an adjacency list, by - // definition.) - for nodeID, adjacent := range t.Adjacency { - var newAdjacency IDList - for _, adjacentID := range adjacent { - if isRemote(f(adjacentID)) { - adjacentID = TheInternet - } - newAdjacency = newAdjacency.Add(adjacentID) - } - newTopo.Adjacency[nodeID] = newAdjacency - } - - // Edge metadata keys are "|". If the dst node - // ID is remote, rename it to TheInternet. - for key, metadata := range t.EdgeMetadatas { - srcNodeID, dstNodeID, ok := ParseEdgeID(key) - if !ok { - log.Printf("bad edge ID %q", key) - continue - } - if ip := f(dstNodeID); ip != nil && isRemote(ip) { - key = MakeEdgeID(srcNodeID, TheInternet) - } - - // Could be we're merging two keys into one now. - summedMetadata := newTopo.EdgeMetadatas[key] - summedMetadata.Flatten(metadata) - newTopo.EdgeMetadatas[key] = summedMetadata - } - - newTopo.NodeMetadatas = t.NodeMetadatas - return newTopo -} - -func netsContain(nets []*net.IPNet, ip net.IP) bool { - for _, net := range nets { - if net.Contains(ip) { - return true - } - } - return false -} diff --git a/report/squash_test.go b/report/squash_test.go deleted file mode 100644 index cd12448fc0..0000000000 --- a/report/squash_test.go +++ /dev/null @@ -1,256 +0,0 @@ -package report - -import ( - "net" - "reflect" - "testing" -) - -var ( - _, netdot1, _ = net.ParseCIDR("192.168.1.0/24") - _, netdot2, _ = net.ParseCIDR("192.168.2.0/24") -) - -func reportToSquash() Report { - return Report{ - Endpoint: Topology{ - Adjacency: Adjacency{ - "hostA|;192.168.1.1;12345": []string{";192.168.1.2;80"}, - "hostA|;192.168.1.1;8888": []string{";1.2.3.4;22", ";1.2.3.4;23"}, - "hostB|;192.168.1.2;80": []string{";192.168.1.1;12345"}, - "hostZ|;192.168.2.2;80": []string{";192.168.1.1;12345"}, - }, - EdgeMetadatas: EdgeMetadatas{ - ";192.168.1.1;12345|;192.168.1.2;80": EdgeMetadata{ - WithBytes: true, - BytesEgress: 12, - BytesIngress: 0, - }, - ";192.168.1.1;8888|;1.2.3.4;22": EdgeMetadata{ - WithBytes: true, - BytesEgress: 200, - BytesIngress: 0, - }, - ";192.168.1.1;8888|;1.2.3.4;23": EdgeMetadata{ - WithBytes: true, - BytesEgress: 200, - BytesIngress: 0, - }, - ";192.168.1.2;80|;192.168.1.1;12345": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - ";192.168.2.2;80|;192.168.1.1;12345": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - }, - NodeMetadatas: NodeMetadatas{ - ";192.168.1.1;12345": NodeMetadata{ - "pid": "23128", - "name": "curl", - "domain": "node-a.local", - }, - ";192.168.1.1;8888": NodeMetadata{ - "pid": "55100", - "name": "ssh", - "domain": "node-a.local", - }, - ";192.168.1.2;80": NodeMetadata{ - "pid": "215", - "name": "apache", - "domain": "node-b.local", - }, - ";192.168.2.2;80": NodeMetadata{ - "pid": "213", - "name": "apache", - "domain": "node-z.local", - }, - }, - }, - - Address: Topology{ - Adjacency: Adjacency{ - "hostA|;192.168.1.1": []string{";192.168.1.2", ";1.2.3.4"}, - "hostB|;192.168.1.2": []string{";192.168.1.1"}, - "hostZ|;192.168.2.2": []string{";192.168.1.1"}, - }, - EdgeMetadatas: EdgeMetadatas{ - ";192.168.1.1|;192.168.1.2": EdgeMetadata{ - WithBytes: true, - BytesEgress: 12, - BytesIngress: 0, - }, - ";192.168.1.1|;1.2.3.4": EdgeMetadata{ - WithBytes: true, - BytesEgress: 200, - BytesIngress: 0, - }, - ";192.168.1.2|;192.168.1.1": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - ";192.168.2.2|;192.168.1.1": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - }, - NodeMetadatas: NodeMetadatas{ - ";192.168.1.1": NodeMetadata{ - "name": "host-a", - }, - ";192.168.1.2": NodeMetadata{ - "name": "host-b", - }, - ";192.168.2.2": NodeMetadata{ - "name": "host-z", - }, - }, - }, - - HostMetadatas: HostMetadatas{ - "hostA": HostMetadata{ - Hostname: "node-a.local", - OS: "Linux", - LocalNets: []*net.IPNet{netdot1}, - }, - "hostB": HostMetadata{ - Hostname: "node-b.local", - OS: "Linux", - LocalNets: []*net.IPNet{netdot1}, - }, - "hostZ": HostMetadata{ - Hostname: "node-z.local", - OS: "Linux", - LocalNets: []*net.IPNet{netdot2}, - }, - }, - } -} - -func TestSquashTopology(t *testing.T) { - // Tests just a topology - want := Topology{ - Adjacency: Adjacency{ - "hostA|;192.168.1.1;12345": []string{";192.168.1.2;80"}, - "hostA|;192.168.1.1;8888": []string{"theinternet"}, - "hostB|;192.168.1.2;80": []string{";192.168.1.1;12345"}, - "hostZ|;192.168.2.2;80": []string{";192.168.1.1;12345"}, - }, - EdgeMetadatas: EdgeMetadatas{ - ";192.168.1.1;12345|;192.168.1.2;80": EdgeMetadata{ - WithBytes: true, - BytesEgress: 12, - BytesIngress: 0, - }, - ";192.168.1.1;8888|theinternet": EdgeMetadata{ - WithBytes: true, - BytesEgress: 2 * 200, - BytesIngress: 2 * 0, - }, - ";192.168.1.2;80|;192.168.1.1;12345": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - ";192.168.2.2;80|;192.168.1.1;12345": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - }, - NodeMetadatas: reportToSquash().Endpoint.NodeMetadatas, - } - - have := Squash(reportToSquash().Endpoint, EndpointIDAddresser, reportToSquash().HostMetadatas.LocalNets()) - if !reflect.DeepEqual(want, have) { - t.Errorf("want\n\t%#v, have\n\t%#v", want, have) - } -} - -func TestSquashReport(t *testing.T) { - // Tests a full report squash. - want := Report{ - Endpoint: Topology{ - Adjacency: Adjacency{ - "hostA|;192.168.1.1;12345": []string{";192.168.1.2;80"}, - "hostA|;192.168.1.1;8888": []string{"theinternet"}, - "hostB|;192.168.1.2;80": []string{";192.168.1.1;12345"}, - "hostZ|;192.168.2.2;80": []string{";192.168.1.1;12345"}, - }, - EdgeMetadatas: EdgeMetadatas{ - ";192.168.1.1;12345|;192.168.1.2;80": EdgeMetadata{ - WithBytes: true, - BytesEgress: 12, - BytesIngress: 0, - }, - ";192.168.1.1;8888|theinternet": EdgeMetadata{ - WithBytes: true, - BytesEgress: 2 * 200, - BytesIngress: 2 * 0, - }, - ";192.168.1.2;80|;192.168.1.1;12345": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - ";192.168.2.2;80|;192.168.1.1;12345": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - }, - NodeMetadatas: reportToSquash().Endpoint.NodeMetadatas, - }, - Address: Topology{ - Adjacency: Adjacency{ - "hostA|;192.168.1.1": []string{";192.168.1.2", "theinternet"}, - "hostB|;192.168.1.2": []string{";192.168.1.1"}, - "hostZ|;192.168.2.2": []string{";192.168.1.1"}, - }, - EdgeMetadatas: EdgeMetadatas{ - ";192.168.1.1|;192.168.1.2": EdgeMetadata{ - WithBytes: true, - BytesEgress: 12, - BytesIngress: 0, - }, - ";192.168.1.1|theinternet": EdgeMetadata{ - WithBytes: true, - BytesEgress: 200, - BytesIngress: 0, - }, - ";192.168.1.2|;192.168.1.1": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - ";192.168.2.2|;192.168.1.1": EdgeMetadata{ - WithBytes: true, - BytesEgress: 0, - BytesIngress: 12, - }, - }, - NodeMetadatas: NodeMetadatas{ - ";192.168.1.1": NodeMetadata{ - "name": "host-a", - }, - ";192.168.1.2": NodeMetadata{ - "name": "host-b", - }, - ";192.168.2.2": NodeMetadata{ - "name": "host-z", - }, - }, - }, - HostMetadatas: reportToSquash().HostMetadatas, - } - - have := reportToSquash().SquashRemote() - if !reflect.DeepEqual(want, have) { - t.Errorf("want\n\t%#v, have\n\t%#v", want, have) - } -} diff --git a/report/topology.go b/report/topology.go index 50affeb0a9..1ad2c7393d 100644 --- a/report/topology.go +++ b/report/topology.go @@ -2,6 +2,7 @@ package report import ( "log" + "net" "reflect" ) @@ -182,6 +183,32 @@ func (t Topology) EdgeMetadata(mapFunc MapFunc, srcRenderableID, dstRenderableID return metadata } +// Squash squashes all non-local nodes in the topology to a super-node called +// the Internet. +func (t Topology) Squash(f IDAddresser, localNets []*net.IPNet) Topology { + isRemote := func(ip net.IP) bool { return !netsContain(localNets, ip) } + for srcID, dstIDs := range t.Adjacency { + newDstIDs := make(IDList, 0, len(dstIDs)) + for _, dstID := range dstIDs { + if ip := f(dstID); ip != nil && isRemote(ip) { + dstID = TheInternet + } + newDstIDs = newDstIDs.Add(dstID) + } + t.Adjacency[srcID] = newDstIDs + } + return t +} + +func netsContain(nets []*net.IPNet, ip net.IP) bool { + for _, net := range nets { + if net.Contains(ip) { + return true + } + } + return false +} + // Diff is returned by TopoDiff. It represents the changes between two // RenderableNode maps. type Diff struct { diff --git a/report/topology_test.go b/report/topology_test.go index eb7ce89671..68c4180668 100644 --- a/report/topology_test.go +++ b/report/topology_test.go @@ -406,5 +406,5 @@ func diff(want, have interface{}) string { ToFile: "have", Context: 3, }) - return text + return "\n" + text } diff --git a/xfer/merge_test.go b/xfer/merge_test.go index 888196583c..b5cbe93e22 100644 --- a/xfer/merge_test.go +++ b/xfer/merge_test.go @@ -39,12 +39,12 @@ func TestMerge(t *testing.T) { { r := report.MakeReport() - r.HostMetadatas["p1"] = report.HostMetadata{Hostname: "test1"} + r.Host.NodeMetadatas["p1"] = report.NodeMetadata{"host_name": "test1"} p1.Publish(r) } { r := report.MakeReport() - r.HostMetadatas["p2"] = report.HostMetadata{Hostname: "test2"} + r.Host.NodeMetadatas["p2"] = report.NodeMetadata{"host_name": "test2"} p2.Publish(r) } @@ -52,10 +52,10 @@ func TestMerge(t *testing.T) { go func() { defer close(success) for r := range c.Reports() { - if r.HostMetadatas["p1"].Hostname != "test1" { + if r.Host.NodeMetadatas["p1"]["host_name"] != "test1" { continue } - if r.HostMetadatas["p2"].Hostname != "test2" { + if r.Host.NodeMetadatas["p2"]["host_name"] != "test2" { continue } return