github.com/ezbercih/terraform@v0.1.1-0.20140729011846-3c33865e0839/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  	buf.WriteString(fmt.Sprintf("root: %s\n", g.Root.Name))
   167  	for _, k := range keys {
   168  		n := mapping[k]
   169  		buf.WriteString(fmt.Sprintf("%s\n", n.Name))
   170  
   171  		// Alphabetize the dependency names
   172  		depKeys := make([]string, 0, len(n.Deps))
   173  		depMapping := make(map[string]*Dependency)
   174  		for _, d := range n.Deps {
   175  			depMapping[d.Target.Name] = d
   176  			depKeys = append(depKeys, d.Target.Name)
   177  		}
   178  		sort.Strings(depKeys)
   179  
   180  		for _, k := range depKeys {
   181  			dep := depMapping[k]
   182  			buf.WriteString(fmt.Sprintf(
   183  				"  %s -> %s\n",
   184  				dep.Source,
   185  				dep.Target))
   186  		}
   187  	}
   188  
   189  	return buf.String()
   190  }
   191  
   192  // Validate is used to ensure that a few properties of the graph are not violated:
   193  // 1) There must be a single "root", or source on which nothing depends.
   194  // 2) All nouns in the graph must be reachable from the root
   195  // 3) The graph must be cycle free, meaning there are no cicular dependencies
   196  func (g *Graph) Validate() error {
   197  	// Convert to node list
   198  	nodes := make([]digraph.Node, len(g.Nouns))
   199  	for i, n := range g.Nouns {
   200  		nodes[i] = n
   201  	}
   202  
   203  	// Create a validate erro
   204  	vErr := &ValidateError{}
   205  
   206  	// Search for all the sources, if we have only 1, it must be the root
   207  	if sources := digraph.Sources(nodes); len(sources) != 1 {
   208  		vErr.MissingRoot = true
   209  		goto CHECK_CYCLES
   210  	} else {
   211  		g.Root = sources[0].(*Noun)
   212  	}
   213  
   214  	// Check reachability
   215  	if unreached := digraph.Unreachable(g.Root, nodes); len(unreached) > 0 {
   216  		vErr.Unreachable = make([]*Noun, len(unreached))
   217  		for i, u := range unreached {
   218  			vErr.Unreachable[i] = u.(*Noun)
   219  		}
   220  	}
   221  
   222  CHECK_CYCLES:
   223  	// Check for cycles
   224  	if cycles := digraph.StronglyConnectedComponents(nodes, true); len(cycles) > 0 {
   225  		vErr.Cycles = make([][]*Noun, len(cycles))
   226  		for i, cycle := range cycles {
   227  			group := make([]*Noun, len(cycle))
   228  			for j, n := range cycle {
   229  				group[j] = n.(*Noun)
   230  			}
   231  			vErr.Cycles[i] = group
   232  		}
   233  	}
   234  
   235  	// Check for loops to yourself
   236  	for _, n := range g.Nouns {
   237  		for _, d := range n.Deps {
   238  			if d.Source == d.Target {
   239  				vErr.Cycles = append(vErr.Cycles, []*Noun{n})
   240  			}
   241  		}
   242  	}
   243  
   244  	// Return the detailed error
   245  	if vErr.MissingRoot || vErr.Unreachable != nil || vErr.Cycles != nil {
   246  		return vErr
   247  	}
   248  	return nil
   249  }
   250  
   251  // Walk will walk the tree depth-first (dependency first) and call
   252  // the callback.
   253  //
   254  // The callbacks will be called in parallel, so if you need non-parallelism,
   255  // then introduce a lock in your callback.
   256  func (g *Graph) Walk(fn WalkFunc) error {
   257  	// Set so we don't callback for a single noun multiple times
   258  	var seenMapL sync.RWMutex
   259  	seenMap := make(map[*Noun]chan struct{})
   260  	seenMap[g.Root] = make(chan struct{})
   261  
   262  	// Keep track of what nodes errored.
   263  	var errMapL sync.RWMutex
   264  	errMap := make(map[*Noun]struct{})
   265  
   266  	// Build the list of things to visit
   267  	tovisit := make([]*Noun, 1, len(g.Nouns))
   268  	tovisit[0] = g.Root
   269  
   270  	// Spawn off all our goroutines to walk the tree
   271  	errCh := make(chan error)
   272  	quitCh := make(chan struct{})
   273  	for len(tovisit) > 0 {
   274  		// Grab the current thing to use
   275  		n := len(tovisit)
   276  		current := tovisit[n-1]
   277  		tovisit = tovisit[:n-1]
   278  
   279  		// Go through each dependency and run that first
   280  		for _, dep := range current.Deps {
   281  			if _, ok := seenMap[dep.Target]; !ok {
   282  				seenMapL.Lock()
   283  				seenMap[dep.Target] = make(chan struct{})
   284  				seenMapL.Unlock()
   285  				tovisit = append(tovisit, dep.Target)
   286  			}
   287  		}
   288  
   289  		// Spawn off a goroutine to execute our callback once
   290  		// all our dependencies are satisified.
   291  		go func(current *Noun) {
   292  			seenMapL.RLock()
   293  			closeCh := seenMap[current]
   294  			seenMapL.RUnlock()
   295  
   296  			defer close(closeCh)
   297  
   298  			// Wait for all our dependencies
   299  			for _, dep := range current.Deps {
   300  				seenMapL.RLock()
   301  				ch := seenMap[dep.Target]
   302  				seenMapL.RUnlock()
   303  
   304  				select {
   305  				case <-ch:
   306  				case <-quitCh:
   307  					return
   308  				}
   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  		// Close the quit channel so all our goroutines will end now
   347  		close(quitCh)
   348  
   349  		// Drain the error channel
   350  		go func() {
   351  			for _ = range errCh {
   352  				// Nothing
   353  			}
   354  		}()
   355  
   356  		// Wait for the goroutines to end
   357  		<-doneCh
   358  		close(errCh)
   359  
   360  		return err
   361  	}
   362  }