func TestExpandTransform(t *testing.T) { var g Graph g.Add(1) g.Add(2) g.Connect(dag.BasicEdge(1, 2)) tf := &ExpandTransform{} out, err := tf.Transform(&testExpandable{ Result: &g, }) if err != nil { t.Fatalf("err: %s", err) } sn, ok := out.(GraphNodeSubgraph) if !ok { t.Fatalf("not subgraph: %#v", out) } actual := strings.TrimSpace(sn.Subgraph().String()) expected := strings.TrimSpace(testExpandTransformStr) if actual != expected { t.Fatalf("bad: %s", actual) } }
func (t *ProxyTransformer) Transform(g *Graph) error { for _, v := range g.Vertices() { pn, ok := v.(GraphNodeProxy) if !ok { continue } // If we don't want to be proxies, don't do it if !pn.Proxy() { continue } // Connect all the things that depend on this to things that // we depend on as the proxy. See docs for GraphNodeProxy for // a visual explanation. for _, s := range g.UpEdges(v).List() { for _, t := range g.DownEdges(v).List() { g.Connect(GraphProxyEdge{ Edge: dag.BasicEdge(s, t), }) } } } return nil }
func TestModuleInputTransformer(t *testing.T) { var g Graph g.Add(1) g.Add(2) g.Add(3) g.Connect(dag.BasicEdge(1, 2)) g.Connect(dag.BasicEdge(1, 3)) { tf := &ModuleInputTransformer{} if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) } } actual := strings.TrimSpace(g.String()) expected := strings.TrimSpace(testModuleInputTransformStr) if actual != expected { t.Fatalf("bad:\n\n%s", actual) } }
func (t *ProviderTransformer) Transform(g *Graph) error { // Go through the other nodes and match them to providers they need var err error m := providerVertexMap(g) for _, v := range g.Vertices() { if pv, ok := v.(GraphNodeProviderConsumer); ok { for _, p := range pv.ProvidedBy() { target := m[p] if target == nil { err = multierror.Append(err, fmt.Errorf( "%s: provider %s couldn't be found", dag.VertexName(v), p)) continue } g.Connect(dag.BasicEdge(v, target)) } } } return err }
func (t *RootTransformer) Transform(g *Graph) error { // If we already have a good root, we're done if _, err := g.Root(); err == nil { return nil } // Add a root var root graphNodeRoot g.Add(root) // Connect the root to all the edges that need it for _, v := range g.Vertices() { if v == root { continue } if g.UpEdges(v).Len() == 0 { g.Connect(dag.BasicEdge(root, v)) } } return nil }
func (t *DestroyTransformer) transform( g *Graph, mode GraphNodeDestroyMode) ([]dag.Edge, []dag.Edge, error) { var connect, remove []dag.Edge nodeToCn := make(map[dag.Vertex]dag.Vertex, len(g.Vertices())) nodeToDn := make(map[dag.Vertex]dag.Vertex, len(g.Vertices())) for _, v := range g.Vertices() { // If it is not a destroyable, we don't care cn, ok := v.(GraphNodeDestroyable) if !ok { continue } // Grab the destroy side of the node and connect it through n := cn.DestroyNode(mode) if n == nil { continue } // Store it nodeToCn[n] = cn nodeToDn[cn] = n // Add it to the graph g.Add(n) // Inherit all the edges from the old node downEdges := g.DownEdges(v).List() for _, edgeRaw := range downEdges { // If this thing specifically requests to not be depended on // by destroy nodes, then don't. if i, ok := edgeRaw.(GraphNodeDestroyEdgeInclude); ok && !i.DestroyEdgeInclude(v) { continue } g.Connect(dag.BasicEdge(n, edgeRaw.(dag.Vertex))) } // Add a new edge to connect the node to be created to // the destroy node. connect = append(connect, dag.BasicEdge(v, n)) } // Go through the nodes we added and determine if they depend // on any nodes with a destroy node. If so, depend on that instead. for n, _ := range nodeToCn { for _, downRaw := range g.DownEdges(n).List() { target := downRaw.(dag.Vertex) cn2, ok := target.(GraphNodeDestroyable) if !ok { continue } newTarget := nodeToDn[cn2] if newTarget == nil { continue } // Make the new edge and transpose connect = append(connect, dag.BasicEdge(newTarget, n)) // Remove the old edge remove = append(remove, dag.BasicEdge(n, target)) } } return connect, remove, nil }
func (t *CreateBeforeDestroyTransformer) Transform(g *Graph) error { // We "stage" the edge connections/destroys in these slices so that // while we're doing the edge transformations (transpositions) in // the graph, we're not affecting future edge transpositions. These // slices let us stage ALL the changes that WILL happen so that all // of the transformations happen atomically. var connect, destroy []dag.Edge for _, v := range g.Vertices() { // We only care to use the destroy nodes dn, ok := v.(GraphNodeDestroy) if !ok { continue } // If the node doesn't need to create before destroy, then continue if !dn.CreateBeforeDestroy() { continue } // Get the creation side of this node cn := dn.CreateNode() // Take all the things which depend on the creation node and // make them dependencies on the destruction. Clarifying this // with an example: if you have a web server and a load balancer // and the load balancer depends on the web server, then when we // do a create before destroy, we want to make sure the steps are: // // 1.) Create new web server // 2.) Update load balancer // 3.) Delete old web server // // This ensures that. for _, sourceRaw := range g.UpEdges(cn).List() { source := sourceRaw.(dag.Vertex) // If the graph has a "root" node (one added by a RootTransformer and not // just a resource that happens to have no ancestors), we don't want to // add any edges to it, because then it ceases to be a root. if _, ok := source.(graphNodeRoot); ok { continue } connect = append(connect, dag.BasicEdge(dn, source)) } // Swap the edge so that the destroy depends on the creation // happening... connect = append(connect, dag.BasicEdge(dn, cn)) destroy = append(destroy, dag.BasicEdge(cn, dn)) } for _, edge := range connect { g.Connect(edge) } for _, edge := range destroy { g.RemoveEdge(edge) } return nil }