github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/jujud/agent/util_test.go (about)

     1  // Copyright 2012-2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package agent
     5  
     6  import (
     7  	"sync"
     8  
     9  	"github.com/juju/errors"
    10  	jc "github.com/juju/testing/checkers"
    11  	"github.com/juju/utils/set"
    12  	gc "gopkg.in/check.v1"
    13  
    14  	"github.com/juju/juju/cmd/jujud/agent/model"
    15  	coretesting "github.com/juju/juju/testing"
    16  	"github.com/juju/juju/worker"
    17  	"github.com/juju/juju/worker/dependency"
    18  )
    19  
    20  var (
    21  	// These vars hold the per-model workers we expect to run in
    22  	// various circumstances. Note the absence of dyingModelWorkers:
    23  	// it's not a stable state, because it's responsible for making
    24  	// the model Dead via the undertaker, so it can't be waited for
    25  	// reliably.
    26  	alwaysModelWorkers = []string{
    27  		"agent",
    28  		"api-caller",
    29  		"api-config-watcher",
    30  		"clock",
    31  		"spaces-imported-gate",
    32  		"is-responsible-flag",
    33  		"not-alive-flag",
    34  		"not-dead-flag",
    35  	}
    36  	aliveModelWorkers = []string{
    37  		"charm-revision-updater",
    38  		"compute-provisioner",
    39  		"environ-tracker",
    40  		"firewaller",
    41  		"instance-poller",
    42  		"metric-worker",
    43  		"migration-fortress",
    44  		"migration-master",
    45  		"service-scaler",
    46  		"space-importer",
    47  		"state-cleaner",
    48  		"status-history-pruner",
    49  		"storage-provisioner",
    50  		"unit-assigner",
    51  	}
    52  	deadModelWorkers = []string{
    53  		"environ-tracker", "undertaker",
    54  	}
    55  
    56  	// ReallyLongTimeout should be long enough for the model-tracker
    57  	// tests that depend on a hosted model; its backing state is not
    58  	// accessible for StartSyncs, so we generally have to wait for at
    59  	// least two 5s ticks to pass, and should expect rare circumstances
    60  	// to take even longer.
    61  	ReallyLongWait = coretesting.LongWait * 3
    62  )
    63  
    64  // modelMatchFunc returns a func that will return whether the current
    65  // set of workers running for the supplied model matches those supplied;
    66  // and will log what it saw in some detail.
    67  func modelMatchFunc(c *gc.C, tracker *modelTracker, workers []string) func(string) bool {
    68  	expect := set.NewStrings(workers...)
    69  	return func(uuid string) bool {
    70  		actual := tracker.Workers(uuid)
    71  		c.Logf("\n%s: has workers %v", uuid, actual.SortedValues())
    72  		extras := actual.Difference(expect)
    73  		missed := expect.Difference(actual)
    74  		if len(extras) == 0 && len(missed) == 0 {
    75  			return true
    76  		}
    77  		c.Logf("%s: waiting for %v", uuid, missed.SortedValues())
    78  		c.Logf("%s: unexpected %v", uuid, extras.SortedValues())
    79  		return false
    80  	}
    81  }
    82  
    83  // newModelTracker creates a type whose Manifolds method can
    84  // be patched over modelManifolds, and whose Workers method
    85  // will tell you what workers are currently running stably
    86  // within the requested model's dependency engine.
    87  func newModelTracker(c *gc.C) *modelTracker {
    88  	return &modelTracker{
    89  		c:       c,
    90  		current: make(map[string]set.Strings),
    91  	}
    92  }
    93  
    94  type modelTracker struct {
    95  	c       *gc.C
    96  	mu      sync.Mutex
    97  	current map[string]set.Strings
    98  }
    99  
   100  func (tracker *modelTracker) Workers(model string) set.Strings {
   101  	tracker.mu.Lock()
   102  	defer tracker.mu.Unlock()
   103  	return tracker.current[model]
   104  }
   105  
   106  func (tracker *modelTracker) Manifolds(config model.ManifoldsConfig) dependency.Manifolds {
   107  	const trackerName = "TEST-TRACKER"
   108  	raw := model.Manifolds(config)
   109  	uuid := config.Agent.CurrentConfig().Model().Id()
   110  
   111  	names := make([]string, 0, len(raw))
   112  	for name := range raw {
   113  		if name == trackerName {
   114  			tracker.c.Errorf("manifold tracker used repeatedly")
   115  			return raw
   116  		} else {
   117  			names = append(names, name)
   118  		}
   119  	}
   120  
   121  	tracker.mu.Lock()
   122  	defer tracker.mu.Unlock()
   123  	if _, exists := tracker.current[uuid]; exists {
   124  		tracker.c.Errorf("model %s started repeatedly", uuid)
   125  		return raw
   126  	}
   127  
   128  	raw[trackerName] = tracker.manifold(uuid, names)
   129  	return raw
   130  }
   131  
   132  func (tracker *modelTracker) manifold(uuid string, names []string) dependency.Manifold {
   133  	return dependency.Manifold{
   134  		Inputs: names,
   135  		Start: func(context dependency.Context) (worker.Worker, error) {
   136  			seen := set.NewStrings()
   137  			for _, name := range names {
   138  				err := context.Get(name, nil)
   139  				if errors.Cause(err) == dependency.ErrMissing {
   140  					continue
   141  				}
   142  				if tracker.c.Check(err, jc.ErrorIsNil) {
   143  					seen.Add(name)
   144  				}
   145  			}
   146  			select {
   147  			case <-context.Abort():
   148  				// don't bother to report if it's about to change
   149  			default:
   150  				tracker.mu.Lock()
   151  				defer tracker.mu.Unlock()
   152  				tracker.current[uuid] = seen
   153  			}
   154  			return nil, dependency.ErrMissing
   155  		},
   156  	}
   157  }