From 4150ae7307d4bd24a602d9c556001e408582cbbe Mon Sep 17 00:00:00 2001 From: James Shubin Date: Sun, 28 May 2017 19:51:56 -0400 Subject: [PATCH] pgraph: Replace edge struct with interface This further cleans up the pgraph lib to be more generic. --- lib/main.go | 7 +- pgraph/graphsync.go | 13 ++- pgraph/graphviz.go | 10 +-- pgraph/pgraph.go | 62 ++++++-------- pgraph/pgraph_test.go | 162 ++++++++++++++++++++---------------- resources/actions.go | 3 + resources/autoedge.go | 6 +- resources/autogroup.go | 6 +- resources/autogroup_test.go | 98 ++++++++++++---------- resources/edge.go | 41 +++++++++ yamlgraph/gconfig.go | 6 +- yamlgraph2/gconfig.go | 6 +- 12 files changed, 247 insertions(+), 173 deletions(-) create mode 100644 resources/edge.go diff --git a/lib/main.go b/lib/main.go index a8393f4f..2d42ef4c 100644 --- a/lib/main.go +++ b/lib/main.go @@ -492,8 +492,13 @@ func (obj *Main) Run() error { resources.VtoR(v).Exit() // sync return nil } + edgeCmpFn := func(e1, e2 pgraph.Edge) (bool, error) { + edge1 := e1.(*resources.Edge) // panic if wrong + edge2 := e2.(*resources.Edge) // panic if wrong + return edge1.Name == edge2.Name && edge1.Notify == edge2.Notify, nil // simple cmp + } // on success, this updates the receiver graph... - if err := oldGraph.GraphSync(newGraph, vertexCmpFn, vertexAddFn, vertexRemoveFn); err != nil { + if err := oldGraph.GraphSync(newGraph, vertexCmpFn, vertexAddFn, vertexRemoveFn, edgeCmpFn); err != nil { log.Printf("Main: Error running graph sync: %v", err) // unpause! if !first { diff --git a/pgraph/graphsync.go b/pgraph/graphsync.go index bd30fd11..6895bab4 100644 --- a/pgraph/graphsync.go +++ b/pgraph/graphsync.go @@ -29,7 +29,7 @@ import ( // This updates the Graph on success only. // FIXME: should we do this with copies of the vertex resources? // FIXME: add test cases -func (obj *Graph) GraphSync(newGraph *Graph, vertexCmpFn func(Vertex, Vertex) (bool, error), vertexAddFn func(Vertex) error, vertexRemoveFn func(Vertex) error) error { +func (obj *Graph) GraphSync(newGraph *Graph, vertexCmpFn func(Vertex, Vertex) (bool, error), vertexAddFn func(Vertex) error, vertexRemoveFn func(Vertex) error, edgeCmpFn func(Edge, Edge) (bool, error)) error { oldGraph := obj.Copy() // work on a copy of the old graph if oldGraph == nil { @@ -43,7 +43,7 @@ func (obj *Graph) GraphSync(newGraph *Graph, vertexCmpFn func(Vertex, Vertex) (b var lookup = make(map[Vertex]Vertex) var vertexKeep []Vertex // list of vertices which are the same in new graph - var edgeKeep []*Edge // list of vertices which are the same in new graph + var edgeKeep []Edge // list of vertices which are the same in new graph for v := range newGraph.Adjacency() { // loop through the vertices (resources) var vertex Vertex @@ -100,9 +100,14 @@ func (obj *Graph) GraphSync(newGraph *Graph, vertexCmpFn func(Vertex, Vertex) (b } edge, exists := oldGraph.Adjacency()[vertex1][vertex2] - if !exists || edge.Name != e.Name { // TODO: edgeCmp - edge = e // use or overwrite edge + if !exists { + edge = e // use edge + } else if b, err := edgeCmpFn(edge, e); err != nil { + return errwrap.Wrapf(err, "edgeCmpFn failed") + } else if !b { + edge = e // overwrite edge } + oldGraph.Adjacency()[vertex1][vertex2] = edge // store it (AddEdge) edgeKeep = append(edgeKeep, edge) // mark as saved } diff --git a/pgraph/graphviz.go b/pgraph/graphviz.go index e1c051c2..522a058e 100644 --- a/pgraph/graphviz.go +++ b/pgraph/graphviz.go @@ -50,11 +50,11 @@ func (g *Graph) Graphviz() (out string) { for j := range g.Adjacency()[i] { k := g.Adjacency()[i][j] // use str for clearer output ordering - if k.Notify { - str += fmt.Sprintf("\t\"%s\" -> \"%s\" [label=\"%s\",style=bold];\n", i, j, k.Name) - } else { - str += fmt.Sprintf("\t\"%s\" -> \"%s\" [label=\"%s\"];\n", i, j, k.Name) - } + //if fmtBoldFn(k) { // TODO: add this sort of formatting + // str += fmt.Sprintf("\t\"%s\" -> \"%s\" [label=\"%s\",style=bold];\n", i, j, k) + //} else { + str += fmt.Sprintf("\t\"%s\" -> \"%s\" [label=\"%s\"];\n", i, j, k) + //} } } out += str diff --git a/pgraph/pgraph.go b/pgraph/pgraph.go index acb21ba1..e0da0e94 100644 --- a/pgraph/pgraph.go +++ b/pgraph/pgraph.go @@ -34,8 +34,8 @@ import ( type Graph struct { Name string - adjacency map[Vertex]map[Vertex]*Edge // Vertex -> Vertex (edge) - kv map[string]interface{} // some values associated with the graph + adjacency map[Vertex]map[Vertex]Edge // Vertex -> Vertex (edge) + kv map[string]interface{} // some values associated with the graph } // Vertex is the primary vertex struct in this library. It can be anything that @@ -44,12 +44,10 @@ type Vertex interface { fmt.Stringer // String() string } -// Edge is the primary edge struct in this library. -type Edge struct { - Name string - Notify bool // should we send a refresh notification along this edge? - - refresh bool // is there a notify pending for the dest vertex ? +// Edge is the primary edge struct in this library. It can be anything that +// implements Stringer. The string output must be stable and unique in a graph. +type Edge interface { + fmt.Stringer // String() string } // Init initializes the graph which populates all the internal structures. @@ -58,7 +56,7 @@ func (g *Graph) Init() error { return fmt.Errorf("can't initialize graph with empty name") } - //g.adjacency = make(map[Vertex]map[Vertex]*Edge) // not required + //g.adjacency = make(map[Vertex]map[Vertex]Edge) // not required //g.kv = make(map[string]interface{}) // not required return nil } @@ -81,21 +79,11 @@ func NewVertex(x Vertex) Vertex { return x } -// NewEdge returns a new graph edge struct. -func NewEdge(name string) *Edge { - return &Edge{ - Name: name, - } -} - -// Refresh returns the pending refresh status of this edge. -func (obj *Edge) Refresh() bool { - return obj.refresh -} - -// SetRefresh sets the pending refresh status of this edge. -func (obj *Edge) SetRefresh(b bool) { - obj.refresh = b +// NewEdge returns whatever was passed in. This is for compatibility with the +// usage of the old NewEdge method. This is considered deprecated. +// FIXME: remove me +func NewEdge(x Edge) Edge { + return x } // Value returns a value stored alongside the graph in a particular key. @@ -119,7 +107,7 @@ func (g *Graph) Copy() *Graph { } newGraph := &Graph{ Name: g.Name, - adjacency: make(map[Vertex]map[Vertex]*Edge, len(g.adjacency)), + adjacency: make(map[Vertex]map[Vertex]Edge, len(g.adjacency)), kv: g.kv, } for k, v := range g.adjacency { @@ -141,11 +129,11 @@ func (g *Graph) SetName(name string) { // AddVertex uses variadic input to add all listed vertices to the graph. func (g *Graph) AddVertex(xv ...Vertex) { if g.adjacency == nil { // initialize on first use - g.adjacency = make(map[Vertex]map[Vertex]*Edge) + g.adjacency = make(map[Vertex]map[Vertex]Edge) } for _, v := range xv { if _, exists := g.adjacency[v]; !exists { - g.adjacency[v] = make(map[Vertex]*Edge) + g.adjacency[v] = make(map[Vertex]Edge) } } } @@ -159,7 +147,7 @@ func (g *Graph) DeleteVertex(v Vertex) { } // AddEdge adds a directed edge to the graph from v1 to v2. -func (g *Graph) AddEdge(v1, v2 Vertex, e *Edge) { +func (g *Graph) AddEdge(v1, v2 Vertex, e Edge) { // NOTE: this doesn't allow more than one edge between two vertexes... g.AddVertex(v1, v2) // supports adding N vertices now // TODO: check if an edge exists to avoid overwriting it! @@ -169,7 +157,7 @@ func (g *Graph) AddEdge(v1, v2 Vertex, e *Edge) { // DeleteEdge deletes a particular edge from the graph. // FIXME: add test cases -func (g *Graph) DeleteEdge(e *Edge) { +func (g *Graph) DeleteEdge(e Edge) { for v1 := range g.adjacency { for v2, edge := range g.adjacency[v1] { if e == edge { @@ -218,7 +206,7 @@ func (g *Graph) NumEdges() int { // Adjacency returns the adjacency map representing this graph. This is useful // for users who which to operate on the raw data structure more efficiently. // This works because maps are reference types so we can edit this at will. -func (g *Graph) Adjacency() map[Vertex]map[Vertex]*Edge { +func (g *Graph) Adjacency() map[Vertex]map[Vertex]Edge { return g.adjacency } @@ -303,8 +291,8 @@ func (g *Graph) GraphVertices(v Vertex) []Vertex { } // IncomingGraphEdges returns all of the edges that point to vertex v (??? -> v). -func (g *Graph) IncomingGraphEdges(v Vertex) []*Edge { - var edges []*Edge +func (g *Graph) IncomingGraphEdges(v Vertex) []Edge { + var edges []Edge for v1 := range g.adjacency { // reverse paths for v2, e := range g.adjacency[v1] { if v2 == v { @@ -316,8 +304,8 @@ func (g *Graph) IncomingGraphEdges(v Vertex) []*Edge { } // OutgoingGraphEdges returns all of the edges that point from vertex v (v -> ???). -func (g *Graph) OutgoingGraphEdges(v Vertex) []*Edge { - var edges []*Edge +func (g *Graph) OutgoingGraphEdges(v Vertex) []Edge { + var edges []Edge for _, e := range g.adjacency[v] { // forward paths edges = append(edges, e) } @@ -326,8 +314,8 @@ func (g *Graph) OutgoingGraphEdges(v Vertex) []*Edge { // GraphEdges returns an array (slice) of all edges that connect to vertex v. // This is the union of IncomingGraphEdges and OutgoingGraphEdges. -func (g *Graph) GraphEdges(v Vertex) []*Edge { - var edges []*Edge +func (g *Graph) GraphEdges(v Vertex) []Edge { + var edges []Edge edges = append(edges, g.IncomingGraphEdges(v)...) edges = append(edges, g.OutgoingGraphEdges(v)...) return edges @@ -535,7 +523,7 @@ func VertexContains(needle Vertex, haystack []Vertex) bool { } // EdgeContains is an "in array" function to test for an edge in a slice of edges. -func EdgeContains(needle *Edge, haystack []*Edge) bool { +func EdgeContains(needle Edge, haystack []Edge) bool { for _, v := range haystack { if needle == v { return true diff --git a/pgraph/pgraph_test.go b/pgraph/pgraph_test.go index 4ba81897..92987756 100644 --- a/pgraph/pgraph_test.go +++ b/pgraph/pgraph_test.go @@ -27,7 +27,7 @@ type vertex struct { name string } -// String is a required method of the Vertex interface we must fulfill. +// String is a required method of the Vertex interface that we must fulfill. func (v *vertex) String() string { return v.name } @@ -38,6 +38,22 @@ func NV(s string) Vertex { return NewVertex(obj) } +// edge is a test struct to test the library. +type edge struct { + name string +} + +// String is a required method of the Edge interface that we must fulfill. +func (e *edge) String() string { + return e.name +} + +// NE is a helper function to make testing easier. It creates a new noop edge. +func NE(s string) Edge { + obj := &edge{s} + return NewEdge(obj) +} + func TestPgraphT1(t *testing.T) { G := &Graph{} @@ -52,7 +68,7 @@ func TestPgraphT1(t *testing.T) { v1 := NV("v1") v2 := NV("v2") - e1 := NewEdge("e1") + e1 := NE("e1") G.AddEdge(v1, v2, e1) if i := G.NumVertices(); i != 2 { @@ -73,12 +89,12 @@ func TestPgraphT2(t *testing.T) { v4 := NV("v4") v5 := NV("v5") v6 := NV("v6") - e1 := NewEdge("e1") - e2 := NewEdge("e2") - e3 := NewEdge("e3") - e4 := NewEdge("e4") - e5 := NewEdge("e5") - //e6 := NewEdge("e6") + e1 := NE("e1") + e2 := NE("e2") + e3 := NE("e3") + e4 := NE("e4") + e5 := NE("e5") + //e6 := NE("e6") G.AddEdge(v1, v2, e1) G.AddEdge(v2, v3, e2) G.AddEdge(v3, v1, e3) @@ -100,12 +116,12 @@ func TestPgraphT3(t *testing.T) { v4 := NV("v4") v5 := NV("v5") v6 := NV("v6") - e1 := NewEdge("e1") - e2 := NewEdge("e2") - e3 := NewEdge("e3") - e4 := NewEdge("e4") - e5 := NewEdge("e5") - //e6 := NewEdge("e6") + e1 := NE("e1") + e2 := NE("e2") + e3 := NE("e3") + e4 := NE("e4") + e5 := NE("e5") + //e6 := NE("e6") G.AddEdge(v1, v2, e1) G.AddEdge(v2, v3, e2) G.AddEdge(v3, v1, e3) @@ -138,9 +154,9 @@ func TestPgraphT4(t *testing.T) { v1 := NV("v1") v2 := NV("v2") v3 := NV("v3") - e1 := NewEdge("e1") - e2 := NewEdge("e2") - e3 := NewEdge("e3") + e1 := NE("e1") + e2 := NE("e2") + e3 := NE("e3") G.AddEdge(v1, v2, e1) G.AddEdge(v2, v3, e2) G.AddEdge(v3, v1, e3) @@ -163,12 +179,12 @@ func TestPgraphT5(t *testing.T) { v4 := NV("v4") v5 := NV("v5") v6 := NV("v6") - e1 := NewEdge("e1") - e2 := NewEdge("e2") - e3 := NewEdge("e3") - e4 := NewEdge("e4") - e5 := NewEdge("e5") - //e6 := NewEdge("e6") + e1 := NE("e1") + e2 := NE("e2") + e3 := NE("e3") + e4 := NE("e4") + e5 := NE("e5") + //e6 := NE("e6") G.AddEdge(v1, v2, e1) G.AddEdge(v2, v3, e2) G.AddEdge(v3, v1, e3) @@ -196,12 +212,12 @@ func TestPgraphT6(t *testing.T) { v4 := NV("v4") v5 := NV("v5") v6 := NV("v6") - e1 := NewEdge("e1") - e2 := NewEdge("e2") - e3 := NewEdge("e3") - e4 := NewEdge("e4") - e5 := NewEdge("e5") - //e6 := NewEdge("e6") + e1 := NE("e1") + e2 := NE("e2") + e3 := NE("e3") + e4 := NE("e4") + e5 := NE("e5") + //e6 := NE("e6") G.AddEdge(v1, v2, e1) G.AddEdge(v2, v3, e2) G.AddEdge(v3, v1, e3) @@ -226,9 +242,9 @@ func TestPgraphT7(t *testing.T) { v1 := NV("v1") v2 := NV("v2") v3 := NV("v3") - e1 := NewEdge("e1") - e2 := NewEdge("e2") - e3 := NewEdge("e3") + e1 := NE("e1") + e2 := NE("e2") + e3 := NE("e3") G.AddEdge(v1, v2, e1) G.AddEdge(v2, v3, e2) G.AddEdge(v3, v1, e3) @@ -300,12 +316,12 @@ func TestPgraphT9(t *testing.T) { v4 := NV("v4") v5 := NV("v5") v6 := NV("v6") - e1 := NewEdge("e1") - e2 := NewEdge("e2") - e3 := NewEdge("e3") - e4 := NewEdge("e4") - e5 := NewEdge("e5") - e6 := NewEdge("e6") + e1 := NE("e1") + e2 := NE("e2") + e3 := NE("e3") + e4 := NE("e4") + e5 := NE("e5") + e6 := NE("e6") G.AddEdge(v1, v2, e1) G.AddEdge(v1, v3, e2) G.AddEdge(v2, v4, e3) @@ -376,12 +392,12 @@ func TestPgraphT10(t *testing.T) { v4 := NV("v4") v5 := NV("v5") v6 := NV("v6") - e1 := NewEdge("e1") - e2 := NewEdge("e2") - e3 := NewEdge("e3") - e4 := NewEdge("e4") - e5 := NewEdge("e5") - e6 := NewEdge("e6") + e1 := NE("e1") + e2 := NE("e2") + e3 := NE("e3") + e4 := NE("e4") + e5 := NE("e5") + e6 := NE("e6") G.AddEdge(v1, v2, e1) G.AddEdge(v2, v3, e2) G.AddEdge(v3, v4, e3) @@ -433,11 +449,11 @@ func TestPgraphReachability0(t *testing.T) { v4 := NV("v4") v5 := NV("v5") v6 := NV("v6") - e1 := NewEdge("e1") - e2 := NewEdge("e2") - e3 := NewEdge("e3") - e4 := NewEdge("e4") - e5 := NewEdge("e5") + e1 := NE("e1") + e2 := NE("e2") + e3 := NE("e3") + e4 := NE("e4") + e5 := NE("e5") G.AddEdge(v1, v2, e1) G.AddEdge(v2, v3, e2) G.AddEdge(v1, v4, e3) @@ -467,12 +483,12 @@ func TestPgraphReachability1(t *testing.T) { v4 := NV("v4") v5 := NV("v5") v6 := NV("v6") - e1 := NewEdge("e1") - e2 := NewEdge("e2") - e3 := NewEdge("e3") - e4 := NewEdge("e4") - e5 := NewEdge("e5") - //e6 := NewEdge("e6") + e1 := NE("e1") + e2 := NE("e2") + e3 := NE("e3") + e4 := NE("e4") + e5 := NE("e5") + //e6 := NE("e6") G.AddEdge(v1, v2, e1) G.AddEdge(v2, v3, e2) G.AddEdge(v3, v4, e3) @@ -501,12 +517,12 @@ func TestPgraphReachability2(t *testing.T) { v4 := NV("v4") v5 := NV("v5") v6 := NV("v6") - e1 := NewEdge("e1") - e2 := NewEdge("e2") - e3 := NewEdge("e3") - e4 := NewEdge("e4") - e5 := NewEdge("e5") - e6 := NewEdge("e6") + e1 := NE("e1") + e2 := NE("e2") + e3 := NE("e3") + e4 := NE("e4") + e5 := NE("e5") + e6 := NE("e6") G.AddEdge(v1, v2, e1) G.AddEdge(v1, v3, e2) G.AddEdge(v2, v4, e3) @@ -538,12 +554,12 @@ func TestPgraphReachability3(t *testing.T) { v4 := NV("v4") v5 := NV("v5") v6 := NV("v6") - e1 := NewEdge("e1") - e2 := NewEdge("e2") - e3 := NewEdge("e3") - e4 := NewEdge("e4") - e5 := NewEdge("e5") - e6 := NewEdge("e6") + e1 := NE("e1") + e2 := NE("e2") + e3 := NE("e3") + e4 := NE("e4") + e5 := NE("e5") + e6 := NE("e6") G.AddEdge(v1, v2, e1) G.AddEdge(v2, v3, e2) G.AddEdge(v3, v4, e3) @@ -573,12 +589,12 @@ func TestPgraphReachability4(t *testing.T) { v4 := NV("v4") v5 := NV("v5") v6 := NV("v6") - e1 := NewEdge("e1") - e2 := NewEdge("e2") - e3 := NewEdge("e3") - e4 := NewEdge("e4") - e5 := NewEdge("e5") - e6 := NewEdge("e6") + e1 := NE("e1") + e2 := NE("e2") + e3 := NE("e3") + e4 := NE("e4") + e5 := NE("e5") + e6 := NE("e6") G.AddEdge(v1, v2, e1) G.AddEdge(v2, v3, e2) G.AddEdge(v3, v4, e3) diff --git a/resources/actions.go b/resources/actions.go index fb483f12..b3856f0e 100644 --- a/resources/actions.go +++ b/resources/actions.go @@ -140,6 +140,7 @@ func (obj *BaseRes) RefreshPending() bool { var refresh bool for _, edge := range obj.Graph.IncomingGraphEdges(obj.Vertex) { // if we asked for a notify *and* if one is pending! + edge := edge.(*Edge) // panic if wrong if edge.Notify && edge.Refresh() { refresh = true break @@ -151,6 +152,7 @@ func (obj *BaseRes) RefreshPending() bool { // SetUpstreamRefresh sets the refresh value to any upstream vertices. func (obj *BaseRes) SetUpstreamRefresh(b bool) { for _, edge := range obj.Graph.IncomingGraphEdges(obj.Vertex) { + edge := edge.(*Edge) // panic if wrong if edge.Notify { edge.SetRefresh(b) } @@ -160,6 +162,7 @@ func (obj *BaseRes) SetUpstreamRefresh(b bool) { // SetDownstreamRefresh sets the refresh value to any downstream vertices. func (obj *BaseRes) SetDownstreamRefresh(b bool) { for _, edge := range obj.Graph.OutgoingGraphEdges(obj.Vertex) { + edge := edge.(*Edge) // panic if wrong // if we asked for a notify *and* if one is pending! if edge.Notify { edge.SetRefresh(b) diff --git a/resources/autoedge.go b/resources/autoedge.go index f68f9099..600be570 100644 --- a/resources/autoedge.go +++ b/resources/autoedge.go @@ -61,11 +61,13 @@ func addEdgesByMatchingUIDS(g *pgraph.Graph, v pgraph.Vertex, uids []ResUID) []b if uid.IsReversed() { txt := fmt.Sprintf("AutoEdge: %s -> %s", VtoR(vv).String(), VtoR(v).String()) log.Printf("Compile: Adding %s", txt) - g.AddEdge(vv, v, pgraph.NewEdge(txt)) + edge := &Edge{Name: txt} + g.AddEdge(vv, v, edge) } else { // edges go the "normal" way, eg: pkg resource txt := fmt.Sprintf("AutoEdge: %s -> %s", VtoR(v).String(), VtoR(vv).String()) log.Printf("Compile: Adding %s", txt) - g.AddEdge(v, vv, pgraph.NewEdge(txt)) + edge := &Edge{Name: txt} + g.AddEdge(v, vv, edge) } found = true break diff --git a/resources/autogroup.go b/resources/autogroup.go index 9d4a43a2..a9abf62b 100644 --- a/resources/autogroup.go +++ b/resources/autogroup.go @@ -35,7 +35,7 @@ type AutoGrouper interface { vertexNext() (pgraph.Vertex, pgraph.Vertex, error) // mostly algorithmic vertexCmp(pgraph.Vertex, pgraph.Vertex) error // can we merge these ? vertexMerge(pgraph.Vertex, pgraph.Vertex) (pgraph.Vertex, error) // vertex merge fn to use - edgeMerge(*pgraph.Edge, *pgraph.Edge) *pgraph.Edge // edge merge fn to use + edgeMerge(pgraph.Edge, pgraph.Edge) pgraph.Edge // edge merge fn to use vertexTest(bool) (bool, error) // call until false } @@ -145,7 +145,7 @@ func (ag *baseGrouper) vertexMerge(v1, v2 pgraph.Vertex) (v pgraph.Vertex, err e return // success or fail, and no need to merge the actual vertices! } -func (ag *baseGrouper) edgeMerge(e1, e2 *pgraph.Edge) *pgraph.Edge { +func (ag *baseGrouper) edgeMerge(e1, e2 pgraph.Edge) pgraph.Edge { return e1 // noop } @@ -204,7 +204,7 @@ func (ag *NonReachabilityGrouper) vertexNext() (v1, v2 pgraph.Vertex, err error) // and then by deleting v2 from the graph. Since more than one edge between two // vertices is not allowed, duplicate edges are merged as well. an edge merge // function can be provided if you'd like to control how you merge the edges! -func VertexMerge(g *pgraph.Graph, v1, v2 pgraph.Vertex, vertexMergeFn func(pgraph.Vertex, pgraph.Vertex) (pgraph.Vertex, error), edgeMergeFn func(*pgraph.Edge, *pgraph.Edge) *pgraph.Edge) error { +func VertexMerge(g *pgraph.Graph, v1, v2 pgraph.Vertex, vertexMergeFn func(pgraph.Vertex, pgraph.Vertex) (pgraph.Vertex, error), edgeMergeFn func(pgraph.Edge, pgraph.Edge) pgraph.Edge) error { // methodology // 1) edges between v1 and v2 are removed //Loop: diff --git a/resources/autogroup_test.go b/resources/autogroup_test.go index a93fa9c1..9e7746d9 100644 --- a/resources/autogroup_test.go +++ b/resources/autogroup_test.go @@ -29,6 +29,12 @@ import ( "github.com/purpleidea/mgmt/util" ) +// NE is a helper function to make testing easier. It creates a new noop edge. +func NE(s string) pgraph.Edge { + obj := &Edge{Name: s} + return obj +} + type testGrouper struct { // TODO: this algorithm may not be correct in all cases. replace if needed! NonReachabilityGrouper // "inherit" what we want, and reimplement the rest @@ -54,14 +60,16 @@ func (ag *testGrouper) vertexMerge(v1, v2 pgraph.Vertex) (v pgraph.Vertex, err e return // success or fail, and no need to merge the actual vertices! } -func (ag *testGrouper) edgeMerge(e1, e2 *pgraph.Edge) *pgraph.Edge { +func (ag *testGrouper) edgeMerge(e1, e2 pgraph.Edge) pgraph.Edge { + edge1 := e1.(*Edge) // panic if wrong + edge2 := e2.(*Edge) // panic if wrong // HACK: update the name so it makes a union of both names - n1 := strings.Split(e1.Name, ",") // load - n2 := strings.Split(e2.Name, ",") // load + n1 := strings.Split(edge1.Name, ",") // load + n2 := strings.Split(edge2.Name, ",") // load names := append(n1, n2...) names = util.StrRemoveDuplicatesInList(names) // remove duplicates sort.Strings(names) - return pgraph.NewEdge(strings.Join(names, ",")) + return &Edge{Name: strings.Join(names, ",")} } // helper function @@ -161,7 +169,8 @@ Loop: for vv1, ee1 := range g1.Adjacency()[v1] { vv2 := m[vv1] - ee2 := g2.Adjacency()[v2][vv2] + ee1 := ee1.(*Edge) + ee2 := g2.Adjacency()[v2][vv2].(*Edge) // these are edges from v1 -> vv1 via ee1 (graph 1) // to cmp to edges from v2 -> vv2 via ee2 (graph 2) @@ -240,7 +249,8 @@ func fullPrint(g *pgraph.Graph) (str string) { } for v1 := range g.Adjacency() { for v2, e := range g.Adjacency()[v1] { - str += fmt.Sprintf("* e: %v -> %v # %v\n", VtoR(v1).GetName(), VtoR(v2).GetName(), e.Name) + edge := e.(*Edge) + str += fmt.Sprintf("* e: %v -> %v # %v\n", VtoR(v1).GetName(), VtoR(v2).GetName(), edge.Name) } } return @@ -460,8 +470,8 @@ func TestPgraphGrouping12(t *testing.T) { a1 := pgraph.NewVertex(NewNoopResTest("a1")) a2 := pgraph.NewVertex(NewNoopResTest("a2")) b1 := pgraph.NewVertex(NewNoopResTest("b1")) - e1 := pgraph.NewEdge("e1") - e2 := pgraph.NewEdge("e2") + e1 := NE("e1") + e2 := NE("e2") g1.AddEdge(a1, b1, e1) g1.AddEdge(a2, b1, e2) } @@ -469,7 +479,7 @@ func TestPgraphGrouping12(t *testing.T) { { a := pgraph.NewVertex(NewNoopResTest("a1,a2")) b1 := pgraph.NewVertex(NewNoopResTest("b1")) - e := pgraph.NewEdge("e1,e2") + e := NE("e1,e2") g2.AddEdge(a, b1, e) } runGraphCmp(t, g1, g2) @@ -485,8 +495,8 @@ func TestPgraphGrouping13(t *testing.T) { a1 := pgraph.NewVertex(NewNoopResTest("a1")) a2 := pgraph.NewVertex(NewNoopResTest("a2")) b1 := pgraph.NewVertex(NewNoopResTest("b1")) - e1 := pgraph.NewEdge("e1") - e2 := pgraph.NewEdge("e2") + e1 := NE("e1") + e2 := NE("e2") g1.AddEdge(b1, a1, e1) g1.AddEdge(b1, a2, e2) } @@ -494,7 +504,7 @@ func TestPgraphGrouping13(t *testing.T) { { a := pgraph.NewVertex(NewNoopResTest("a1,a2")) b1 := pgraph.NewVertex(NewNoopResTest("b1")) - e := pgraph.NewEdge("e1,e2") + e := NE("e1,e2") g2.AddEdge(b1, a, e) } runGraphCmp(t, g1, g2) @@ -511,9 +521,9 @@ func TestPgraphGrouping14(t *testing.T) { a2 := pgraph.NewVertex(NewNoopResTest("a2")) a3 := pgraph.NewVertex(NewNoopResTest("a3")) b1 := pgraph.NewVertex(NewNoopResTest("b1")) - e1 := pgraph.NewEdge("e1") - e2 := pgraph.NewEdge("e2") - e3 := pgraph.NewEdge("e3") + e1 := NE("e1") + e2 := NE("e2") + e3 := NE("e3") g1.AddEdge(a1, b1, e1) g1.AddEdge(a2, b1, e2) g1.AddEdge(a3, b1, e3) @@ -522,7 +532,7 @@ func TestPgraphGrouping14(t *testing.T) { { a := pgraph.NewVertex(NewNoopResTest("a1,a2,a3")) b1 := pgraph.NewVertex(NewNoopResTest("b1")) - e := pgraph.NewEdge("e1,e2,e3") + e := NE("e1,e2,e3") g2.AddEdge(a, b1, e) } runGraphCmp(t, g1, g2) @@ -541,10 +551,10 @@ func TestPgraphGrouping15(t *testing.T) { b1 := pgraph.NewVertex(NewNoopResTest("b1")) b2 := pgraph.NewVertex(NewNoopResTest("b2")) c1 := pgraph.NewVertex(NewNoopResTest("c1")) - e1 := pgraph.NewEdge("e1") - e2 := pgraph.NewEdge("e2") - e3 := pgraph.NewEdge("e3") - e4 := pgraph.NewEdge("e4") + e1 := NE("e1") + e2 := NE("e2") + e3 := NE("e3") + e4 := NE("e4") g1.AddEdge(a1, b1, e1) g1.AddEdge(a1, b2, e2) g1.AddEdge(b1, c1, e3) @@ -555,8 +565,8 @@ func TestPgraphGrouping15(t *testing.T) { a1 := pgraph.NewVertex(NewNoopResTest("a1")) b := pgraph.NewVertex(NewNoopResTest("b1,b2")) c1 := pgraph.NewVertex(NewNoopResTest("c1")) - e1 := pgraph.NewEdge("e1,e2") - e2 := pgraph.NewEdge("e3,e4") + e1 := NE("e1,e2") + e2 := NE("e3,e4") g2.AddEdge(a1, b, e1) g2.AddEdge(b, c1, e2) } @@ -578,9 +588,9 @@ func TestPgraphGrouping16(t *testing.T) { a2 := pgraph.NewVertex(NewNoopResTest("a2")) b1 := pgraph.NewVertex(NewNoopResTest("b1")) c1 := pgraph.NewVertex(NewNoopResTest("c1")) - e1 := pgraph.NewEdge("e1") - e2 := pgraph.NewEdge("e2") - e3 := pgraph.NewEdge("e3") + e1 := NE("e1") + e2 := NE("e2") + e3 := NE("e3") g1.AddEdge(a1, b1, e1) g1.AddEdge(b1, c1, e2) g1.AddEdge(a2, c1, e3) @@ -590,8 +600,8 @@ func TestPgraphGrouping16(t *testing.T) { a := pgraph.NewVertex(NewNoopResTest("a1,a2")) b1 := pgraph.NewVertex(NewNoopResTest("b1")) c1 := pgraph.NewVertex(NewNoopResTest("c1")) - e1 := pgraph.NewEdge("e1,e3") - e2 := pgraph.NewEdge("e2,e3") // e3 gets "merged through" to BOTH edges! + e1 := NE("e1,e3") + e2 := NE("e2,e3") // e3 gets "merged through" to BOTH edges! g2.AddEdge(a, b1, e1) g2.AddEdge(b1, c1, e2) } @@ -611,9 +621,9 @@ func TestPgraphGrouping17(t *testing.T) { b1 := pgraph.NewVertex(NewNoopResTest("b1")) b2 := pgraph.NewVertex(NewNoopResTest("b2")) c1 := pgraph.NewVertex(NewNoopResTest("c1")) - e1 := pgraph.NewEdge("e1") - e2 := pgraph.NewEdge("e2") - e3 := pgraph.NewEdge("e3") + e1 := NE("e1") + e2 := NE("e2") + e3 := NE("e3") g1.AddEdge(a1, b1, e1) g1.AddEdge(b1, c1, e2) g1.AddEdge(b2, c1, e3) @@ -623,8 +633,8 @@ func TestPgraphGrouping17(t *testing.T) { a1 := pgraph.NewVertex(NewNoopResTest("a1")) b := pgraph.NewVertex(NewNoopResTest("b1,b2")) c1 := pgraph.NewVertex(NewNoopResTest("c1")) - e1 := pgraph.NewEdge("e1") - e2 := pgraph.NewEdge("e2,e3") + e1 := NE("e1") + e2 := NE("e2,e3") g2.AddEdge(a1, b, e1) g2.AddEdge(b, c1, e2) } @@ -646,10 +656,10 @@ func TestPgraphGrouping18(t *testing.T) { b1 := pgraph.NewVertex(NewNoopResTest("b1")) b2 := pgraph.NewVertex(NewNoopResTest("b2")) c1 := pgraph.NewVertex(NewNoopResTest("c1")) - e1 := pgraph.NewEdge("e1") - e2 := pgraph.NewEdge("e2") - e3 := pgraph.NewEdge("e3") - e4 := pgraph.NewEdge("e4") + e1 := NE("e1") + e2 := NE("e2") + e3 := NE("e3") + e4 := NE("e4") g1.AddEdge(a1, b1, e1) g1.AddEdge(b1, c1, e2) g1.AddEdge(a2, c1, e3) @@ -660,8 +670,8 @@ func TestPgraphGrouping18(t *testing.T) { a := pgraph.NewVertex(NewNoopResTest("a1,a2")) b := pgraph.NewVertex(NewNoopResTest("b1,b2")) c1 := pgraph.NewVertex(NewNoopResTest("c1")) - e1 := pgraph.NewEdge("e1,e3") - e2 := pgraph.NewEdge("e2,e3,e4") // e3 gets "merged through" to BOTH edges! + e1 := NE("e1,e3") + e2 := NE("e2,e3,e4") // e3 gets "merged through" to BOTH edges! g2.AddEdge(a, b, e1) g2.AddEdge(b, c1, e2) } @@ -677,14 +687,14 @@ func TestPgraphGroupingConnected0(t *testing.T) { { a1 := pgraph.NewVertex(NewNoopResTest("a1")) a2 := pgraph.NewVertex(NewNoopResTest("a2")) - e1 := pgraph.NewEdge("e1") + e1 := NE("e1") g1.AddEdge(a1, a2, e1) } g2, _ := pgraph.NewGraph("g2") // expected result ? { a1 := pgraph.NewVertex(NewNoopResTest("a1")) a2 := pgraph.NewVertex(NewNoopResTest("a2")) - e1 := pgraph.NewEdge("e1") + e1 := NE("e1") g2.AddEdge(a1, a2, e1) } runGraphCmp(t, g1, g2) @@ -702,8 +712,8 @@ func TestPgraphGroupingConnected1(t *testing.T) { a1 := pgraph.NewVertex(NewNoopResTest("a1")) b := pgraph.NewVertex(NewNoopResTest("b")) a2 := pgraph.NewVertex(NewNoopResTest("a2")) - e1 := pgraph.NewEdge("e1") - e2 := pgraph.NewEdge("e2") + e1 := NE("e1") + e2 := NE("e2") g1.AddEdge(a1, b, e1) g1.AddEdge(b, a2, e2) } @@ -712,8 +722,8 @@ func TestPgraphGroupingConnected1(t *testing.T) { a1 := pgraph.NewVertex(NewNoopResTest("a1")) b := pgraph.NewVertex(NewNoopResTest("b")) a2 := pgraph.NewVertex(NewNoopResTest("a2")) - e1 := pgraph.NewEdge("e1") - e2 := pgraph.NewEdge("e2") + e1 := NE("e1") + e2 := NE("e2") g2.AddEdge(a1, b, e1) g2.AddEdge(b, a2, e2) } diff --git a/resources/edge.go b/resources/edge.go new file mode 100644 index 00000000..b7207b76 --- /dev/null +++ b/resources/edge.go @@ -0,0 +1,41 @@ +// Mgmt +// Copyright (C) 2013-2017+ James Shubin and the project contributors +// Written by James Shubin and the project contributors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package resources + +// Edge is a struct that represents a graph's edge. +type Edge struct { + Name string + Notify bool // should we send a refresh notification along this edge? + + refresh bool // is there a notify pending for the dest vertex ? +} + +// String is a required method of the Edge interface that we must fulfill. +func (obj *Edge) String() string { + return obj.Name +} + +// Refresh returns the pending refresh status of this edge. +func (obj *Edge) Refresh() bool { + return obj.refresh +} + +// SetRefresh sets the pending refresh status of this edge. +func (obj *Edge) SetRefresh(b bool) { + obj.refresh = b +} diff --git a/yamlgraph/gconfig.go b/yamlgraph/gconfig.go index 50c546ed..4a05f15c 100644 --- a/yamlgraph/gconfig.go +++ b/yamlgraph/gconfig.go @@ -247,8 +247,10 @@ func (c *GraphConfig) NewGraphFromConfig(hostname string, world resources.World, } from := lookup[strings.ToLower(e.From.Kind)][e.From.Name] to := lookup[strings.ToLower(e.To.Kind)][e.To.Name] - edge := pgraph.NewEdge(e.Name) - edge.Notify = e.Notify + edge := &resources.Edge{ + Name: e.Name, + Notify: e.Notify, + } graph.AddEdge(from, to, edge) } diff --git a/yamlgraph2/gconfig.go b/yamlgraph2/gconfig.go index b4b2bc94..bf7d8f69 100644 --- a/yamlgraph2/gconfig.go +++ b/yamlgraph2/gconfig.go @@ -293,8 +293,10 @@ func (c *GraphConfig) NewGraphFromConfig(hostname string, world resources.World, } from := lookup[strings.ToLower(e.From.Kind)][e.From.Name] to := lookup[strings.ToLower(e.To.Kind)][e.To.Name] - edge := pgraph.NewEdge(e.Name) - edge.Notify = e.Notify + edge := &resources.Edge{ + Name: e.Name, + Notify: e.Notify, + } graph.AddEdge(from, to, edge) }