github.com/jsoriano/terraform@v0.6.7-0.20151026070445-8b70867fdd95/depgraph/graph.go (about)

     1  // The depgraph package is used to create and model a dependency graph
     2  // of nouns. Each noun can represent a service, server, application,
     3  // network switch, etc. Nouns can depend on other nouns, and provide
     4  // versioning constraints. Nouns can also have various meta data that
     5  // may be relevant to their construction or configuration.
     6  package depgraph
     7  
     8  import (
     9  	"bytes"
    10  	"fmt"
    11  	"sort"
    12  	"strings"
    13  	"sync"
    14  
    15  	"github.com/hashicorp/terraform/digraph"
    16  )
    17  
    18  // WalkFunc is the type used for the callback for Walk.
    19  type WalkFunc func(*Noun) error
    20  
    21  // Graph is used to represent a dependency graph.
    22  type Graph struct {
    23  	Name  string
    24  	Meta  interface{}
    25  	Nouns []*Noun
    26  	Root  *Noun
    27  }
    28  
    29  // ValidateError implements the Error interface but provides
    30  // additional information on a validation error.
    31  type ValidateError struct {
    32  	// If set, then the graph is missing a single root, on which
    33  	// there are no depdendencies
    34  	MissingRoot bool
    35  
    36  	// Unreachable are nodes that could not be reached from
    37  	// the root noun.
    38  	Unreachable []*Noun
    39  
    40  	// Cycles are groups of strongly connected nodes, which
    41  	// form a cycle. This is disallowed.
    42  	Cycles [][]*Noun
    43  }
    44  
    45  func (v *ValidateError) Error() string {
    46  	var msgs []string
    47  
    48  	if v.MissingRoot {
    49  		msgs = append(msgs, "The graph has no single root")
    50  	}
    51  
    52  	for _, n := range v.Unreachable {
    53  		msgs = append(msgs, fmt.Sprintf(
    54  			"Unreachable node: %s", n.Name))
    55  	}
    56  
    57  	for _, c := range v.Cycles {
    58  		cycleNodes := make([]string, len(c))
    59  		for i, n := range c {
    60  			cycleNodes[i] = n.Name
    61  		}
    62  
    63  		msgs = append(msgs, fmt.Sprintf(
    64  			"Cycle: %s", strings.Join(cycleNodes, " -> ")))
    65  	}
    66  
    67  	for i, m := range msgs {
    68  		msgs[i] = fmt.Sprintf("* %s", m)
    69  	}
    70  
    71  	return fmt.Sprintf(
    72  		"The dependency graph is not valid:\n\n%s",
    73  		strings.Join(msgs, "\n"))
    74  }
    75  
    76  // ConstraintError is used to return detailed violation
    77  // information from CheckConstraints
    78  type ConstraintError struct {
    79  	Violations []*Violation
    80  }
    81  
    82  func (c *ConstraintError) Error() string {
    83  	return fmt.Sprintf("%d constraint violations", len(c.Violations))
    84  }
    85  
    86  // Violation is used to pass along information about
    87  // a constraint violation
    88  type Violation struct {
    89  	Source     *Noun
    90  	Target     *Noun
    91  	Dependency *Dependency
    92  	Constraint Constraint
    93  	Err        error
    94  }
    95  
    96  func (v *Violation) Error() string {
    97  	return fmt.Sprintf("Constraint %v between %v and %v violated: %v",
    98  		v.Constraint, v.Source, v.Target, v.Err)
    99  }
   100  
   101  // CheckConstraints walks the graph and ensures that all
   102  // user imposed constraints are satisfied.
   103  func (g *Graph) CheckConstraints() error {
   104  	// Ensure we have a root
   105  	if g.Root == nil {
   106  		return fmt.Errorf("Graph must be validated before checking constraint violations")
   107  	}
   108  
   109  	// Create a constraint error
   110  	cErr := &ConstraintError{}
   111  
   112  	// Walk from the root
   113  	digraph.DepthFirstWalk(g.Root, func(n digraph.Node) bool {
   114  		noun := n.(*Noun)
   115  		for _, dep := range noun.Deps {
   116  			target := dep.Target
   117  			for _, constraint := range dep.Constraints {
   118  				ok, err := constraint.Satisfied(noun, target)
   119  				if ok {
   120  					continue
   121  				}
   122  				violation := &Violation{
   123  					Source:     noun,
   124  					Target:     target,
   125  					Dependency: dep,
   126  					Constraint: constraint,
   127  					Err:        err,
   128  				}
   129  				cErr.Violations = append(cErr.Violations, violation)
   130  			}
   131  		}
   132  		return true
   133  	})
   134  
   135  	if cErr.Violations != nil {
   136  		return cErr
   137  	}
   138  	return nil
   139  }
   140  
   141  // Noun returns the noun with the given name, or nil if it cannot be found.
   142  func (g *Graph) Noun(name string) *Noun {
   143  	for _, n := range g.Nouns {
   144  		if n.Name == name {
   145  			return n
   146  		}
   147  	}
   148  
   149  	return nil
   150  }
   151  
   152  // String generates a little ASCII string of the graph, useful in
   153  // debugging output.
   154  func (g *Graph) String() string {
   155  	var buf bytes.Buffer
   156  
   157  	// Alphabetize the output based on the noun name
   158  	keys := make([]string, 0, len(g.Nouns))
   159  	mapping := make(map[string]*Noun)
   160  	for _, n := range g.Nouns {
   161  		mapping[n.Name] = n
   162  		keys = append(keys, n.Name)
   163  	}
   164  	sort.Strings(keys)
   165  
   166  	if g.Root != nil {
   167  		buf.WriteString(fmt.Sprintf("root: %s\n", g.Root.Name))
   168  	} else {
   169  		buf.WriteString("root: <unknown>\n")
   170  	}
   171  	for _, k := range keys {
   172  		n := mapping[k]
   173  		buf.WriteString(fmt.Sprintf("%s\n", n.Name))
   174  
   175  		// Alphabetize the dependency names
   176  		depKeys := make([]string, 0, len(n.Deps))
   177  		depMapping := make(map[string]*Dependency)
   178  		for _, d := range n.Deps {
   179  			depMapping[d.Target.Name] = d
   180  			depKeys = append(depKeys, d.Target.Name)
   181  		}
   182  		sort.Strings(depKeys)
   183  
   184  		for _, k := range depKeys {
   185  			dep := depMapping[k]
   186  			buf.WriteString(fmt.Sprintf(
   187  				"  %s -> %s\n",
   188  				dep.Source,
   189  				dep.Target))
   190  		}
   191  	}
   192  
   193  	return buf.String()
   194  }
   195  
   196  // Validate is used to ensure that a few properties of the graph are not violated:
   197  // 1) There must be a single "root", or source on which nothing depends.
   198  // 2) All nouns in the graph must be reachable from the root
   199  // 3) The graph must be cycle free, meaning there are no cicular dependencies
   200  func (g *Graph) Validate() error {
   201  	// Convert to node list
   202  	nodes := make([]digraph.Node, len(g.Nouns))
   203  	for i, n := range g.Nouns {
   204  		nodes[i] = n
   205  	}
   206  
   207  	// Create a validate erro
   208  	vErr := &ValidateError{}
   209  
   210  	// Search for all the sources, if we have only 1, it must be the root
   211  	if sources := digraph.Sources(nodes); len(sources) != 1 {
   212  		vErr.MissingRoot = true
   213  		goto CHECK_CYCLES
   214  	} else {
   215  		g.Root = sources[0].(*Noun)
   216  	}
   217  
   218  	// Check reachability
   219  	if unreached := digraph.Unreachable(g.Root, nodes); len(unreached) > 0 {
   220  		vErr.Unreachable = make([]*Noun, len(unreached))
   221  		for i, u := range unreached {
   222  			vErr.Unreachable[i] = u.(*Noun)
   223  		}
   224  	}
   225  
   226  CHECK_CYCLES:
   227  	// Check for cycles
   228  	if cycles := digraph.StronglyConnectedComponents(nodes, true); len(cycles) > 0 {
   229  		vErr.Cycles = make([][]*Noun, len(cycles))
   230  		for i, cycle := range cycles {
   231  			group := make([]*Noun, len(cycle))
   232  			for j, n := range cycle {
   233  				group[j] = n.(*Noun)
   234  			}
   235  			vErr.Cycles[i] = group
   236  		}
   237  	}
   238  
   239  	// Check for loops to yourself
   240  	for _, n := range g.Nouns {
   241  		for _, d := range n.Deps {
   242  			if d.Source == d.Target {
   243  				vErr.Cycles = append(vErr.Cycles, []*Noun{n})
   244  			}
   245  		}
   246  	}
   247  
   248  	// Return the detailed error
   249  	if vErr.MissingRoot || vErr.Unreachable != nil || vErr.Cycles != nil {
   250  		return vErr
   251  	}
   252  	return nil
   253  }
   254  
   255  // Walk will walk the tree depth-first (dependency first) and call
   256  // the callback.
   257  //
   258  // The callbacks will be called in parallel, so if you need non-parallelism,
   259  // then introduce a lock in your callback.
   260  func (g *Graph) Walk(fn WalkFunc) error {
   261  	// Set so we don't callback for a single noun multiple times
   262  	var seenMapL sync.RWMutex
   263  	seenMap := make(map[*Noun]chan struct{})
   264  	seenMap[g.Root] = make(chan struct{})
   265  
   266  	// Keep track of what nodes errored.
   267  	var errMapL sync.RWMutex
   268  	errMap := make(map[*Noun]struct{})
   269  
   270  	// Build the list of things to visit
   271  	tovisit := make([]*Noun, 1, len(g.Nouns))
   272  	tovisit[0] = g.Root
   273  
   274  	// Spawn off all our goroutines to walk the tree
   275  	errCh := make(chan error)
   276  	for len(tovisit) > 0 {
   277  		// Grab the current thing to use
   278  		n := len(tovisit)
   279  		current := tovisit[n-1]
   280  		tovisit = tovisit[:n-1]
   281  
   282  		// Go through each dependency and run that first
   283  		for _, dep := range current.Deps {
   284  			if _, ok := seenMap[dep.Target]; !ok {
   285  				seenMapL.Lock()
   286  				seenMap[dep.Target] = make(chan struct{})
   287  				seenMapL.Unlock()
   288  				tovisit = append(tovisit, dep.Target)
   289  			}
   290  		}
   291  
   292  		// Spawn off a goroutine to execute our callback once
   293  		// all our dependencies are satisfied.
   294  		go func(current *Noun) {
   295  			seenMapL.RLock()
   296  			closeCh := seenMap[current]
   297  			seenMapL.RUnlock()
   298  
   299  			defer close(closeCh)
   300  
   301  			// Wait for all our dependencies
   302  			for _, dep := range current.Deps {
   303  				seenMapL.RLock()
   304  				ch := seenMap[dep.Target]
   305  				seenMapL.RUnlock()
   306  
   307  				// Wait for the dep to be run
   308  				<-ch
   309  
   310  				// Check if any dependencies errored. If so,
   311  				// then return right away, we won't walk it.
   312  				errMapL.RLock()
   313  				_, errOk := errMap[dep.Target]
   314  				errMapL.RUnlock()
   315  				if errOk {
   316  					return
   317  				}
   318  			}
   319  
   320  			// Call our callback!
   321  			if err := fn(current); err != nil {
   322  				errMapL.Lock()
   323  				errMap[current] = struct{}{}
   324  				errMapL.Unlock()
   325  
   326  				errCh <- err
   327  			}
   328  		}(current)
   329  	}
   330  
   331  	// Aggregate channel that is closed when all goroutines finish
   332  	doneCh := make(chan struct{})
   333  	go func() {
   334  		defer close(doneCh)
   335  
   336  		for _, ch := range seenMap {
   337  			<-ch
   338  		}
   339  	}()
   340  
   341  	// Wait for finish OR an error
   342  	select {
   343  	case <-doneCh:
   344  		return nil
   345  	case err := <-errCh:
   346  		// Drain the error channel
   347  		go func() {
   348  			for _ = range errCh {
   349  				// Nothing
   350  			}
   351  		}()
   352  
   353  		// Wait for the goroutines to end
   354  		<-doneCh
   355  		close(errCh)
   356  
   357  		return err
   358  	}
   359  }
   360  
   361  // DependsOn returns the set of nouns that have a
   362  // dependency on a given noun. This can be used to find
   363  // the incoming edges to a noun.
   364  func (g *Graph) DependsOn(n *Noun) []*Noun {
   365  	var incoming []*Noun
   366  OUTER:
   367  	for _, other := range g.Nouns {
   368  		if other == n {
   369  			continue
   370  		}
   371  		for _, d := range other.Deps {
   372  			if d.Target == n {
   373  				incoming = append(incoming, other)
   374  				continue OUTER
   375  			}
   376  		}
   377  	}
   378  	return incoming
   379  }