// MapIP2Container maps IP nodes produced from MapContainer2IP back to // container nodes. If there is more than one container with a given // IP, it is dropped. func MapIP2Container(n report.Node, _ report.Networks) report.Nodes { // If an IP is shared between multiple containers, we can't // reliably attribute an connection based on its IP if count, _ := n.Counters.Lookup(IP); count > 1 { return report.Nodes{} } // Propagate the internet pseudo node if strings.HasSuffix(n.ID, TheInternetID) { return report.Nodes{n.ID: n} } // If this node is not a container, exclude it. // This excludes all the nodes we've dragged in from endpoint // that we failed to join to a container. containerID, ok := n.Latest.Lookup(docker.ContainerID) if !ok { return report.Nodes{} } id := report.MakeContainerNodeID(containerID) return report.Nodes{ id: NewDerivedNode(id, n). WithTopology(report.Container), } }
// MapProcess2Container maps process Nodes to container // Nodes. // // If this function is given a node without a docker_container_id // (including other pseudo nodes), it will produce an "Uncontained" // pseudo node. // // Otherwise, this function will produce a node with the correct ID // format for a container, 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 container graph to get that info. func MapProcess2Container(n report.Node, _ report.Networks) report.Nodes { // Propagate pseudo nodes if n.Topology == Pseudo { return report.Nodes{n.ID: n} } // Otherwise, if the process is not in a container, group it // into an per-host "Uncontained" node. If for whatever reason // this node doesn't have a host id in their nodemetadata, it'll // all get grouped into a single uncontained node. var ( id string node report.Node ) if containerID, ok := n.Latest.Lookup(docker.ContainerID); ok { id = report.MakeContainerNodeID(containerID) node = NewDerivedNode(id, n).WithTopology(report.Container) } else { id = MakePseudoNodeID(UncontainedID, report.ExtractHostID(n)) node = NewDerivedPseudoNode(id, n) node = propagateLatest(report.HostNodeID, n, node) node = propagateLatest(IsConnected, n, node) } return report.Nodes{id: node} }
func TestContainerHostnameFilterRenderer(t *testing.T) { // add a system container into the topology and ensure // it is filtered out correctly. input := fixture.Report.Copy() clientContainer2ID := "f6g7h8i9j1" clientContainer2NodeID := report.MakeContainerNodeID(clientContainer2ID) input.Container.AddNode(report.MakeNodeWith(clientContainer2NodeID, map[string]string{ docker.LabelPrefix + "works.weave.role": "system", docker.ContainerHostname: fixture.ClientContainerHostname, report.HostNodeID: fixture.ClientHostNodeID, }). WithParents(report.EmptySets. Add("host", report.MakeStringSet(fixture.ClientHostNodeID)), ).WithTopology(report.Container)) have := Prune(render.ContainerHostnameRenderer.Render(input, render.FilterApplication)) want := Prune(expected.RenderedContainerHostnames) // Test works by virtue of the RenderedContainerHostname only having a container // counter == 1 if !reflect.DeepEqual(want, have) { t.Error(test.Diff(want, have)) } }
func TestPipes(t *testing.T) { oldNewPipe := controls.NewPipe defer func() { controls.NewPipe = oldNewPipe }() controls.NewPipe = func(_ controls.PipeClient, _ string) (string, xfer.Pipe, error) { return "pipeid", mockPipe{}, nil } mdc := newMockClient() setupStubs(mdc, func() { registry, _ := docker.NewRegistry(10*time.Second, nil, false, "") defer registry.Stop() test.Poll(t, 100*time.Millisecond, true, func() interface{} { _, ok := registry.GetContainer("ping") return ok }) for _, tc := range []string{ docker.AttachContainer, docker.ExecContainer, } { result := controls.HandleControlRequest(xfer.Request{ Control: tc, NodeID: report.MakeContainerNodeID("ping"), }) want := xfer.Response{ Pipe: "pipeid", RawTTY: true, } if !reflect.DeepEqual(result, want) { t.Errorf("diff %s: %s", tc, test.Diff(want, result)) } } }) }
func TestControls(t *testing.T) { mdc := newMockClient() setupStubs(mdc, func() { registry, _ := docker.NewRegistry(10*time.Second, nil, false, "") defer registry.Stop() for _, tc := range []struct{ command, result string }{ {docker.StopContainer, "stopped"}, {docker.StartContainer, "started"}, {docker.RestartContainer, "restarted"}, {docker.PauseContainer, "paused"}, {docker.UnpauseContainer, "unpaused"}, } { result := controls.HandleControlRequest(xfer.Request{ Control: tc.command, NodeID: report.MakeContainerNodeID("a1b2c3d4e5"), }) if !reflect.DeepEqual(result, xfer.Response{ Error: tc.result, }) { t.Error(result) } } }) }
func (c *container) getBaseNode() report.Node { result := report.MakeNodeWith(report.MakeContainerNodeID(c.ID()), map[string]string{ ContainerID: c.ID(), ContainerCreated: c.container.Created.Format(time.RFC822), ContainerCommand: c.container.Path + " " + strings.Join(c.container.Args, " "), ImageID: c.Image(), ContainerHostname: c.Hostname(), }).WithParents(report.EmptySets. Add(report.ContainerImage, report.MakeStringSet(report.MakeContainerImageNodeID(c.Image()))), ) result = result.AddTable(LabelPrefix, c.container.Config.Labels) result = result.AddTable(EnvPrefix, c.env()) return result }
// Tag implements Tagger. func (w *Weave) Tag(r report.Report) (report.Report, error) { w.mtx.RLock() defer w.mtx.RUnlock() // Put information from weaveDNS on the container nodes for _, entry := range w.statusCache.DNS.Entries { if entry.Tombstone > 0 { continue } nodeID := report.MakeContainerNodeID(entry.ContainerID) node, ok := r.Container.Nodes[nodeID] if !ok { continue } w, _ := node.Latest.Lookup(WeaveDNSHostname) hostnames := report.IDList(strings.Fields(w)) hostnames = hostnames.Add(strings.TrimSuffix(entry.Hostname, ".")) r.Container.Nodes[nodeID] = node.WithLatests(map[string]string{WeaveDNSHostname: strings.Join(hostnames, " ")}) } // Put information from weave ps on the container nodes const maxPrefixSize = 12 for id, node := range r.Container.Nodes { prefix, ok := node.Latest.Lookup(docker.ContainerID) if !ok { continue } if len(prefix) > maxPrefixSize { prefix = prefix[:maxPrefixSize] } entry, ok := w.psCache[prefix] if !ok { continue } ipsWithScope := report.MakeStringSet() for _, ip := range entry.IPs { ipsWithScope = ipsWithScope.Add(report.MakeAddressNodeID("", ip)) } node = node.WithSet(docker.ContainerIPs, report.MakeStringSet(entry.IPs...)) node = node.WithSet(docker.ContainerIPsWithScopes, ipsWithScope) node = node.WithLatests(map[string]string{ WeaveMACAddress: entry.MACAddress, }) r.Container.Nodes[id] = node } return r, nil }
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(report.MakeNodeWith(pid1NodeID, map[string]string{process.PID: "2"})) input.Process.AddNode(report.MakeNodeWith(pid2NodeID, 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 (t *Tagger) tag(tree process.Tree, topology *report.Topology) { for nodeID, node := range topology.Nodes { pidStr, ok := node.Latest.Lookup(process.PID) if !ok { continue } pid, err := strconv.ParseUint(pidStr, 10, 64) if err != nil { continue } var ( c Container candidate = int(pid) ) t.registry.LockedPIDLookup(func(lookup func(int) Container) { for { c = lookup(candidate) if c != nil { break } candidate, err = tree.GetParent(candidate) if err != nil { break } } }) if c == nil || ContainerIsStopped(c) || c.PID() == 1 { continue } topology.AddNode(report.MakeNodeWith(nodeID, map[string]string{ ContainerID: c.ID(), }).WithParents(report.EmptySets. Add(report.Container, report.MakeStringSet(report.MakeContainerNodeID(c.ID()))). Add(report.ContainerImage, report.MakeStringSet(report.MakeContainerImageNodeID(c.Image()))), )) } }
func (r processWithContainerNameRenderer) Render(rpt report.Report, dct Decorator) report.Nodes { processes := r.Renderer.Render(rpt, dct) containers := SelectContainer.Render(rpt, dct) outputs := report.Nodes{} for id, p := range processes { outputs[id] = p containerID, timestamp, ok := p.Latest.LookupEntry(docker.ContainerID) if !ok { continue } container, ok := containers[report.MakeContainerNodeID(containerID)] if !ok { continue } output := p.Copy() output.Latest = output.Latest.Set(docker.ContainerID, timestamp, containerID) if containerName, timestamp, ok := container.Latest.LookupEntry(docker.ContainerName); ok { output.Latest = output.Latest.Set(docker.ContainerName, timestamp, containerName) } outputs[id] = output } return outputs }
func (r *registry) updateContainerState(containerID string, intendedState *string) { r.Lock() defer r.Unlock() dockerContainer, err := r.client.InspectContainer(containerID) if err != nil { // Don't spam the logs if the container was short lived if _, ok := err.(*docker_client.NoSuchContainer); !ok { log.Errorf("Error processing event for container %s: %v", containerID, err) return } // Container doesn't exist anymore, so lets stop and remove it c, ok := r.containers.Get(containerID) if !ok { return } container := c.(Container) r.containers.Delete(containerID) delete(r.containersByPID, container.PID()) if r.collectStats { container.StopGatheringStats() } if intendedState != nil { node := report.MakeNodeWith(report.MakeContainerNodeID(containerID), map[string]string{ ContainerID: containerID, ContainerState: *intendedState, }) // Trigger anyone watching for updates for _, f := range r.watchers { f(node) } } return } // Container exists, ensure we have it o, ok := r.containers.Get(containerID) var c Container if !ok { c = NewContainerStub(dockerContainer, r.hostID) r.containers.Insert(containerID, c) } else { c = o.(Container) // potentially remove existing pid mapping. delete(r.containersByPID, c.PID()) c.UpdateState(dockerContainer) } // Update PID index if c.PID() > 1 { r.containersByPID[c.PID()] = c } // Trigger anyone watching for updates if err != nil { node := c.GetNode() for _, f := range r.watchers { f(node) } } // And finally, ensure we gather stats for it if r.collectStats { if dockerContainer.State.Running { if err := c.StartGatheringStats(); err != nil { log.Errorf("Error gather stats for container: %s", containerID) return } } else { c.StopGatheringStats() } } }
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" ClientContainerName = "client" ServerContainerID = "5e4d3c2b1a" ServerContainerName = "task-name-5-server-aceb93e2f2b797caba01" ClientContainerNodeID = report.MakeContainerNodeID(ClientContainerID) ServerContainerNodeID = report.MakeContainerNodeID(ServerContainerID) ClientContainerHostname = ClientContainerName + ".hostname.com" ServerContainerHostname = ServerContainerName + ".hostname.com" ClientContainerImageID = "imageid123" ServerContainerImageID = "imageid456" ClientContainerImageNodeID = report.MakeContainerImageNodeID(ClientContainerImageID) ServerContainerImageNodeID = report.MakeContainerImageNodeID(ServerContainerImageID) ClientContainerImageName = "image/client" ServerContainerImageName = "image/server" KubernetesNamespace = "ping" ClientPodID = "ping/pong-a" ServerPodID = "ping/pong-b"
func TestReporter(t *testing.T) { var controlProbeID = "a1b2c3d4" containerImageNodeID := report.MakeContainerImageNodeID("baz") rpt, err := docker.NewReporter(mockRegistryInstance, "host1", controlProbeID, nil).Report() if err != nil { t.Fatal(err) } // Reporter should add a container { containerNodeID := report.MakeContainerNodeID("ping") node, ok := rpt.Container.Nodes[containerNodeID] if !ok { t.Fatalf("Expected report to have container image %q, but not found", containerNodeID) } for k, want := range map[string]string{ docker.ContainerID: "ping", docker.ContainerName: "pong", docker.ImageID: "baz", report.ControlProbeID: controlProbeID, } { if have, ok := node.Latest.Lookup(k); !ok || have != want { t.Errorf("Expected container %s latest %q: %q, got %q", containerNodeID, k, want, have) } } // container should have controls if len(rpt.Container.Controls) == 0 { t.Errorf("Container should have some controls") } // container should have the image as a parent if parents, ok := node.Parents.Lookup(report.ContainerImage); !ok || !parents.Contains(containerImageNodeID) { t.Errorf("Expected container %s to have parent container image %q, got %q", containerNodeID, containerImageNodeID, parents) } } // Reporter should add a container image { node, ok := rpt.ContainerImage.Nodes[containerImageNodeID] if !ok { t.Fatalf("Expected report to have container image %q, but not found", containerImageNodeID) } for k, want := range map[string]string{ docker.ImageID: "baz", docker.ImageName: "bang", docker.ImageLabelPrefix + "imgfoo1": "bar1", docker.ImageLabelPrefix + "imgfoo2": "bar2", } { if have, ok := node.Latest.Lookup(k); !ok || have != want { t.Errorf("Expected container image %s latest %q: %q, got %q", containerImageNodeID, k, want, have) } } // container image should have no controls if len(rpt.ContainerImage.Controls) != 0 { t.Errorf("Container images should not have any controls") } } }
var ( serverHostID = "host1" serverHostNodeID = report.MakeHostNodeID(serverHostID) randomIP = "3.4.5.6" randomPort = "56789" randomEndpointNodeID = report.MakeEndpointNodeID(serverHostID, randomIP, randomPort) serverIP = "192.168.1.1" serverPort = "80" serverEndpointNodeID = report.MakeEndpointNodeID(serverHostID, serverIP, serverPort) container1ID = "11b2c3d4e5" container1IP = "192.168.0.1" container1Name = "foo" container1NodeID = report.MakeContainerNodeID(container1ID) container1Port = "16782" container1EndpointNodeID = report.MakeEndpointNodeID(serverHostID, container1IP, container1Port) duplicatedIP = "192.168.0.2" duplicatedPort = "80" duplicatedEndpointNodeID = report.MakeEndpointNodeID(serverHostID, duplicatedIP, duplicatedPort) container2ID = "21b2c3d4e5" container2IP = duplicatedIP container2Name = "bar" container2NodeID = report.MakeContainerNodeID(container2ID) pauseContainerID = "31b2c3d4e5" pauseContainerIP = duplicatedIP
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) } } }