diff --git a/render/container.go b/render/container.go index 347d56745..47aed260f 100644 --- a/render/container.go +++ b/render/container.go @@ -158,10 +158,7 @@ var ContainerWithImageNameRenderer = Memoise(containerWithImageNameRenderer{}) // ContainerImageRenderer produces a graph where each node is a container image // with the original containers as children var ContainerImageRenderer = Memoise(FilterEmpty(report.Container, - MakeMap( - MapContainerImage2Name, - containerImageRenderer{}, - ), + containerImageRenderer{}, )) // ContainerHostnameRenderer is a Renderer which produces a renderable container @@ -260,7 +257,8 @@ type containerImageRenderer struct{} func (m containerImageRenderer) Render(ctx context.Context, rpt report.Report) Nodes { containers := ContainerWithImageNameRenderer.Render(ctx, rpt) - ret := newJoinResults(rpt.ContainerImage.Nodes) + images := rpt.ContainerImage.Nodes + ret := newJoinResults(nil) for _, n := range containers.Nodes { if n.Topology == Pseudo { @@ -272,8 +270,18 @@ func (m containerImageRenderer) Render(ctx context.Context, rpt report.Report) N if !ok { continue } - id := report.MakeContainerImageNodeID(imageID) - ret.addChildAndChildren(n, id, report.ContainerImage) + id := containerImageNodeID(n) + if id == "" { + continue + } + ret.addWithCreate(n, id, func() report.Node { + imageID = report.MakeContainerImageNodeID(imageID) + imageNode, ok := images[imageID] + if !ok { + imageNode = report.MakeNode(imageID).WithTopology(report.ContainerImage) + } + return imageNode.WithID(id) + }) } return ret.result(containers) } @@ -292,21 +300,6 @@ func containerImageNodeID(n report.Node) string { return report.MakeContainerImageNodeID(imageNameWithoutTag) } -// MapContainerImage2Name ignores image versions -func MapContainerImage2Name(n report.Node) report.Node { - // Propagate all pseudo nodes - if n.Topology == Pseudo { - return n - } - - n.ID = containerImageNodeID(n) - if n.ID == "" { - return report.Node{} - } - - return n -} - var containerHostnameTopology = MakeGroupNodeTopology(report.Container, report.DockerContainerHostname) // containerHostnameRenderer collects containers by docker hostname diff --git a/render/render.go b/render/render.go index 4327277de..2fdd4b9a3 100644 --- a/render/render.go +++ b/render/render.go @@ -158,6 +158,7 @@ type joinResults struct { multi map[string][]string // input node ID -> output node IDs - exceptional case } +// Create a joinResults with a starting set of nodes, that other info will be added into. func newJoinResults(inputNodes report.Nodes) joinResults { nodes := make(report.Nodes, len(inputNodes)) for id, n := range inputNodes { @@ -217,6 +218,19 @@ func (ret *joinResults) addChildAndChildren(m report.Node, id string, topology s ret.mapChild(m.ID, id) } +func (ret *joinResults) addWithCreate(m report.Node, id string, create func() report.Node) { + result, exists := ret.nodes[id] + if !exists { + result = create() + ret.nodes[id] = result + } + ret.addUnmappedChild(m, id, result.Topology) + result = ret.nodes[id] + result.Children.UnsafeMerge(m.Children) + ret.nodes[id] = result + ret.mapChild(m.ID, id) +} + // Add a copy of n straight into the results func (ret *joinResults) passThrough(n report.Node) { n.Adjacency = nil // result() assumes all nodes start with no adjacencies