예제 #1
0
func (spec specCommit) Resolve(g system.CoreGraph, mid uint64, src system.VertexTuple) (e system.StdEdge, success bool) {
	e = system.StdEdge{
		Source: src.ID,
		Props:  ps.NewMap(),
		EType:  "version",
	}
	e.Props = e.Props.Set("sha1", system.Property{MsgSrc: mid, Value: spec.Sha1})

	re := g.OutWith(src.ID, q.Qbe(system.EType("version")))
	if len(re) > 0 {
		sha1, _ := re[0].Props.Lookup("sha1")
		e.ID = re[0].ID // FIXME setting the id to non-0 AND failing is currently unhandled
		if sha1.(system.Property).Value == spec.Sha1 {
			success = true
			e.Target = re[0].Target
		} else {
			rv := g.VerticesWith(q.Qbv(system.VType("commit"), "sha1", spec.Sha1))
			if len(rv) == 1 {
				success = true
				e.Target = rv[0].ID
			}
		}
	} else {
		rv := g.VerticesWith(q.Qbv(system.VType("commit"), "sha1", spec.Sha1))
		if len(rv) == 1 {
			success = true
			e.Target = rv[0].ID
		}
	}

	return
}
예제 #2
0
파일: process.go 프로젝트: sdboyer/pipeviz
func (spec specUnixDomainListener) Resolve(g system.CoreGraph, mid uint64, src system.VertexTuple) (e system.StdEdge, success bool) {
	// check for existing edge; this one is quite straightforward
	re := g.OutWith(src.ID, q.Qbe(system.EType("listening"), "type", "unix", "path", spec.Path))
	if len(re) == 1 {
		return re[0], true
	}

	e = system.StdEdge{
		Source: src.ID,
		Props:  ps.NewMap(),
		EType:  "listening",
	}

	e.Props = e.Props.Set("path", system.Property{MsgSrc: mid, Value: spec.Path})

	envid, _, hasenv := findEnv(g, src)
	if hasenv {
		rv := g.PredecessorsWith(envid, q.Qbv(system.VType("comm"), "type", "unix", "path", spec.Path))
		if len(rv) == 1 {
			success = true
			e.Target = rv[0].ID
		}
	}

	return
}
예제 #3
0
파일: process.go 프로젝트: sdboyer/pipeviz
func (spec specParentDataset) Resolve(g system.CoreGraph, mid uint64, src system.VertexTuple) (e system.StdEdge, success bool) {
	e = system.StdEdge{
		Source: src.ID,
		Props:  ps.NewMap(),
		EType:  "dataset-gateway",
	}
	e.Props = e.Props.Set("name", system.Property{MsgSrc: mid, Value: spec.Name})

	// check for existing link - there can be only be one
	re := g.OutWith(src.ID, q.Qbe(system.EType("dataset-gateway")))
	if len(re) == 1 {
		success = true
		e = re[0]
		// TODO semantics should preclude this from being able to change, but doing it dirty means force-setting it anyway for now
	} else {

		// no existing link found; search for proc directly
		envid, _, _ := findEnv(g, src)
		rv := g.PredecessorsWith(envid, q.Qbv(system.VType("parent-dataset"), "name", spec.Name))
		if len(rv) != 0 { // >1 shouldn't be possible
			success = true
			e.Target = rv[0].ID
		}
	}

	return
}
예제 #4
0
파일: commit.go 프로젝트: sdboyer/pipeviz
func (spec specGitCommitParent) Resolve(g system.CoreGraph, mid uint64, src system.VertexTuple) (e system.StdEdge, success bool) {
	e = system.StdEdge{
		Source: src.ID,
		Props:  ps.NewMap(),
		EType:  "parent-commit",
	}

	re := g.OutWith(src.ID, q.Qbe(system.EType("parent-commit"), "pnum", spec.ParentNum))
	if len(re) > 0 {
		success = true
		e.Target = re[0].Target
		e.Props = re[0].Props
		// FIXME evidence of a problem here - since we're using pnum as the deduping identifier, there's no
		// way it could also sensibly change its MsgSrc value. This is very much a product of the intensional/extensional
		// identity problem: what does it mean to have the identifying data change? is it now a new thing? was it the old thing,
		// and it underwent a transition into the new thing? or is there no distinction between the old and new thing?
		e.Props = e.Props.Set("sha1", system.Property{MsgSrc: mid, Value: spec.Sha1})
		e.ID = re[0].ID
	} else {
		rv := g.VerticesWith(q.Qbv(system.VType("commit"), "sha1", spec.Sha1))
		if len(rv) == 1 {
			success = true
			e.Target = rv[0].ID
			e.Props = e.Props.Set("pnum", system.Property{MsgSrc: mid, Value: spec.ParentNum})
			e.Props = e.Props.Set("sha1", system.Property{MsgSrc: mid, Value: spec.Sha1})
		}
	}

	return
}
예제 #5
0
func (spec EnvLink) Resolve(g system.CoreGraph, mid uint64, src system.VertexTuple) (e system.StdEdge, success bool) {
	_, e, success = findEnv(g, src)

	// Whether we find a match or not, have to merge in the EnvLink
	e.Props = maputil.FillPropMap(mid, false,
		pp("hostname", spec.Address.Hostname),
		pp("ipv4", spec.Address.Ipv4),
		pp("ipv6", spec.Address.Ipv6),
		pp("nick", spec.Nick),
	)

	// If we already found the matching edge, bail out now
	if success {
		return
	}

	rv := g.VerticesWith(q.Qbv(system.VType("environment")))
	for _, vt := range rv {
		// TODO this'll be cross-package eventually - reorg needed
		if maputil.AnyMatch(e.Props, vt.Vertex.Properties, "nick", "hostname", "ipv4", "ipv6") {
			success = true
			e.Target = vt.ID
			break
		}
	}

	return
}
예제 #6
0
파일: process.go 프로젝트: sdboyer/pipeviz
func (spec specLocalLogic) Resolve(g system.CoreGraph, mid uint64, src system.VertexTuple) (e system.StdEdge, success bool) {
	e = system.StdEdge{
		Source: src.ID,
		Props:  ps.NewMap(),
		EType:  "logic-link",
	}

	// search for existing link
	re := g.OutWith(src.ID, q.Qbe(system.EType("logic-link"), "path", spec.Path))
	if len(re) == 1 {
		// TODO don't set the path prop again, it's the unique id...meh, same question here w/uniqueness as above
		success = true
		e = re[0]
		return
	}

	// no existing link found, search for proc directly
	envid, _, _ := findEnv(g, src)
	rv := g.PredecessorsWith(envid, q.Qbv(system.VType("logic-state"), "path", spec.Path))
	if len(rv) == 1 {
		success = true
		e.Target = rv[0].ID
	}

	return
}
예제 #7
0
파일: commit.go 프로젝트: sdboyer/pipeviz
func commitUnify(g system.CoreGraph, u system.UnifyInstructionForm) uint64 {
	candidates := g.VerticesWith(q.Qbv(system.VType("commit"), "sha1", u.Vertex().Properties()["sha1"]))

	if len(candidates) > 0 { // there can be only one
		return candidates[0].ID
	}

	return 0
}
예제 #8
0
파일: process.go 프로젝트: sdboyer/pipeviz
func processUnify(g system.CoreGraph, u system.UnifyInstructionForm) uint64 {
	// only one scoping edge - the envlink
	edge, success := u.ScopingSpecs()[0].(EnvLink).Resolve(g, 0, emptyVT(u.Vertex()))
	if !success {
		// FIXME scoping edge resolution failure does not mean no match - there could be an orphan
		return 0
	}

	return findMatchingEnvId(g, edge, g.VerticesWith(q.Qbv(system.VType("process"), "pid", u.Vertex().Properties()["pid"])))
}
예제 #9
0
파일: util.go 프로젝트: sdboyer/pipeviz
func findEnvironment(g system.CoreGraph, props ps.Map) (envid uint64, success bool) {
	rv := g.VerticesWith(q.Qbv(system.VType("environment")))
	for _, vt := range rv {
		if maputil.AnyMatch(props, vt.Vertex.Props(), "hostname", "ipv4", "ipv6", "nick") {
			return vt.ID, true
		}
	}

	return
}
예제 #10
0
파일: dataset.go 프로젝트: sdboyer/pipeviz
func parentDatasetUnify(g system.CoreGraph, u system.UnifyInstructionForm) uint64 {
	edge, success := u.ScopingSpecs()[0].(EnvLink).Resolve(g, 0, emptyVT(u.Vertex()))
	if !success {
		// FIXME scoping edge resolution failure does not mean no match - there could be an orphan
		return 0
	}

	props := u.Vertex().Properties()
	return findMatchingEnvId(g, edge, g.VerticesWith(q.Qbv(system.VType("parent-dataset"), "path", props["path"], "name", props["name"])))
}
예제 #11
0
func envUnify(g system.CoreGraph, u system.UnifyInstructionForm) uint64 {
	matches := g.VerticesWith(q.Qbv(system.VType("environment")))

	for _, e := range matches {
		if maputil.AnyMatch(e.Vertex.Properties, u.Vertex().Properties(), "hostname", "ipv4", "ipv6") {
			return e.ID
		}
	}

	return 0
}
예제 #12
0
func TestVerticesWith(t *testing.T) {
	g := getGraphFixture()
	var result system.VertexTupleVector

	result = g.VerticesWith(q.Qbv())
	if len(result) != 5 {
		t.Errorf("Should find 4 vertices with no filter; found %v", len(result))
	}

	result = g.VerticesWith(q.Qbv(system.VType("env")))
	if len(result) != 2 {
		t.Errorf("Should find 2 vertices when filtering to type env; found %v", len(result))
	}

	result = g.VerticesWith(q.Qbv(system.VType("nonexistent-type")))
	if len(result) != 0 {
		t.Errorf("Should find no vertices when filtering on type that's not present; found %v", len(result))
	}

	result = g.VerticesWith(q.Qbv(system.VTypeNone, "prop1", "bar"))
	if len(result) != 2 {
		t.Errorf("Should find two vertices with prop1 == \"bar\"; found %v", len(result))
	}

	result = g.VerticesWith(q.Qbv(system.VTypeNone, "none-have-this-prop-key", "doesn't matter"))
	if len(result) != 0 {
		t.Errorf("Should find no vertices when filtering on nonexistent prop key; found %v", len(result))
	}

	result = g.VerticesWith(q.Qbv(system.VType("env"), "prop1", "foo"))
	if len(result) != 1 {
		t.Errorf("Should find one vertex when filtering to env types and with prop1 == \"foo\"; found %v", len(result))
	}

	result = g.VerticesWith(q.Qbv(system.VType("env"), "prop2", 42))
	if len(result) != 1 {
		t.Errorf("Should find one vertex when filtering to env types and with prop2 == 42; found %v", len(result))
	}
}
예제 #13
0
파일: process.go 프로젝트: sdboyer/pipeviz
func commUnify(g system.CoreGraph, u system.UnifyInstructionForm) uint64 {
	// only one scoping edge - the envlink
	edge, success := u.ScopingSpecs()[0].(EnvLink).Resolve(g, 0, emptyVT(u.Vertex()))
	if !success {
		// FIXME scoping edge resolution failure does not mean no match - there could be an orphan
		return 0
	}

	vp := u.Vertex().Properties()
	typ, _ := vp["type"]
	path, haspath := vp["path"]
	if haspath {
		return findMatchingEnvId(g, edge, g.VerticesWith(q.Qbv(system.VType("comm"),
			"type", typ,
			"path", path)))
	} else {
		port, _ := vp["port"]
		return findMatchingEnvId(g, edge, g.VerticesWith(q.Qbv(system.VType("comm"),
			"type", typ,
			"port", port)))
	}
}
예제 #14
0
파일: pkg_yum.go 프로젝트: sdboyer/pipeviz
func pkgYumUnify(g system.CoreGraph, u system.UnifyInstructionForm) uint64 {
	props := u.Vertex().Properties()
	vtv := g.VerticesWith(q.Qbv(system.VType("pkg-yum"),
		"name", props["name"],
		"version", props["version"],
		"arch", props["arch"],
		"epoch", props["epoch"],
	))

	if len(vtv) > 0 {
		return vtv[0].ID
	}
	return 0
}
예제 #15
0
func TestQbv(t *testing.T) {
	// ensure implement both VFilter and EFilter interfaces, and the V chainer
	var _ system.VFilterChain = vertexFilter{}
	var _ system.VEFilter = vertexFilter{}

	assert.Equal(t, Qbv(), vertexFilter{}, "qbv with no args creates an empty vertexFilter")
	assert.Equal(t, Qbv(), vertexFilter{vtype: system.VTypeNone}, "qbv with no args creates equivalent of passing VTypeNone as first arg")
	assert.Equal(t,
		Qbv(system.VType("foo")),
		vertexFilter{vtype: system.VType("foo")},
		"qbv with single arg assigns to VType struct prop")
	assert.Equal(t,
		Qbv(system.VTypeNone, "foo"),
		vertexFilter{vtype: system.VTypeNone},
		"qbv with two args ignores second (unpaired) arg")
	assert.Equal(t,
		Qbv(system.VTypeNone, "foo", "bar"),
		vertexFilter{vtype: system.VTypeNone, props: []system.PropPair{{"foo", "bar"}}},
		"qbv with three args creates one pair of second (key) and third (value) args")
	assert.Equal(t,
		Qbv(system.VTypeNone, "foo", "bar", "baz"),
		vertexFilter{vtype: system.VTypeNone, props: []system.PropPair{{"foo", "bar"}}},
		"qbv with four args creates one pair from 2nd and 3rd args, ignores 4th")

	// ensure that some incorrect things owing to loose typing correctly panic
	assert.Panics(t, func() {
		Qbv("foo")
	}, "qbv panics on type conversion when passing a string instead of VType")

	assert.Panics(t, func() {
		Qbv(system.VTypeNone, 1, "foo")
	}, "qbv panics on type conversion when second argument (with corresponding pair val 3rd arg) is non-string")

	assert.Panics(t, func() {
		Qbv(system.VTypeNone, "foo", "bar", 1, "baz")
	}, "qbv panics on type conversion when Nth even argument (with corresponding pair val N+1 arg) is non-string")
}
예제 #16
0
파일: dataset.go 프로젝트: sdboyer/pipeviz
func datasetUnify(g system.CoreGraph, u system.UnifyInstructionForm) uint64 {
	vtv := g.VerticesWith(q.Qbv(system.VType("dataset"), "name", u.Vertex().Properties()["name"]))
	if len(vtv) == 0 {
		return 0
	}

	spec := u.ScopingSpecs()[0].(specDatasetHierarchy)
	el, success := spec.Environment.Resolve(g, 0, emptyVT(u.Vertex()))
	// FIXME scoping edge resolution failure does not mean no match - there could be an orphan
	if success {
		for _, vt := range vtv {
			if id := findMatchingEnvId(g, el, g.SuccessorsWith(vt.ID, q.Qbe(system.EType("dataset-hierarchy")))); id != 0 {
				return vt.ID
			}
		}
	}

	return 0
}
예제 #17
0
파일: util.go 프로젝트: sdboyer/pipeviz
func findDataset(g system.CoreGraph, envid uint64, name []string) (id uint64, success bool) {
	// first time through use the parent type
	vtype := system.VType("parent-dataset")
	id = envid

	var n string
	for len(name) > 0 {
		n, name = name[0], name[1:]
		rv := g.PredecessorsWith(id, q.Qbv(vtype, "name", n))
		vtype = "dataset"

		if len(rv) != 1 {
			return 0, false
		}

		id = rv[0].ID
	}

	return id, true
}
예제 #18
0
// Tests adjacentWith(), which effectively tests SuccessorsWith() and PredecessorsWith()
func TestAdjacentWith(t *testing.T) {
	g := getGraphFixture()
	var result system.VertexTupleVector

	// basic, unfiltered tests first to ensure the right data is coming through
	// vtx 2 has just one in-edge
	result = g.adjacentWith(2, q.Qbv(), true)
	if len(result) != 1 {
		t.Errorf("Vertex 2 has one predecessor, but got %v vertices", len(result))
	}

	result = g.PredecessorsWith(2, q.Qbv())
	if len(result) != 1 {
		t.Errorf("Vertex 2 has one predecessor, but got %v vertices", len(result))
	}

	// vtx 1 has one out-edge and one in-edge
	result = g.adjacentWith(1, q.Qbv(), false)
	if len(result) != 1 {
		t.Errorf("Vertex 1 has one successor, but got %v vertices", len(result))
	}

	result = g.SuccessorsWith(1, q.Qbv())
	if len(result) != 1 {
		t.Errorf("Vertex 1 has one successor, but got %v vertices", len(result))
	}

	// vtx 5 is an isolate
	result = g.adjacentWith(5, q.Qbv(), true)
	if len(result) != 0 {
		t.Errorf("Vertex 5 has no predecessors, but got %v vertices", len(result))
	}

	result = g.PredecessorsWith(5, q.Qbv())
	if len(result) != 0 {
		t.Errorf("Vertex 5 has no predecessors, but got %v vertices", len(result))
	}

	result = g.adjacentWith(5, q.Qbv(), false)
	if len(result) != 0 {
		t.Errorf("Vertex 5 has no successors, but got %v vertices", len(result))
	}

	result = g.SuccessorsWith(5, q.Qbv())
	if len(result) != 0 {
		t.Errorf("Vertex 5 has no successors, but got %v vertices", len(result))
	}

	// qbe w/out args should be equivalent
	result = g.PredecessorsWith(2, q.Qbe())
	if len(result) != 1 {
		t.Errorf("Vertex 2 has one predecessor, but got %v vertices (qbe)", len(result))
	}

	result = g.SuccessorsWith(1, q.Qbe())
	if len(result) != 1 {
		t.Errorf("Vertex 1 has one successor, but got %v vertices (qbe)", len(result))
	}

	// deduping: vtx 4 has two in-edges and none out, but those edges are parallel so only one unique vtx
	result = g.PredecessorsWith(4, q.Qbv())
	if len(result) != 1 {
		t.Errorf("Vertex 4 has two in-edges, but only one unique predecessor; however, got %v vertices", len(result))
	}

	// vtx 3 is on the other side of vtx 4 - three out-edges, but only two uniques
	result = g.SuccessorsWith(3, q.Qbv())
	if len(result) != 2 {
		t.Errorf("Vertex 4 has three out-edges, but only two unique successors; however, got %v vertices", len(result))
	}

	// filter checks, beginning with edge and/or vertex typing
	result = g.SuccessorsWith(3, q.Qbv(system.VType("vt3")))
	if len(result) != 1 {
		t.Errorf("Vertex 4 has only one unique successor of type \"vt3\"; however, got %v vertices", len(result))
	}

	result = g.SuccessorsWith(3, q.Qbe(system.EType("dummy-edge-type2")))
	if len(result) != 2 {
		t.Errorf("Vertex 4 has two out-edges of \"dummy-edge-type2\" and both point to different vertices, so expecting 2, but got %v vertices", len(result))
	}

	result = g.SuccessorsWith(3, q.Qbe(system.EType("dummy-edge-type2")).And(q.Qbv(system.VType("env"))))
	if len(result) != 1 {
		t.Errorf("Vertex 4 has two unique successors along \"dummy-edge-type2\" out-edges, but only one is vtype \"env\". However, got %v vertices", len(result))
	}

	result = g.SuccessorsWith(3, q.Qbe(system.EType("dummy-edge-type3")).And(q.Qbv(system.VType("env"))))
	if len(result) != 0 {
		t.Errorf("Vertex 4 has one unique successor along \"dummy-edge-type3\" out-edges, but it is not an \"env\" type. However, got %v vertices", len(result))
	}

	// prop-filtering checks
	result = g.SuccessorsWith(3, q.Qbv(system.VTypeNone, "prop2", 42))
	if len(result) != 2 {
		t.Errorf("Vertex 4 has only two unique successors with \"prop2\" at 42; however, got %v vertices", len(result))
	}

	result = g.SuccessorsWith(3, q.Qbe(system.ETypeNone, "eprop2", "bar"))
	if len(result) != 2 {
		t.Errorf("Vertex 4 has two unique successors connected by two out-edges with \"eprop2\" at \"bar\"; however, got %v vertices", len(result))
	}

	result = g.SuccessorsWith(3, q.Qbv(system.VTypeNone, "prop1", "baz", "prop2", 42))
	if len(result) != 1 {
		t.Errorf("Vertex 4 has only one unique successor with \"prop1\" at \"baz\" and \"prop2\" at 42; however, got %v vertices", len(result))
	}

	result = g.SuccessorsWith(3, q.Qbv(system.VTypeNone, "prop3", "qux", "prop2", 42))
	if len(result) != 1 {
		t.Errorf("Vertex 4 has only one unique successor with BOTH \"prop3\" at \"qux\" and \"prop2\" at 42; however, got %v vertices", len(result))
	}

	result = g.SuccessorsWith(3, q.Qbe(system.ETypeNone, "eprop2", "bar").And(q.Qbv(system.VTypeNone, "prop1", "baz")))
	if len(result) != 1 {
		t.Errorf("Vertex 4 has only one unique successor with \"prop1\" at \"baz\" along an out-edge with \"eprop2\" at \"bar\"; however, got %v vertices", len(result))
	}

	result = g.SuccessorsWith(3, q.Qbe(system.ETypeNone, "eprop2", "bar").And(q.Qbv(system.VType("vt3"), "prop1", "baz")))
	if len(result) != 1 {
		t.Errorf("Vertex 4 has one unique successor of type \"vt3\" with \"prop1\" at \"baz\" along an out-edge with \"eprop2\" at \"bar\"; however, got %v vertices", len(result))
	}
}
예제 #19
0
func (spec DataLink) Resolve(g system.CoreGraph, mid uint64, src system.VertexTuple) (e system.StdEdge, success bool) {
	e = system.StdEdge{
		Source: src.ID,
		Props:  ps.NewMap(),
		EType:  "datalink",
	}

	// DataLinks have a 'name' field that is expected to be unique for the source, if present
	if spec.Name != "" {
		// TODO 'name' is a traditional unique key; a change in it inherently denotes a new edge. how to handle this?
		// FIXME this approach just always updates the mid, which is weird?
		e.Props = e.Props.Set("name", system.Property{MsgSrc: mid, Value: spec.Name})

		re := g.OutWith(src.ID, q.Qbe(system.EType("datalink"), "name", spec.Name))
		if len(re) == 1 {
			success = true
			e = re[0]
		}
	}

	if spec.Type != "" {
		e.Props = e.Props.Set("type", system.Property{MsgSrc: mid, Value: spec.Type})
	}
	if spec.Subset != "" {
		e.Props = e.Props.Set("subset", system.Property{MsgSrc: mid, Value: spec.Subset})
	}
	if spec.Interaction != "" {
		e.Props = e.Props.Set("interaction", system.Property{MsgSrc: mid, Value: spec.Interaction})
	}

	// Special bits: if we have ConnUnix data, eliminate ConnNet data, and vice-versa.
	var isLocal bool
	if spec.ConnUnix.Path != "" {
		isLocal = true
		e.Props = e.Props.Set("path", system.Property{MsgSrc: mid, Value: spec.ConnUnix.Path})
		e.Props = e.Props.Delete("hostname")
		e.Props = e.Props.Delete("ipv4")
		e.Props = e.Props.Delete("ipv6")
		e.Props = e.Props.Delete("port")
		e.Props = e.Props.Delete("proto")
	} else {
		e.Props = e.Props.Set("port", system.Property{MsgSrc: mid, Value: spec.ConnNet.Port})
		e.Props = e.Props.Set("proto", system.Property{MsgSrc: mid, Value: spec.ConnNet.Proto})

		// can only be one of hostname, ipv4 or ipv6
		if spec.ConnNet.Hostname != "" {
			e.Props = e.Props.Set("hostname", system.Property{MsgSrc: mid, Value: spec.ConnNet.Hostname})
		} else if spec.ConnNet.Ipv4 != "" {
			e.Props = e.Props.Set("ipv4", system.Property{MsgSrc: mid, Value: spec.ConnNet.Ipv4})
		} else {
			e.Props = e.Props.Set("ipv6", system.Property{MsgSrc: mid, Value: spec.ConnNet.Ipv6})
		}
	}

	if success {
		return
	}

	var sock system.VertexTuple
	var rv system.VertexTupleVector // just for reuse
	// If net, must scan; if local, a bit easier.
	if !isLocal {
		// First, find the environment vertex
		rv = g.VerticesWith(q.Qbv(system.VType("environment")))
		var envid uint64
		for _, vt := range rv {
			if maputil.AnyMatch(e.Props, vt.Vertex.Properties, "hostname", "ipv4", "ipv6") {
				envid = vt.ID
				break
			}
		}

		// No matching env found, bail out
		if envid == 0 {
			return
		}

		// Now, walk the environment's edges to find the vertex representing the port
		rv = g.PredecessorsWith(envid, q.Qbv(system.VType("comm"), "type", "port", "port", spec.ConnNet.Port).And(q.Qbe(system.EType("envlink"))))

		if len(rv) != 1 {
			return
		}
		sock = rv[0]

		// With sock in hand, now find its proc
		rv = g.PredecessorsWith(sock.ID, q.Qbe(system.EType("listening"), "proto", spec.ConnNet.Proto).And(q.Qbv(system.VType("process"))))
		if len(rv) != 1 {
			// TODO could/will we ever allow >1?
			return
		}
	} else {
		envid, _, exists := findEnv(g, src)

		if !exists {
			// this is would be a pretty weird case
			return
		}

		// Walk the graph to find the vertex representing the unix socket
		rv = g.PredecessorsWith(envid, q.Qbv(system.VType("comm"), "path", spec.ConnUnix.Path).And(q.Qbe(system.EType("envlink"))))
		if len(rv) != 1 {
			return
		}
		sock = rv[0]

		// With sock in hand, now find its proc
		rv = g.PredecessorsWith(sock.ID, q.Qbv(system.VType("process")).And(q.Qbe(system.EType("listening"))))
		if len(rv) != 1 {
			// TODO could/will we ever allow >1?
			return
		}
	}

	rv = g.SuccessorsWith(rv[0].ID, q.Qbv(system.VType("parent-dataset")))
	// FIXME this absolutely could be more than 1
	if len(rv) != 1 {
		return
	}
	dataset := rv[0]

	// if the spec indicates a subset, find it
	if spec.Subset != "" {
		rv = g.PredecessorsWith(rv[0].ID, q.Qbv(system.VType("dataset"), "name", spec.Subset).And(q.Qbe(system.EType("dataset-hierarchy"))))
		if len(rv) != 1 {
			return
		}
		dataset = rv[0]
	}

	// FIXME only recording the final target id is totally broken; see https://github.com/pipeviz/pipeviz/issues/37

	// Aaaand we found our target.
	success = true
	e.Target = dataset.ID
	return
}