func TestAPITopologyAddsKubernetes(t *testing.T) { router := mux.NewRouter() c := app.NewCollector(1 * time.Minute) app.RegisterReportPostHandler(c, router) app.RegisterTopologyRoutes(router, c) ts := httptest.NewServer(router) defer ts.Close() body := getRawJSON(t, ts, "/api/topology") var topologies []app.APITopologyDesc decoder := codec.NewDecoderBytes(body, &codec.JsonHandle{}) if err := decoder.Decode(&topologies); err != nil { t.Fatalf("JSON parse error: %s", err) } equals(t, 4, len(topologies)) // Enable the kubernetes topologies rpt := report.MakeReport() rpt.Pod = report.MakeTopology() rpt.Pod.Nodes[fixture.ClientPodNodeID] = kubernetes.NewPod(&api.Pod{ ObjectMeta: api.ObjectMeta{ Name: "pong-a", Namespace: "ping", Labels: map[string]string{"ponger": "true"}, }, Status: api.PodStatus{ HostIP: "1.2.3.4", ContainerStatuses: []api.ContainerStatus{ {ContainerID: "container1"}, {ContainerID: "container2"}, }, }, }).GetNode("") buf := &bytes.Buffer{} encoder := codec.NewEncoder(buf, &codec.MsgpackHandle{}) if err := encoder.Encode(rpt); err != nil { t.Fatalf("GOB encoding error: %s", err) } checkRequest(t, ts, "POST", "/api/report", buf.Bytes()) body = getRawJSON(t, ts, "/api/topology") decoder = codec.NewDecoderBytes(body, &codec.JsonHandle{}) if err := decoder.Decode(&topologies); err != nil { t.Fatalf("JSON parse error: %s", err) } equals(t, 4, len(topologies)) found := false for _, topology := range topologies { if topology.Name == "Pods" { found = true break } } if !found { t.Error("Could not find pods topology") } }
// FIXME: Hideous hack to remove persistent-connection edges to virtual service // IPs attributed to the internet. We add each service IP as a /32 network // (the global service-cluster-ip-range is not exposed by the API // server so we treat each IP as a /32 network see // https://github.com/kubernetes/kubernetes/issues/25533). // The right way of fixing this is performing DNAT mapping on persistent // connections for which we don't have a robust solution // (see https://$GITHUB_URI/issues/1491) func (r *Reporter) hostTopology(services []Service) report.Topology { localNetworks := report.EmptyStringSet for _, service := range services { localNetworks = localNetworks.Add(service.ClusterIP() + "/32") } node := report.MakeNode(report.MakeHostNodeID(r.hostID)) node = node.WithSets(report.EmptySets. Add(host.LocalNetworks, localNetworks)) return report.MakeTopology().AddNode(node) }
func TestNodeTables(t *testing.T) { inputs := []struct { name string rpt report.Report node report.Node want []report.Table }{ { name: "container", rpt: report.Report{ Container: report.MakeTopology(). WithTableTemplates(docker.ContainerTableTemplates), }, node: report.MakeNodeWith(fixture.ClientContainerNodeID, map[string]string{ docker.ContainerID: fixture.ClientContainerID, docker.LabelPrefix + "label1": "label1value", docker.ContainerState: docker.StateRunning, }).WithTopology(report.Container).WithSets(report.EmptySets. Add(docker.ContainerIPs, report.MakeStringSet("10.10.10.0/24", "10.10.10.1/24")), ), want: []report.Table{ { ID: docker.EnvPrefix, Label: "Environment Variables", Rows: []report.MetadataRow{}, }, { ID: docker.LabelPrefix, Label: "Docker Labels", Rows: []report.MetadataRow{ { ID: "label_label1", Label: "label1", Value: "label1value", }, }, }, }, }, { name: "unknown topology", rpt: report.MakeReport(), node: report.MakeNodeWith(fixture.ClientContainerNodeID, map[string]string{ docker.ContainerID: fixture.ClientContainerID, }).WithTopology("foobar"), want: nil, }, } for _, input := range inputs { have := detailed.NodeTables(input.rpt, input.node) if !reflect.DeepEqual(input.want, have) { t.Errorf("%s: %s", input.name, test.Diff(input.want, have)) } } }
func (r *Reporter) containerTopology(localAddrs []net.IP) report.Topology { result := report.MakeTopology(). WithMetadataTemplates(ContainerMetadataTemplates). WithMetricTemplates(ContainerMetricTemplates). WithTableTemplates(ContainerTableTemplates) result.Controls.AddControls(ContainerControls) metadata := map[string]string{report.ControlProbeID: r.probeID} nodes := []report.Node{} r.registry.WalkContainers(func(c Container) { nodes = append(nodes, c.GetNode().WithLatests(metadata)) }) // Copy the IP addresses from other containers where they share network // namespaces & deal with containers in the host net namespace. This // is recursive to deal with people who decide to be clever. { hostNetworkInfo := report.EmptySets if hostIPs, err := getLocalIPs(); err == nil { hostIPsWithScopes := addScopeToIPs(r.hostID, hostIPs) hostNetworkInfo = hostNetworkInfo. Add(ContainerIPs, report.MakeStringSet(hostIPs...)). Add(ContainerIPsWithScopes, report.MakeStringSet(hostIPsWithScopes...)) } var networkInfo func(prefix string) report.Sets networkInfo = func(prefix string) report.Sets { container, ok := r.registry.GetContainerByPrefix(prefix) if !ok { return report.EmptySets } networkMode, ok := container.NetworkMode() if ok && strings.HasPrefix(networkMode, "container:") { return networkInfo(networkMode[10:]) } else if ok && networkMode == NetworkModeHost { return hostNetworkInfo } return container.NetworkInfo(localAddrs) } for _, node := range nodes { id, ok := report.ParseContainerNodeID(node.ID) if !ok { continue } networkInfo := networkInfo(id) result.AddNode(node.WithSets(networkInfo)) } } return result }
func (r *Reporter) podTopology(services []Service, replicaSets []ReplicaSet) (report.Topology, error) { var ( pods = report.MakeTopology(). WithMetadataTemplates(PodMetadataTemplates). WithTableTemplates(TableTemplates) selectors = []func(labelledChild){} ) pods.Controls.AddControl(report.Control{ ID: GetLogs, Human: "Get logs", Icon: "fa-desktop", Rank: 0, }) pods.Controls.AddControl(report.Control{ ID: DeletePod, Human: "Delete", Icon: "fa-trash-o", Rank: 1, }) for _, service := range services { selectors = append(selectors, match( service.Selector(), report.Service, report.MakeServiceNodeID(service.UID()), )) } for _, replicaSet := range replicaSets { selectors = append(selectors, match( replicaSet.Selector(), report.ReplicaSet, report.MakeReplicaSetNodeID(replicaSet.UID()), )) } thisNodeName, err := GetNodeName(r) if err != nil { return pods, err } err = r.client.WalkPods(func(p Pod) error { if p.NodeName() != thisNodeName { return nil } for _, selector := range selectors { selector(p) } pods = pods.AddNode(p.GetNode(r.probeID)) return nil }) return pods, err }
func (r *Reporter) serviceTopology() (report.Topology, []Service, error) { var ( result = report.MakeTopology(). WithMetadataTemplates(ServiceMetadataTemplates). WithTableTemplates(TableTemplates) services = []Service{} ) err := r.client.WalkServices(func(s Service) error { result = result.AddNode(s.GetNode()) services = append(services, s) return nil }) return result, services, err }
func (r *Reporter) deploymentTopology(probeID string) (report.Topology, []Deployment, error) { var ( result = report.MakeTopology(). WithMetadataTemplates(DeploymentMetadataTemplates). WithTableTemplates(TableTemplates) deployments = []Deployment{} ) result.Controls.AddControls(ScalingControls) err := r.client.WalkDeployments(func(d Deployment) error { result = result.AddNode(d.GetNode(probeID)) deployments = append(deployments, d) return nil }) return result, deployments, err }
// Make sure we don't add a topology and miss it in the Topologies method. func TestReportTopologies(t *testing.T) { var ( reportType = reflect.TypeOf(report.MakeReport()) topologyType = reflect.TypeOf(report.MakeTopology()) ) var want int for i := 0; i < reportType.NumField(); i++ { if reportType.Field(i).Type == topologyType { want++ } } if have := len(report.MakeReport().Topologies()); want != have { t.Errorf("want %d, have %d", want, 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 (r *Reporter) replicaSetTopology(probeID string, deployments []Deployment) (report.Topology, []ReplicaSet, error) { var ( result = report.MakeTopology(). WithMetadataTemplates(ReplicaSetMetadataTemplates). WithTableTemplates(TableTemplates) replicaSets = []ReplicaSet{} selectors = []func(labelledChild){} ) result.Controls.AddControls(ScalingControls) for _, deployment := range deployments { selectors = append(selectors, match( deployment.Selector(), report.Deployment, report.MakeDeploymentNodeID(deployment.UID()), )) } err := r.client.WalkReplicaSets(func(r ReplicaSet) error { for _, selector := range selectors { selector(r) } result = result.AddNode(r.GetNode(probeID)) replicaSets = append(replicaSets, r) return nil }) if err != nil { return result, replicaSets, err } err = r.client.WalkReplicationControllers(func(r ReplicationController) error { for _, selector := range selectors { selector(r) } result = result.AddNode(r.GetNode(probeID)) replicaSets = append(replicaSets, ReplicaSet(r)) return nil }) return result, replicaSets, err }
func (r *Reporter) containerImageTopology() report.Topology { result := report.MakeTopology(). WithMetadataTemplates(ContainerImageMetadataTemplates). WithTableTemplates(ContainerImageTableTemplates) r.registry.WalkImages(func(image *docker_client.APIImages) { imageID := trimImageID(image.ID) nodeID := report.MakeContainerImageNodeID(imageID) node := report.MakeNodeWith(nodeID, map[string]string{ ImageID: imageID, }) node = node.AddTable(ImageLabelPrefix, image.Labels) if len(image.RepoTags) > 0 { node = node.WithLatests(map[string]string{ImageName: image.RepoTags[0]}) } result.AddNode(node) }) return result }
func TestAppClientPublish(t *testing.T) { var ( token = "abcdefg" id = "1234567" rpt = report.MakeReport() done = make(chan struct{}, 10) ) // marshalling->unmarshaling is not idempotent due to `json:"omitempty"` // tags, transforming empty slices into nils. So, we make DeepEqual // happy by setting empty `json:"omitempty"` entries to nil rpt.Endpoint = report.MakeTopology() rpt.Process = report.MakeTopology() rpt.Container = report.MakeTopology() rpt.ContainerImage = report.MakeTopology() rpt.Pod = report.MakeTopology() rpt.Service = report.MakeTopology() rpt.Deployment = report.MakeTopology() rpt.ReplicaSet = report.MakeTopology() rpt.Host = report.MakeTopology() rpt.Overlay = report.MakeTopology() rpt.Endpoint.Controls = nil rpt.Process.Controls = nil rpt.Container.Controls = nil rpt.ContainerImage.Controls = nil rpt.Pod.Controls = nil rpt.Service.Controls = nil rpt.Deployment.Controls = nil rpt.ReplicaSet.Controls = nil rpt.Host.Controls = nil rpt.Overlay.Controls = nil s := dummyServer(t, token, id, rpt, done) defer s.Close() u, err := url.Parse(s.URL) if err != nil { t.Fatal(err) } pc := ProbeConfig{ Token: token, ProbeID: id, Insecure: false, } p, err := NewAppClient(pc, u.Host, s.URL, nil) if err != nil { t.Fatal(err) } defer p.Stop() // First few reports might be dropped as the client is spinning up. rp := NewReportPublisher(p) for i := 0; i < 10; i++ { if err := rp.Publish(rpt); err != nil { t.Error(err) } time.Sleep(10 * time.Millisecond) } select { case <-done: case <-time.After(100 * time.Millisecond): t.Error("timeout") } }
func TestWeaveTaggerOverlayTopology(t *testing.T) { w := overlay.NewWeave(mockHostID, weave.MockClient{}) defer w.Stop() // Wait until the reporter reports some nodes test.Poll(t, 300*time.Millisecond, 1, func() interface{} { have, _ := w.Report() return len(have.Overlay.Nodes) }) { // Overlay node should include peer name and nickname have, err := w.Report() if err != nil { t.Fatal(err) } nodeID := report.MakeOverlayNodeID(weave.MockWeavePeerName) node, ok := have.Overlay.Nodes[nodeID] if !ok { t.Errorf("Expected overlay node %q, but not found", nodeID) } if peerName, ok := node.Latest.Lookup(overlay.WeavePeerName); !ok || peerName != weave.MockWeavePeerName { t.Errorf("Expected weave peer name %q, got %q", weave.MockWeavePeerName, peerName) } if peerNick, ok := node.Latest.Lookup(overlay.WeavePeerNickName); !ok || peerNick != weave.MockWeavePeerNickName { t.Errorf("Expected weave peer nickname %q, got %q", weave.MockWeavePeerNickName, peerNick) } if localNetworks, ok := node.Sets.Lookup(host.LocalNetworks); !ok || !reflect.DeepEqual(localNetworks, report.MakeStringSet(weave.MockWeaveDefaultSubnet)) { t.Errorf("Expected weave node local_networks %q, got %q", report.MakeStringSet(weave.MockWeaveDefaultSubnet), localNetworks) } } { // Container nodes should be tagged with their overlay info nodeID := report.MakeContainerNodeID(weave.MockContainerID) have, err := w.Tag(report.Report{ Container: report.MakeTopology().AddNode(report.MakeNodeWith(nodeID, map[string]string{ docker.ContainerID: weave.MockContainerID, })), }) if err != nil { t.Fatal(err) } node, ok := have.Container.Nodes[nodeID] if !ok { t.Errorf("Expected container node %q, but not found", nodeID) } // Should have Weave DNS Hostname if have, ok := node.Latest.Lookup(overlay.WeaveDNSHostname); !ok || have != weave.MockHostname { t.Errorf("Expected weave dns hostname %q, got %q", weave.MockHostname, have) } // Should have Weave MAC Address if have, ok := node.Latest.Lookup(overlay.WeaveMACAddress); !ok || have != weave.MockContainerMAC { t.Errorf("Expected weave mac address %q, got %q", weave.MockContainerMAC, have) } // Should have Weave container ip if have, ok := node.Sets.Lookup(docker.ContainerIPs); !ok || !have.Contains(weave.MockContainerIP) { t.Errorf("Expected container ips to include the weave IP %q, got %q", weave.MockContainerIP, have) } // Should have Weave container ip (with scope) if have, ok := node.Sets.Lookup(docker.ContainerIPsWithScopes); !ok || !have.Contains(mockContainerIPWithScope) { t.Errorf("Expected container ips to include the weave IP (with scope) %q, got %q", mockContainerIPWithScope, have) } } }