terraform: GraphNodeDependent

This commit is contained in:
Mitchell Hashimoto 2015-01-27 14:56:01 -08:00
parent 8dc4c56b2e
commit 8bf725e746
5 changed files with 102 additions and 44 deletions

View File

@ -51,15 +51,21 @@ func (g *Graph) Add(v dag.Vertex) dag.Vertex {
return v return v
} }
// ConnectTo is a helper to create edges between a node and a list of // ConnectDependent connects a GraphNodeDependent to all of its
// targets by their DependableNames. // GraphNodeDependables. It returns the list of dependents it was
func (g *Graph) ConnectTo(source dag.Vertex, target []string) []string { // unable to connect to.
func (g *Graph) ConnectDependent(raw dag.Vertex) []string {
g.once.Do(g.init) g.once.Do(g.init)
v, ok := raw.(GraphNodeDependent)
if !ok {
return nil
}
var missing []string var missing []string
for _, t := range target { for _, t := range v.DependentOn() {
if dest := g.dependableMap[t]; dest != nil { if dest := g.dependableMap[t]; dest != nil {
g.Connect(dag.BasicEdge(source, dest)) g.Connect(dag.BasicEdge(v, dest))
} else { } else {
missing = append(missing, t) missing = append(missing, t)
} }
@ -68,6 +74,20 @@ func (g *Graph) ConnectTo(source dag.Vertex, target []string) []string {
return missing return missing
} }
// ConnectDependents goes through the graph, connecting all the
// GraphNodeDependents to GraphNodeDependables. This is safe to call
// multiple times.
//
// To get details on whether dependencies could be found/made, the more
// specific ConnectDependent should be used.
func (g *Graph) ConnectDependents() {
for _, v := range g.Vertices() {
if dv, ok := v.(GraphNodeDependent); ok {
g.ConnectDependent(dv)
}
}
}
func (g *Graph) init() { func (g *Graph) init() {
if g.Graph == nil { if g.Graph == nil {
g.Graph = new(dag.Graph) g.Graph = new(dag.Graph)
@ -86,3 +106,11 @@ func (g *Graph) init() {
type GraphNodeDependable interface { type GraphNodeDependable interface {
DependableName() []string DependableName() []string
} }
// GraphNodeDependent is an interface which says that a node depends
// on another GraphNodeDependable by some name. By implementing this
// interface, Graph.ConnectDependents() can be called multiple times
// safely and efficiently.
type GraphNodeDependent interface {
DependentOn() []string
}

View File

@ -14,10 +14,10 @@ import (
type graphNodeConfig interface { type graphNodeConfig interface {
dag.NamedVertex dag.NamedVertex
// Variables returns the full list of variables that this node // All graph nodes should be dependent on other things, and able to
// depends on. The values within the slice should map to the VarName() // be depended on.
// values that are returned by any nodes. GraphNodeDependable
Variables() []string GraphNodeDependent
} }
// GraphNodeConfigModule represents a module within the configuration graph. // GraphNodeConfigModule represents a module within the configuration graph.
@ -29,20 +29,23 @@ type GraphNodeConfigModule struct {
func (n *GraphNodeConfigModule) DependableName() []string { func (n *GraphNodeConfigModule) DependableName() []string {
return []string{n.Name()} return []string{n.Name()}
} }
func (n *GraphNodeConfigModule) Name() string {
return fmt.Sprintf("module.%s", n.Module.Name)
}
func (n *GraphNodeConfigModule) Variables() []string { func (n *GraphNodeConfigModule) DependentOn() []string {
vars := n.Module.RawConfig.Variables vars := n.Module.RawConfig.Variables
result := make([]string, 0, len(vars)) result := make([]string, 0, len(vars))
for _, v := range vars { for _, v := range vars {
result = append(result, varNameForVar(v)) if vn := varNameForVar(v); vn != "" {
result = append(result, vn)
}
} }
return result return result
} }
func (n *GraphNodeConfigModule) Name() string {
return fmt.Sprintf("module.%s", n.Module.Name)
}
// GraphNodeConfigProvider represents a configured provider within the // GraphNodeConfigProvider represents a configured provider within the
// configuration graph. These are only immediately in the graph when an // configuration graph. These are only immediately in the graph when an
// explicit `provider` configuration block is in the configuration. // explicit `provider` configuration block is in the configuration.
@ -54,11 +57,17 @@ func (n *GraphNodeConfigProvider) Name() string {
return fmt.Sprintf("provider.%s", n.Provider.Name) return fmt.Sprintf("provider.%s", n.Provider.Name)
} }
func (n *GraphNodeConfigProvider) Variables() []string { func (n *GraphNodeConfigProvider) DependableName() []string {
return []string{n.Name()}
}
func (n *GraphNodeConfigProvider) DependentOn() []string {
vars := n.Provider.RawConfig.Variables vars := n.Provider.RawConfig.Variables
result := make([]string, 0, len(vars)) result := make([]string, 0, len(vars))
for _, v := range vars { for _, v := range vars {
result = append(result, varNameForVar(v)) if vn := varNameForVar(v); vn != "" {
result = append(result, vn)
}
} }
return result return result
@ -73,22 +82,26 @@ func (n *GraphNodeConfigResource) DependableName() []string {
return []string{n.Resource.Id()} return []string{n.Resource.Id()}
} }
func (n *GraphNodeConfigResource) Name() string { func (n *GraphNodeConfigResource) DependentOn() []string {
return n.Resource.Id()
}
func (n *GraphNodeConfigResource) Variables() []string {
result := make([]string, len(n.Resource.DependsOn), result := make([]string, len(n.Resource.DependsOn),
len(n.Resource.RawCount.Variables)+ len(n.Resource.RawCount.Variables)+
len(n.Resource.RawConfig.Variables)+ len(n.Resource.RawConfig.Variables)+
len(n.Resource.DependsOn)) len(n.Resource.DependsOn))
copy(result, n.Resource.DependsOn) copy(result, n.Resource.DependsOn)
for _, v := range n.Resource.RawCount.Variables { for _, v := range n.Resource.RawCount.Variables {
result = append(result, varNameForVar(v)) if vn := varNameForVar(v); vn != "" {
result = append(result, vn)
}
} }
for _, v := range n.Resource.RawConfig.Variables { for _, v := range n.Resource.RawConfig.Variables {
result = append(result, varNameForVar(v)) if vn := varNameForVar(v); vn != "" {
result = append(result, vn)
}
} }
return result return result
} }
func (n *GraphNodeConfigResource) Name() string {
return n.Resource.Id()
}

View File

@ -18,12 +18,15 @@ func TestGraphAdd(t *testing.T) {
} }
} }
func TestGraphConnectTo(t *testing.T) { func TestGraphConnectDependent(t *testing.T) {
var g Graph var g Graph
g.Add(&testGraphDependable{VertexName: "a", Mock: []string{"a"}}) g.Add(&testGraphDependable{VertexName: "a", Mock: []string{"a"}})
b := g.Add(&testGraphDependable{VertexName: "b"}) b := g.Add(&testGraphDependable{
VertexName: "b",
DependentOnMock: []string{"a"},
})
if missing := g.ConnectTo(b, []string{"a"}); len(missing) > 0 { if missing := g.ConnectDependent(b); len(missing) > 0 {
t.Fatalf("bad: %#v", missing) t.Fatalf("bad: %#v", missing)
} }
@ -36,6 +39,7 @@ func TestGraphConnectTo(t *testing.T) {
type testGraphDependable struct { type testGraphDependable struct {
VertexName string VertexName string
DependentOnMock []string
Mock []string Mock []string
} }
@ -47,6 +51,10 @@ func (v *testGraphDependable) DependableName() []string {
return v.Mock return v.Mock
} }
func (v *testGraphDependable) DependentOn() []string {
return v.DependentOnMock
}
const testGraphAddStr = ` const testGraphAddStr = `
42 42
84 84

View File

@ -63,14 +63,7 @@ func (t *ConfigTransformer) Transform(g *Graph) error {
// Build up the dependencies. We have to do this outside of the above // Build up the dependencies. We have to do this outside of the above
// loop since the nodes need to be in place for us to build the deps. // loop since the nodes need to be in place for us to build the deps.
for _, n := range nodes { for _, n := range nodes {
vars := n.Variables() if missing := g.ConnectDependent(n); len(missing) > 0 {
targets := make([]string, 0, len(vars))
for _, t := range vars {
if t != "" {
targets = append(targets, t)
}
}
if missing := g.ConnectTo(n, targets); len(missing) > 0 {
for _, m := range missing { for _, m := range missing {
err = multierror.Append(err, fmt.Errorf( err = multierror.Append(err, fmt.Errorf(
"%s: missing dependency: %s", n.Name(), m)) "%s: missing dependency: %s", n.Name(), m))

View File

@ -29,17 +29,21 @@ func (t *OrphanTransformer) Transform(g *Graph) error {
resourceOrphans := state.Orphans(t.Config) resourceOrphans := state.Orphans(t.Config)
resourceVertexes := make([]dag.Vertex, len(resourceOrphans)) resourceVertexes := make([]dag.Vertex, len(resourceOrphans))
for i, k := range resourceOrphans { for i, k := range resourceOrphans {
resourceVertexes[i] = g.Add(&graphNodeOrphanResource{ResourceName: k}) resourceVertexes[i] = g.Add(&graphNodeOrphanResource{
ResourceName: k,
dependentOn: state.Resources[k].Dependencies,
})
} }
// Go over each module orphan and add it to the graph. We store the // Go over each module orphan and add it to the graph. We store the
// vertexes and states outside so that we can connect dependencies later. // vertexes and states outside so that we can connect dependencies later.
moduleOrphans := t.State.ModuleOrphans(g.Path, t.Config) moduleOrphans := t.State.ModuleOrphans(g.Path, t.Config)
moduleVertexes := make([]dag.Vertex, len(moduleOrphans)) moduleVertexes := make([]dag.Vertex, len(moduleOrphans))
moduleStates := make([]*ModuleState, len(moduleVertexes))
for i, path := range moduleOrphans { for i, path := range moduleOrphans {
moduleVertexes[i] = g.Add(&graphNodeOrphanModule{Path: path}) moduleVertexes[i] = g.Add(&graphNodeOrphanModule{
moduleStates[i] = t.State.ModuleByPath(path) Path: path,
dependentOn: t.State.ModuleByPath(path).Dependencies,
})
} }
// Now do the dependencies. We do this _after_ adding all the orphan // Now do the dependencies. We do this _after_ adding all the orphan
@ -47,13 +51,13 @@ func (t *OrphanTransformer) Transform(g *Graph) error {
// depend on other orphans. // depend on other orphans.
// Resource dependencies // Resource dependencies
for i, v := range resourceVertexes { for _, v := range resourceVertexes {
g.ConnectTo(v, state.Resources[resourceOrphans[i]].Dependencies) g.ConnectDependent(v)
} }
// Module dependencies // Module dependencies
for i, v := range moduleVertexes { for _, v := range moduleVertexes {
g.ConnectTo(v, moduleStates[i].Dependencies) g.ConnectDependent(v)
} }
return nil return nil
@ -62,12 +66,18 @@ func (t *OrphanTransformer) Transform(g *Graph) error {
// graphNodeOrphanModule is the graph vertex representing an orphan resource.. // graphNodeOrphanModule is the graph vertex representing an orphan resource..
type graphNodeOrphanModule struct { type graphNodeOrphanModule struct {
Path []string Path []string
dependentOn []string
} }
func (n *graphNodeOrphanModule) DependableName() []string { func (n *graphNodeOrphanModule) DependableName() []string {
return []string{n.dependableName()} return []string{n.dependableName()}
} }
func (n *graphNodeOrphanModule) DependentOn() []string {
return n.dependentOn
}
func (n *graphNodeOrphanModule) Name() string { func (n *graphNodeOrphanModule) Name() string {
return fmt.Sprintf("%s (orphan)", n.dependableName()) return fmt.Sprintf("%s (orphan)", n.dependableName())
} }
@ -79,12 +89,18 @@ func (n *graphNodeOrphanModule) dependableName() string {
// graphNodeOrphanResource is the graph vertex representing an orphan resource.. // graphNodeOrphanResource is the graph vertex representing an orphan resource..
type graphNodeOrphanResource struct { type graphNodeOrphanResource struct {
ResourceName string ResourceName string
dependentOn []string
} }
func (n *graphNodeOrphanResource) DependableName() []string { func (n *graphNodeOrphanResource) DependableName() []string {
return []string{n.dependableName()} return []string{n.dependableName()}
} }
func (n *graphNodeOrphanResource) DependentOn() []string {
return n.dependentOn
}
func (n *graphNodeOrphanResource) Name() string { func (n *graphNodeOrphanResource) Name() string {
return fmt.Sprintf("%s (orphan)", n.ResourceName) return fmt.Sprintf("%s (orphan)", n.ResourceName)
} }