Пример #1
0
// Set the list of endpoints for the given hostname.
func (c *multiClient) Set(hostname string, endpoints []string) {
	wg := sync.WaitGroup{}
	wg.Add(len(endpoints))
	clients := make(chan clientTuple, len(endpoints))
	for _, endpoint := range endpoints {
		go func(endpoint string) {
			c.sema.acquire()
			defer c.sema.release()
			defer wg.Done()

			client, err := c.clientFactory(hostname, endpoint)
			if err != nil {
				log.Errorf("Error creating new app client: %v", err)
				return
			}

			details, err := client.Details()
			if err != nil {
				log.Errorf("Error fetching app details: %v", err)
				return
			}

			clients <- clientTuple{details, client}
		}(endpoint)
	}

	wg.Wait()
	close(clients)
	c.mtx.Lock()
	defer c.mtx.Unlock()

	// Start any new apps, and replace the list of app ids for this hostname
	hostIDs := report.MakeIDList()
	for tuple := range clients {
		hostIDs = hostIDs.Add(tuple.ID)

		_, ok := c.clients[tuple.ID]
		if !ok {
			c.clients[tuple.ID] = tuple.AppClient
			tuple.AppClient.ControlConnection()
		}
	}
	c.ids[hostname] = hostIDs

	// Remove apps that are no longer referenced (by id) from any hostname
	allReferencedIDs := report.MakeIDList()
	for _, ids := range c.ids {
		allReferencedIDs = allReferencedIDs.Add(ids...)
	}
	for id, client := range c.clients {
		if !allReferencedIDs.Contains(id) {
			client.Stop()
			delete(c.clients, id)
		}
	}
}
Пример #2
0
func TestFilterRender(t *testing.T) {
	renderer := mockRenderer{Nodes: report.Nodes{
		"foo": report.MakeNode("foo").WithAdjacent("bar"),
		"bar": report.MakeNode("bar").WithAdjacent("foo"),
		"baz": report.MakeNode("baz"),
	}}
	have := report.MakeIDList()
	for id := range renderer.Render(report.MakeReport(), render.FilterUnconnected) {
		have = have.Add(id)
	}
	want := report.MakeIDList("foo", "bar")
	if !reflect.DeepEqual(want, have) {
		t.Error(test.Diff(want, have))
	}
}
Пример #3
0
func assertAdjacent(t *testing.T, n report.Node, ids ...string) {
	want := report.MakeIDList(ids...)

	if have := n.Adjacency; !reflect.DeepEqual(want, have) {
		t.Fatalf("want adjacency list %v, have %v", want, have)
	}
}
Пример #4
0
func endpointChildIDsOf(n report.Node) report.IDList {
	result := report.MakeIDList()
	n.Children.ForEach(func(child report.Node) {
		if child.Topology == report.Endpoint {
			result = result.Add(child.ID)
		}
	})
	return result
}
Пример #5
0
func TestIDList(t *testing.T) {
	have := report.MakeIDList("alpha", "mu", "zeta")
	have = have.Add("alpha")
	have = have.Add("nu")
	have = have.Add("mu")
	have = have.Add("alpha")
	have = have.Add("alpha")
	have = have.Add("epsilon")
	have = have.Add("delta")
	if want := report.IDList([]string{"alpha", "delta", "epsilon", "mu", "nu", "zeta"}); !reflect.DeepEqual(want, have) {
		t.Errorf("want %+v, have %+v", want, have)
	}
}
Пример #6
0
func (f *Filter) render(rpt report.Report, dct Decorator) (report.Nodes, int) {
	output := report.Nodes{}
	inDegrees := map[string]int{}
	filtered := 0
	for id, node := range f.Renderer.Render(rpt, dct) {
		if node.Topology == Pseudo || f.FilterFunc(node) {
			output[id] = node
			inDegrees[id] = 0
		} else {
			filtered++
		}
	}

	// Deleted nodes also need to be cut as destinations in adjacency lists.
	for id, node := range output {
		newAdjacency := report.MakeIDList()
		for _, dstID := range node.Adjacency {
			if _, ok := output[dstID]; ok {
				newAdjacency = newAdjacency.Add(dstID)
				inDegrees[dstID]++
			}
		}
		node.Adjacency = newAdjacency
		output[id] = node
	}

	// Remove unconnected pseudo nodes, see #483.
	for id, inDegree := range inDegrees {
		if inDegree > 0 {
			continue
		}
		node := output[id]
		if node.Topology != Pseudo || len(node.Adjacency) > 0 {
			continue
		}
		delete(output, id)
		filtered++
	}
	return output, filtered
}
Пример #7
0
func TestMakeDetailedHostNode(t *testing.T) {
	renderableNodes := render.HostRenderer.Render(fixture.Report, render.FilterNoop)
	renderableNode := renderableNodes[fixture.ClientHostNodeID]
	have := detailed.MakeNode("hosts", fixture.Report, renderableNodes, renderableNode)

	containerImageNodeSummary := child(t, render.ContainerImageRenderer, fixture.ClientContainerImageNodeID)
	containerNodeSummary := child(t, render.ContainerRenderer, fixture.ClientContainerNodeID)
	process1NodeSummary := child(t, render.ProcessRenderer, fixture.ClientProcess1NodeID)
	process1NodeSummary.Linkable = true
	process2NodeSummary := child(t, render.ProcessRenderer, fixture.ClientProcess2NodeID)
	process2NodeSummary.Linkable = true
	podNodeSummary := child(t, render.PodRenderer, fixture.ClientPodNodeID)
	want := detailed.Node{
		NodeSummary: detailed.NodeSummary{
			ID:         fixture.ClientHostNodeID,
			Label:      "client",
			LabelMinor: "hostname.com",
			Rank:       "hostname.com",
			Pseudo:     false,
			Shape:      "circle",
			Linkable:   true,
			Adjacency:  report.MakeIDList(fixture.ServerHostNodeID),
			Metadata: []report.MetadataRow{
				{
					ID:       "host_name",
					Label:    "Hostname",
					Value:    "client.hostname.com",
					Priority: 11,
				},
				{
					ID:       "os",
					Label:    "OS",
					Value:    "Linux",
					Priority: 12,
				},
				{
					ID:       "local_networks",
					Label:    "Local Networks",
					Value:    "10.10.10.0/24",
					Priority: 13,
				},
			},
			Metrics: []report.MetricRow{
				{
					ID:       host.CPUUsage,
					Label:    "CPU",
					Format:   "percent",
					Value:    0.07,
					Priority: 1,
					Metric:   &fixture.ClientHostCPUMetric,
				},
				{
					ID:       host.MemoryUsage,
					Label:    "Memory",
					Format:   "filesize",
					Value:    0.08,
					Priority: 2,
					Metric:   &fixture.ClientHostMemoryMetric,
				},
				{
					ID:       host.Load1,
					Label:    "Load (1m)",
					Group:    "load",
					Value:    0.09,
					Priority: 11,
					Metric:   &fixture.ClientHostLoad1Metric,
				},
			},
		},
		Controls: []detailed.ControlInstance{},
		Children: []detailed.NodeSummaryGroup{
			{
				Label:      "Pods",
				TopologyID: "pods",
				Columns: []detailed.Column{
					{ID: report.Container, Label: "# Containers"},
					{ID: kubernetes.IP, Label: "IP"},
				},
				Nodes: []detailed.NodeSummary{podNodeSummary},
			},
			{
				Label:      "Containers",
				TopologyID: "containers",
				Columns: []detailed.Column{
					{ID: docker.CPUTotalUsage, Label: "CPU"},
					{ID: docker.MemoryUsage, Label: "Memory"},
				},
				Nodes: []detailed.NodeSummary{containerNodeSummary},
			},
			{
				Label:      "Processes",
				TopologyID: "processes",
				Columns: []detailed.Column{
					{ID: process.PID, Label: "PID"},
					{ID: process.CPUUsage, Label: "CPU"},
					{ID: process.MemoryUsage, Label: "Memory"},
				},
				Nodes: []detailed.NodeSummary{process1NodeSummary, process2NodeSummary},
			},
			{
				Label:      "Container Images",
				TopologyID: "containers-by-image",
				Columns: []detailed.Column{
					{ID: report.Container, Label: "# Containers", DefaultSort: true},
				},
				Nodes: []detailed.NodeSummary{containerImageNodeSummary},
			},
		},
		Connections: []detailed.ConnectionsSummary{
			{
				ID:          "incoming-connections",
				TopologyID:  "hosts",
				Label:       "Inbound",
				Columns:     detailed.NormalColumns,
				Connections: []detailed.Connection{},
			},
			{
				ID:         "outgoing-connections",
				TopologyID: "hosts",
				Label:      "Outbound",
				Columns:    detailed.NormalColumns,
				Connections: []detailed.Connection{
					{
						ID:       fmt.Sprintf("%s:%s-%s:%s-%d", fixture.ServerHostNodeID, "", fixture.ClientHostNodeID, "", 80),
						NodeID:   fixture.ServerHostNodeID,
						Label:    "server",
						Linkable: true,
						Metadata: []report.MetadataRow{
							{
								ID:       "port",
								Value:    "80",
								Datatype: "number",
							},
							{
								ID:       "count",
								Value:    "2",
								Datatype: "number",
							},
						},
					},
				},
			},
		},
	}
	if !reflect.DeepEqual(want, have) {
		t.Errorf("%s", test.Diff(want, have))
	}
}
Пример #8
0
func TestTopoDiff(t *testing.T) {
	nodea := detailed.NodeSummary{
		ID:         "nodea",
		Label:      "Node A",
		LabelMinor: "'ts an a",
		Pseudo:     false,
		Adjacency:  report.MakeIDList("nodeb"),
	}
	nodeap := nodea.Copy()
	nodeap.Adjacency = report.MakeIDList("nodeb", "nodeq") // not the same anymore
	nodeb := detailed.NodeSummary{
		ID:    "nodeb",
		Label: "Node B",
	}

	// Helper to make RenderableNode maps.
	nodes := func(ns ...detailed.NodeSummary) detailed.NodeSummaries {
		r := detailed.NodeSummaries{}
		for _, n := range ns {
			r[n.ID] = n
		}
		return r
	}

	for _, c := range []struct {
		label      string
		have, want detailed.Diff
	}{
		{
			label: "basecase: empty -> something",
			have:  detailed.TopoDiff(nodes(), nodes(nodea, nodeb)),
			want: detailed.Diff{
				Add: []detailed.NodeSummary{nodea, nodeb},
			},
		},
		{
			label: "basecase: something -> empty",
			have:  detailed.TopoDiff(nodes(nodea, nodeb), nodes()),
			want: detailed.Diff{
				Remove: []string{"nodea", "nodeb"},
			},
		},
		{
			label: "add and remove",
			have:  detailed.TopoDiff(nodes(nodea), nodes(nodeb)),
			want: detailed.Diff{
				Add:    []detailed.NodeSummary{nodeb},
				Remove: []string{"nodea"},
			},
		},
		{
			label: "no change",
			have:  detailed.TopoDiff(nodes(nodea), nodes(nodea)),
			want:  detailed.Diff{},
		},
		{
			label: "change a single node",
			have:  detailed.TopoDiff(nodes(nodea), nodes(nodeap)),
			want: detailed.Diff{
				Update: []detailed.NodeSummary{nodeap},
			},
		},
	} {
		sort.Strings(c.have.Remove)
		sort.Sort(ByID(c.have.Add))
		sort.Sort(ByID(c.have.Update))
		if !reflect.DeepEqual(c.want, c.have) {
			t.Errorf("%s - %s", c.label, test.Diff(c.want, c.have))
		}
	}
}
Пример #9
0
func TestMakeNodeSummary(t *testing.T) {
	testcases := []struct {
		name  string
		input report.Node
		ok    bool
		want  detailed.NodeSummary
	}{
		{
			name:  "single process rendering",
			input: expected.RenderedProcesses[fixture.ClientProcess1NodeID],
			ok:    true,
			want: detailed.NodeSummary{
				ID:         fixture.ClientProcess1NodeID,
				Label:      fixture.Client1Name,
				LabelMinor: "client.hostname.com (10001)",
				Rank:       fixture.Client1Name,
				Shape:      "square",
				Metadata: []report.MetadataRow{
					{ID: process.PID, Label: "PID", Value: fixture.Client1PID, Priority: 1, Datatype: "number"},
				},
				Adjacency: report.MakeIDList(fixture.ServerProcessNodeID),
			},
		},
		{
			name:  "single container rendering",
			input: expected.RenderedContainers[fixture.ClientContainerNodeID],
			ok:    true,
			want: detailed.NodeSummary{
				ID:         fixture.ClientContainerNodeID,
				Label:      fixture.ClientContainerName,
				LabelMinor: fixture.ClientHostName,
				Rank:       fixture.ClientContainerImageName,
				Shape:      "hexagon",
				Linkable:   true,
				Metadata: []report.MetadataRow{
					{ID: docker.ContainerID, Label: "ID", Value: fixture.ClientContainerID, Priority: 1},
				},
				Adjacency: report.MakeIDList(fixture.ServerContainerNodeID),
			},
		},
		{
			name:  "single container image rendering",
			input: expected.RenderedContainerImages[fixture.ClientContainerImageNodeID],
			ok:    true,
			want: detailed.NodeSummary{
				ID:         fixture.ClientContainerImageNodeID,
				Label:      fixture.ClientContainerImageName,
				LabelMinor: "1 container",
				Rank:       fixture.ClientContainerImageName,
				Shape:      "hexagon",
				Linkable:   true,
				Stack:      true,
				Metadata: []report.MetadataRow{
					{ID: docker.ImageID, Label: "Image ID", Value: fixture.ClientContainerImageID, Priority: 1},
					{ID: report.Container, Label: "# Containers", Value: "1", Priority: 2, Datatype: "number"},
				},
				Adjacency: report.MakeIDList(fixture.ServerContainerImageNodeID),
			},
		},
		{
			name:  "single host rendering",
			input: expected.RenderedHosts[fixture.ClientHostNodeID],
			ok:    true,
			want: detailed.NodeSummary{
				ID:         fixture.ClientHostNodeID,
				Label:      "client",
				LabelMinor: "hostname.com",
				Rank:       "hostname.com",
				Shape:      "circle",
				Linkable:   true,
				Metadata: []report.MetadataRow{
					{ID: host.HostName, Label: "Hostname", Value: fixture.ClientHostName, Priority: 11},
				},
				Adjacency: report.MakeIDList(fixture.ServerHostNodeID),
			},
		},
		{
			name:  "group node rendering",
			input: expected.RenderedProcessNames[fixture.ServerName],
			ok:    true,
			want: detailed.NodeSummary{
				ID:         "apache",
				Label:      "apache",
				LabelMinor: "1 process",
				Rank:       "apache",
				Shape:      "square",
				Stack:      true,
				Linkable:   true,
			},
		},
	}
	for _, testcase := range testcases {
		have, ok := detailed.MakeNodeSummary(fixture.Report, testcase.input)
		if ok != testcase.ok {
			t.Errorf("%s: MakeNodeSummary failed: expected ok value to be: %v", testcase.name, testcase.ok)
			continue
		}

		if !reflect.DeepEqual(testcase.want, have) {
			t.Errorf("%s: Node Summary did not match: %s", testcase.name, test.Diff(testcase.want, have))
		}
	}
}