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