github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/dependency/util.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package dependency
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  
     9  	"github.com/juju/juju/worker"
    10  )
    11  
    12  // Installer exposes an Engine's Install method.
    13  type Installer interface {
    14  	Install(name string, manifold Manifold) error
    15  }
    16  
    17  // Install is a convenience function for installing multiple manifolds into an
    18  // Installer at once. It returns the first error it encounters (and installs no
    19  // more manifolds).
    20  func Install(installer Installer, manifolds Manifolds) error {
    21  	for name, manifold := range manifolds {
    22  		if err := installer.Install(name, manifold); err != nil {
    23  			return errors.Trace(err)
    24  		}
    25  	}
    26  	return nil
    27  }
    28  
    29  // Validate will return an error if the dependency graph defined by the supplied
    30  // manifolds contains any cycles.
    31  func Validate(manifolds Manifolds) error {
    32  	inputs := make(map[string][]string)
    33  	for name, manifold := range manifolds {
    34  		inputs[name] = manifold.Inputs
    35  	}
    36  	return validator{
    37  		inputs: inputs,
    38  		doing:  make(map[string]bool),
    39  		done:   make(map[string]bool),
    40  	}.run()
    41  }
    42  
    43  // validator implements a topological sort of the nodes defined in inputs; it
    44  // doesn't actually produce sorted nodes, but rather exists to return an error
    45  // if it determines that the nodes cannot be sorted (and hence a cycle exists).
    46  type validator struct {
    47  	inputs map[string][]string
    48  	doing  map[string]bool
    49  	done   map[string]bool
    50  }
    51  
    52  func (v validator) run() error {
    53  	for node := range v.inputs {
    54  		if err := v.visit(node); err != nil {
    55  			return errors.Trace(err)
    56  		}
    57  	}
    58  	return nil
    59  }
    60  
    61  func (v validator) visit(node string) error {
    62  	if v.doing[node] {
    63  		return errors.Errorf("cycle detected at %q (considering: %v)", node, v.doing)
    64  	}
    65  	if !v.done[node] {
    66  		v.doing[node] = true
    67  		for _, input := range v.inputs[node] {
    68  			if err := v.visit(input); err != nil {
    69  				// Tracing this error will not help anyone.
    70  				return err
    71  			}
    72  		}
    73  		v.done[node] = true
    74  		v.doing[node] = false
    75  	}
    76  	return nil
    77  }
    78  
    79  // SelfManifold returns a manifold exposing a running dependency engine's
    80  // Installer and Reporter services. The returned manifold is intended for
    81  // installation into the engine it wraps; installing it into other engines
    82  // may have surprising effects.
    83  func SelfManifold(engine *Engine) Manifold {
    84  	return Manifold{
    85  		Start: func(_ Context) (worker.Worker, error) {
    86  			return engine, nil
    87  		},
    88  		Output: func(in worker.Worker, out interface{}) error {
    89  			if in != engine {
    90  				return errors.New("unexpected input worker")
    91  			}
    92  			switch outPtr := out.(type) {
    93  			case *Installer:
    94  				*outPtr = engine
    95  			case *Reporter:
    96  				*outPtr = engine
    97  			default:
    98  				return errors.Errorf("out should be a *Installer or a *Reporter; is %#v", out)
    99  			}
   100  			return nil
   101  		},
   102  	}
   103  }