github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/apiserver/addresser/mock_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package addresser_test
     5  
     6  import (
     7  	"sort"
     8  	"sync"
     9  
    10  	gc "gopkg.in/check.v1"
    11  
    12  	"github.com/juju/errors"
    13  	"github.com/juju/names"
    14  	"github.com/juju/testing"
    15  	jujutxn "github.com/juju/txn"
    16  
    17  	"github.com/juju/juju/apiserver/addresser"
    18  	"github.com/juju/juju/environs"
    19  	"github.com/juju/juju/environs/config"
    20  	"github.com/juju/juju/instance"
    21  	"github.com/juju/juju/network"
    22  	"github.com/juju/juju/provider/dummy"
    23  	"github.com/juju/juju/state"
    24  	coretesting "github.com/juju/juju/testing"
    25  )
    26  
    27  // mockState implements StateInterface and allows inspection of called
    28  // methods.
    29  type mockState struct {
    30  	mu   sync.Mutex
    31  	stub *testing.Stub
    32  
    33  	config      *config.Config
    34  	ipAddresses map[string]*mockIPAddress
    35  
    36  	ipAddressWatchers []*mockIPAddressWatcher
    37  }
    38  
    39  func newMockState() *mockState {
    40  	mst := &mockState{
    41  		stub:        &testing.Stub{},
    42  		ipAddresses: make(map[string]*mockIPAddress),
    43  	}
    44  	mst.setUpState()
    45  	return mst
    46  }
    47  
    48  func (mst *mockState) setUpState() {
    49  	mst.mu.Lock()
    50  	defer mst.mu.Unlock()
    51  
    52  	ips := []struct {
    53  		value      string
    54  		uuid       string
    55  		life       state.Life
    56  		subnetId   string
    57  		instanceId string
    58  		macaddr    string
    59  	}{
    60  		{"0.1.2.3", "00000000-1111-2222-3333-0123456789ab", state.Alive, "a", "a3", "fff3"},
    61  		{"0.1.2.4", "00000000-1111-2222-4444-0123456789ab", state.Alive, "b", "b4", "fff4"},
    62  		{"0.1.2.5", "00000000-1111-2222-5555-0123456789ab", state.Alive, "b", "b5", "fff5"},
    63  		{"0.1.2.6", "00000000-1111-2222-6666-0123456789ab", state.Dead, "c", "c6", "fff6"},
    64  		{"0.1.2.7", "00000000-1111-2222-7777-0123456789ab", state.Dead, "c", "c7", "fff7"},
    65  	}
    66  	for _, ip := range ips {
    67  		mst.ipAddresses[ip.value] = &mockIPAddress{
    68  			stub:       mst.stub,
    69  			st:         mst,
    70  			value:      ip.value,
    71  			tag:        names.NewIPAddressTag(ip.uuid),
    72  			life:       ip.life,
    73  			subnetId:   ip.subnetId,
    74  			instanceId: instance.Id(ip.instanceId),
    75  			addr:       network.NewAddress(ip.value),
    76  			macaddr:    ip.macaddr,
    77  		}
    78  	}
    79  }
    80  
    81  var _ addresser.StateInterface = (*mockState)(nil)
    82  
    83  // EnvironConfig implements StateInterface.
    84  func (mst *mockState) EnvironConfig() (*config.Config, error) {
    85  	mst.mu.Lock()
    86  	defer mst.mu.Unlock()
    87  
    88  	mst.stub.MethodCall(mst, "EnvironConfig")
    89  
    90  	if err := mst.stub.NextErr(); err != nil {
    91  		return nil, err
    92  	}
    93  	return mst.config, nil
    94  }
    95  
    96  // setConfig updates the environ config stored internally. Triggers a
    97  // change event for all created config watchers.
    98  func (mst *mockState) setConfig(c *gc.C, newConfig *config.Config) {
    99  	mst.mu.Lock()
   100  	defer mst.mu.Unlock()
   101  
   102  	mst.config = newConfig
   103  }
   104  
   105  // IPAddress implements StateInterface.
   106  func (mst *mockState) IPAddress(value string) (addresser.StateIPAddress, error) {
   107  	mst.mu.Lock()
   108  	defer mst.mu.Unlock()
   109  
   110  	mst.stub.MethodCall(mst, "IPAddress", value)
   111  	if err := mst.stub.NextErr(); err != nil {
   112  		return nil, err
   113  	}
   114  	ipAddress, found := mst.ipAddresses[value]
   115  	if !found {
   116  		return nil, errors.NotFoundf("IP address %s", value)
   117  	}
   118  	return ipAddress, nil
   119  }
   120  
   121  // setDead sets a mock IP address in state to dead.
   122  func (mst *mockState) setDead(c *gc.C, value string) {
   123  	mst.mu.Lock()
   124  	defer mst.mu.Unlock()
   125  
   126  	ipAddress, found := mst.ipAddresses[value]
   127  	c.Assert(found, gc.Equals, true)
   128  
   129  	ipAddress.life = state.Dead
   130  }
   131  
   132  // DeadIPAddresses implements StateInterface.
   133  func (mst *mockState) DeadIPAddresses() ([]addresser.StateIPAddress, error) {
   134  	mst.mu.Lock()
   135  	defer mst.mu.Unlock()
   136  
   137  	mst.stub.MethodCall(mst, "DeadIPAddresses")
   138  	if err := mst.stub.NextErr(); err != nil {
   139  		return nil, err
   140  	}
   141  	var deadIPAddresses []addresser.StateIPAddress
   142  	for _, ipAddress := range mst.ipAddresses {
   143  		if ipAddress.life == state.Dead {
   144  			deadIPAddresses = append(deadIPAddresses, ipAddress)
   145  		}
   146  	}
   147  	return deadIPAddresses, nil
   148  }
   149  
   150  // WatchIPAddresses implements StateInterface.
   151  func (mst *mockState) WatchIPAddresses() state.StringsWatcher {
   152  	mst.mu.Lock()
   153  	defer mst.mu.Unlock()
   154  
   155  	mst.stub.MethodCall(mst, "WatchIPAddresses")
   156  	mst.stub.NextErr()
   157  	return mst.nextIPAddressWatcher()
   158  }
   159  
   160  // addIPAddressWatcher adds an IP address watcher with the given IDs.
   161  func (mst *mockState) addIPAddressWatcher(ids ...string) *mockIPAddressWatcher {
   162  	w := newMockIPAddressWatcher(ids)
   163  	mst.ipAddressWatchers = append(mst.ipAddressWatchers, w)
   164  	return w
   165  }
   166  
   167  // nextIPAddressWatcher returns an IP address watcher .
   168  func (mst *mockState) nextIPAddressWatcher() *mockIPAddressWatcher {
   169  	if len(mst.ipAddressWatchers) == 0 {
   170  		panic("ran out of watchers")
   171  	}
   172  
   173  	w := mst.ipAddressWatchers[0]
   174  	mst.ipAddressWatchers = mst.ipAddressWatchers[1:]
   175  	if len(w.initial) == 0 {
   176  		ids := make([]string, 0, len(mst.ipAddresses))
   177  		// Initial event - all IP address ids, sorted.
   178  		for id := range mst.ipAddresses {
   179  			ids = append(ids, id)
   180  		}
   181  		sort.Strings(ids)
   182  		w.initial = ids
   183  	}
   184  	w.start()
   185  	return w
   186  }
   187  
   188  // removeIPAddress is used by mockIPAddress.Remove()
   189  func (mst *mockState) removeIPAddress(value string) error {
   190  	mst.mu.Lock()
   191  	defer mst.mu.Unlock()
   192  
   193  	ipAddr, ok := mst.ipAddresses[value]
   194  	if !ok {
   195  		return jujutxn.ErrNoOperations
   196  	}
   197  	if ipAddr.life != state.Dead {
   198  		return errors.Errorf("cannot remove IP address %q: IP address is not dead", ipAddr.value)
   199  	}
   200  	delete(mst.ipAddresses, value)
   201  	return nil
   202  }
   203  
   204  // StartSync implements statetesting.SyncStarter, so mockState can be
   205  // used with watcher helpers/checkers.
   206  func (mst *mockState) StartSync() {}
   207  
   208  // mockIPAddress implements StateIPAddress for testing.
   209  type mockIPAddress struct {
   210  	addresser.StateIPAddress
   211  
   212  	stub       *testing.Stub
   213  	st         *mockState
   214  	value      string
   215  	tag        names.Tag
   216  	life       state.Life
   217  	subnetId   string
   218  	instanceId instance.Id
   219  	addr       network.Address
   220  	macaddr    string
   221  }
   222  
   223  var _ addresser.StateIPAddress = (*mockIPAddress)(nil)
   224  
   225  // Value implements StateIPAddress.
   226  func (mip *mockIPAddress) Value() string {
   227  	mip.stub.MethodCall(mip, "Value")
   228  	mip.stub.NextErr() // Consume the unused error.
   229  	return mip.value
   230  }
   231  
   232  // Tag implements StateIPAddress.
   233  func (mip *mockIPAddress) Tag() names.Tag {
   234  	mip.stub.MethodCall(mip, "Tag")
   235  	mip.stub.NextErr() // Consume the unused error.
   236  	return mip.tag
   237  }
   238  
   239  // Life implements StateIPAddress.
   240  func (mip *mockIPAddress) Life() state.Life {
   241  	mip.stub.MethodCall(mip, "Life")
   242  	mip.stub.NextErr() // Consume the unused error.
   243  	return mip.life
   244  }
   245  
   246  // Remove implements StateIPAddress.
   247  func (mip *mockIPAddress) Remove() error {
   248  	mip.stub.MethodCall(mip, "Remove")
   249  	if err := mip.stub.NextErr(); err != nil {
   250  		return err
   251  	}
   252  	return mip.st.removeIPAddress(mip.value)
   253  }
   254  
   255  // SubnetId implements StateIPAddress.
   256  func (mip *mockIPAddress) SubnetId() string {
   257  	mip.stub.MethodCall(mip, "SubnetId")
   258  	mip.stub.NextErr() // Consume the unused error.
   259  	return mip.subnetId
   260  }
   261  
   262  // InstanceId implements StateIPAddress.
   263  func (mip *mockIPAddress) InstanceId() instance.Id {
   264  	mip.stub.MethodCall(mip, "InstanceId")
   265  	mip.stub.NextErr() // Consume the unused error.
   266  	return mip.instanceId
   267  }
   268  
   269  // Address implements StateIPAddress.
   270  func (mip *mockIPAddress) Address() network.Address {
   271  	mip.stub.MethodCall(mip, "Address")
   272  	mip.stub.NextErr() // Consume the unused error.
   273  	return mip.addr
   274  }
   275  
   276  // MACAddress implements StateIPAddress.
   277  func (mip *mockIPAddress) MACAddress() string {
   278  	mip.stub.MethodCall(mip, "MACAddress")
   279  	mip.stub.NextErr() // Consume the unused error.
   280  	return mip.macaddr
   281  }
   282  
   283  // mockIPAddressWatcher notifies about IP address changes.
   284  type mockIPAddressWatcher struct {
   285  	err       error
   286  	initial   []string
   287  	wontStart bool
   288  	incoming  chan []string
   289  	changes   chan []string
   290  	done      chan struct{}
   291  }
   292  
   293  var _ state.StringsWatcher = (*mockIPAddressWatcher)(nil)
   294  
   295  func newMockIPAddressWatcher(initial []string) *mockIPAddressWatcher {
   296  	mipw := &mockIPAddressWatcher{
   297  		initial:   initial,
   298  		wontStart: false,
   299  		incoming:  make(chan []string),
   300  		changes:   make(chan []string),
   301  		done:      make(chan struct{}),
   302  	}
   303  	return mipw
   304  }
   305  
   306  // Kill implements state.Watcher.
   307  func (mipw *mockIPAddressWatcher) Kill() {}
   308  
   309  // Stop implements state.Watcher.
   310  func (mipw *mockIPAddressWatcher) Stop() error {
   311  	select {
   312  	case <-mipw.done:
   313  		// Closed.
   314  	default:
   315  		// Signal the loop we want to stop.
   316  		close(mipw.done)
   317  		// Signal the clients we've closed.
   318  		close(mipw.changes)
   319  	}
   320  	return mipw.err
   321  }
   322  
   323  // Wait implements state.Watcher.
   324  func (mipw *mockIPAddressWatcher) Wait() error {
   325  	return mipw.Stop()
   326  }
   327  
   328  // Err implements state.Watcher.
   329  func (mipw *mockIPAddressWatcher) Err() error {
   330  	return mipw.err
   331  }
   332  
   333  // start starts the backend loop depending on a field setting.
   334  func (mipw *mockIPAddressWatcher) start() {
   335  	if mipw.wontStart {
   336  		// Set manually by tests that need it.
   337  		mipw.Stop()
   338  		return
   339  	}
   340  	go mipw.loop()
   341  }
   342  
   343  func (mipw *mockIPAddressWatcher) loop() {
   344  	// Prepare initial event.
   345  	unsent := mipw.initial
   346  	outChanges := mipw.changes
   347  	// Forward any incoming changes until stopped.
   348  	for {
   349  		select {
   350  		case <-mipw.done:
   351  			return
   352  		case outChanges <- unsent:
   353  			outChanges = nil
   354  			unsent = nil
   355  		case ids := <-mipw.incoming:
   356  			unsent = append(unsent, ids...)
   357  			outChanges = mipw.changes
   358  		}
   359  	}
   360  }
   361  
   362  // Changes implements state.StringsWatcher.
   363  func (mipw *mockIPAddressWatcher) Changes() <-chan []string {
   364  	return mipw.changes
   365  }
   366  
   367  // mockConfig returns a configuration for the usage of the
   368  // mock provider below.
   369  func mockConfig() coretesting.Attrs {
   370  	return dummy.SampleConfig().Merge(coretesting.Attrs{
   371  		"type": "mock",
   372  	})
   373  }
   374  
   375  // mockEnviron is an environment without networking support.
   376  type mockEnviron struct {
   377  	environs.Environ
   378  }
   379  
   380  func (e mockEnviron) Config() *config.Config {
   381  	cfg, err := config.New(config.NoDefaults, mockConfig())
   382  	if err != nil {
   383  		panic("invalid configuration for testing")
   384  	}
   385  	return cfg
   386  }
   387  
   388  // mockEnvironProvider is the smallest possible provider to
   389  // test the addresses without networking support.
   390  type mockEnvironProvider struct {
   391  	environs.EnvironProvider
   392  }
   393  
   394  func (p mockEnvironProvider) PrepareForBootstrap(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) {
   395  	return &mockEnviron{}, nil
   396  }
   397  
   398  func (p mockEnvironProvider) Open(*config.Config) (environs.Environ, error) {
   399  	return &mockEnviron{}, nil
   400  }