github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/controller/instancepoller/mock_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // TODO (manadart 2020-06-11): Replace these hand-rolled mocks over time with
     5  // generated mocks. See package_test.go for those currently generated.
     6  
     7  package instancepoller_test
     8  
     9  import (
    10  	"sort"
    11  	"sync"
    12  	"time"
    13  
    14  	"github.com/juju/errors"
    15  	"github.com/juju/mgo/v3/txn"
    16  	"github.com/juju/names/v5"
    17  	"github.com/juju/testing"
    18  	gc "gopkg.in/check.v1"
    19  
    20  	"github.com/juju/juju/apiserver/common/networkingcommon"
    21  	"github.com/juju/juju/apiserver/facades/controller/instancepoller"
    22  	"github.com/juju/juju/core/instance"
    23  	"github.com/juju/juju/core/network"
    24  	"github.com/juju/juju/core/status"
    25  	"github.com/juju/juju/environs/config"
    26  	"github.com/juju/juju/state"
    27  )
    28  
    29  // mockState implements StateInterface and allows inspection of called
    30  // methods.
    31  type mockState struct {
    32  	*testing.Stub
    33  
    34  	mu sync.Mutex
    35  
    36  	configWatchers   []*mockConfigWatcher
    37  	machinesWatchers []*mockMachinesWatcher
    38  
    39  	config   *config.Config
    40  	machines map[string]*mockMachine
    41  
    42  	spaceInfos network.SpaceInfos
    43  }
    44  
    45  func NewMockState() *mockState {
    46  	return &mockState{
    47  		Stub:     &testing.Stub{},
    48  		machines: make(map[string]*mockMachine),
    49  	}
    50  }
    51  
    52  var _ instancepoller.StateInterface = (*mockState)(nil)
    53  
    54  // CheckFindEntityCall is a helper wrapper around
    55  // testing.Stub.CheckCall for FindEntity.
    56  func (m *mockState) CheckFindEntityCall(c *gc.C, index int, machineId string) {
    57  	m.CheckCall(c, index, "FindEntity", interface{}(names.NewMachineTag(machineId)))
    58  }
    59  
    60  // CheckMachineCall is a helper wrapper around
    61  // testing.Stub.CheckCall for Machine.
    62  func (m *mockState) CheckMachineCall(c *gc.C, index int, machineId string) {
    63  	m.CheckCall(c, index, "Machine", machineId)
    64  }
    65  
    66  // CheckSetProviderAddressesCall is a helper wrapper around
    67  // testing.Stub.CheckCall for SetProviderAddresses.
    68  func (m *mockState) CheckSetProviderAddressesCall(c *gc.C, index int, addrs []network.SpaceAddress) {
    69  	args := make([]interface{}, len(addrs))
    70  	for i, addr := range addrs {
    71  		args[i] = addr
    72  	}
    73  	m.CheckCall(c, index, "SetProviderAddresses", args...)
    74  }
    75  
    76  // WatchForModelConfigChanges implements StateInterface.
    77  func (m *mockState) WatchForModelConfigChanges() state.NotifyWatcher {
    78  	m.mu.Lock()
    79  	defer m.mu.Unlock()
    80  
    81  	m.MethodCall(m, "WatchForModelConfigChanges")
    82  
    83  	w := NewMockConfigWatcher(m.NextErr())
    84  	m.configWatchers = append(m.configWatchers, w)
    85  	return w
    86  }
    87  
    88  // ModelConfig implements StateInterface.
    89  func (m *mockState) ModelConfig() (*config.Config, error) {
    90  	m.mu.Lock()
    91  	defer m.mu.Unlock()
    92  
    93  	m.MethodCall(m, "ModelConfig")
    94  
    95  	if err := m.NextErr(); err != nil {
    96  		return nil, err
    97  	}
    98  	return m.config, nil
    99  }
   100  
   101  // SetConfig updates the model config stored internally. Triggers a
   102  // change event for all created config watchers.
   103  func (m *mockState) SetConfig(c *gc.C, newConfig *config.Config) {
   104  	m.mu.Lock()
   105  	defer m.mu.Unlock()
   106  
   107  	m.config = newConfig
   108  
   109  	// Notify any watchers for the changes.
   110  	for _, w := range m.configWatchers {
   111  		w.incoming <- struct{}{}
   112  	}
   113  }
   114  
   115  // WatchModelMachines implements StateInterface.
   116  func (m *mockState) WatchModelMachines() state.StringsWatcher {
   117  	m.mu.Lock()
   118  	defer m.mu.Unlock()
   119  
   120  	m.MethodCall(m, "WatchModelMachines")
   121  
   122  	ids := make([]string, 0, len(m.machines))
   123  	// Initial event - all machine ids, sorted.
   124  	for id := range m.machines {
   125  		ids = append(ids, id)
   126  	}
   127  	sort.Strings(ids)
   128  
   129  	w := NewMockMachinesWatcher(ids, m.NextErr())
   130  	m.machinesWatchers = append(m.machinesWatchers, w)
   131  	return w
   132  }
   133  
   134  // WatchModelMachineStartTimes implements StateInterface.
   135  func (m *mockState) WatchModelMachineStartTimes(_ time.Duration) state.StringsWatcher {
   136  	m.mu.Lock()
   137  	defer m.mu.Unlock()
   138  
   139  	m.MethodCall(m, "WatchModelMachineStartTimes")
   140  
   141  	ids := make([]string, 0, len(m.machines))
   142  	// Initial event - all machine ids, sorted.
   143  	for id := range m.machines {
   144  		ids = append(ids, id)
   145  	}
   146  	sort.Strings(ids)
   147  
   148  	w := NewMockMachinesWatcher(ids, m.NextErr())
   149  	m.machinesWatchers = append(m.machinesWatchers, w)
   150  	return w
   151  }
   152  
   153  // FindEntity implements StateInterface.
   154  func (m *mockState) FindEntity(tag names.Tag) (state.Entity, error) {
   155  	m.mu.Lock()
   156  	defer m.mu.Unlock()
   157  
   158  	m.MethodCall(m, "FindEntity", tag)
   159  
   160  	if err := m.NextErr(); err != nil {
   161  		return nil, err
   162  	}
   163  	if tag == nil {
   164  		return nil, errors.NotValidf("nil tag is") // +" not valid"
   165  	}
   166  	machine, found := m.machines[tag.Id()]
   167  	if !found {
   168  		return nil, errors.NotFoundf("machine %s", tag.Id())
   169  	}
   170  	return machine, nil
   171  }
   172  
   173  // SetMachineInfo adds a new or updates existing mockMachine info.
   174  // Triggers any created mock machines watchers to return a change.
   175  func (m *mockState) SetMachineInfo(c *gc.C, args machineInfo) {
   176  	m.mu.Lock()
   177  	defer m.mu.Unlock()
   178  
   179  	c.Assert(args.id, gc.Not(gc.Equals), "")
   180  
   181  	machine, found := m.machines[args.id]
   182  	if !found {
   183  		machine = &mockMachine{
   184  			Stub:        m.Stub, // reuse parent stub.
   185  			machineInfo: args,
   186  		}
   187  	} else {
   188  		machine.machineInfo = args
   189  	}
   190  	m.machines[args.id] = machine
   191  
   192  	// Notify any watchers for the changes.
   193  	ids := []string{args.id}
   194  	for _, w := range m.machinesWatchers {
   195  		w.incoming <- ids
   196  	}
   197  }
   198  
   199  // RemoveMachine removes an existing mockMachine with the given id.
   200  // Triggers the machines watchers on success. If the id is not found
   201  // no error occurs and no change is reported by the watchers.
   202  func (m *mockState) RemoveMachine(c *gc.C, id string) {
   203  	m.mu.Lock()
   204  	defer m.mu.Unlock()
   205  
   206  	if _, found := m.machines[id]; !found {
   207  		return
   208  	}
   209  
   210  	delete(m.machines, id)
   211  
   212  	// Notify any watchers for the changes.
   213  	ids := []string{id}
   214  	for _, w := range m.machinesWatchers {
   215  		w.incoming <- ids
   216  	}
   217  }
   218  
   219  // Machine implements StateInterface.
   220  func (m *mockState) Machine(id string) (instancepoller.StateMachine, error) {
   221  	m.mu.Lock()
   222  	defer m.mu.Unlock()
   223  
   224  	m.MethodCall(m, "Machine", id)
   225  
   226  	if err := m.NextErr(); err != nil {
   227  		return nil, err
   228  	}
   229  	machine, found := m.machines[id]
   230  	if !found {
   231  		return nil, errors.NotFoundf("machine %s", id)
   232  	}
   233  	return machine, nil
   234  }
   235  
   236  // AllSpaceInfos implements network.AllSpaceInfos.
   237  // This method never throws an error.
   238  func (m *mockState) AllSpaceInfos() (network.SpaceInfos, error) {
   239  	m.MethodCall(m, "AllSpaceInfos")
   240  	return m.spaceInfos, nil
   241  }
   242  
   243  // SetSpaceInfo updates the mocked space infos returned by AllSpaceInfos()
   244  func (m *mockState) SetSpaceInfo(infos network.SpaceInfos) {
   245  	m.mu.Lock()
   246  	defer m.mu.Unlock()
   247  	m.spaceInfos = infos
   248  }
   249  
   250  func (m *mockState) ApplyOperation(op state.ModelOperation) error {
   251  	m.MethodCall(m, "ApplyOperation", op)
   252  
   253  	ops, err := op.Build(0)
   254  	if err == nil {
   255  		// Register the generated transactions so that we can interrogate them.
   256  		m.MethodCall(m, "ApplyOperation.Build", ops)
   257  	}
   258  	return err
   259  }
   260  
   261  type machineInfo struct {
   262  	id                string
   263  	instanceId        instance.Id
   264  	status            status.StatusInfo
   265  	instanceStatus    status.StatusInfo
   266  	providerAddresses []network.SpaceAddress
   267  	life              state.Life
   268  	isManual          bool
   269  
   270  	linkLayerDevices []networkingcommon.LinkLayerDevice
   271  	addresses        []networkingcommon.LinkLayerAddress
   272  }
   273  
   274  type mockMachine struct {
   275  	*testing.Stub
   276  	instancepoller.StateMachine
   277  
   278  	mu sync.Mutex
   279  
   280  	machineInfo
   281  }
   282  
   283  var _ instancepoller.StateMachine = (*mockMachine)(nil)
   284  
   285  // Id implements StateMachine.
   286  func (m *mockMachine) Id() string {
   287  	m.mu.Lock()
   288  	defer m.mu.Unlock()
   289  
   290  	m.MethodCall(m, "Id")
   291  	return m.id
   292  }
   293  
   294  // ModelUUID implements StateMachine.
   295  func (m *mockMachine) ModelUUID() string {
   296  	m.mu.Lock()
   297  	defer m.mu.Unlock()
   298  
   299  	m.MethodCall(m, "ModelUUID")
   300  	return "any-model-uuid"
   301  }
   302  
   303  // InstanceId implements StateMachine.
   304  func (m *mockMachine) InstanceId() (instance.Id, error) {
   305  	m.mu.Lock()
   306  	defer m.mu.Unlock()
   307  
   308  	m.MethodCall(m, "InstanceId")
   309  	if err := m.NextErr(); err != nil {
   310  		return "", err
   311  	}
   312  	return m.instanceId, nil
   313  }
   314  
   315  // ProviderAddresses implements StateMachine.
   316  func (m *mockMachine) ProviderAddresses() network.SpaceAddresses {
   317  	m.mu.Lock()
   318  	defer m.mu.Unlock()
   319  
   320  	m.MethodCall(m, "ProviderAddresses")
   321  	m.NextErr() // consume the unused error
   322  	return m.providerAddresses
   323  }
   324  
   325  // SetProviderAddresses implements StateMachine.
   326  func (m *mockMachine) SetProviderAddresses(addrs ...network.SpaceAddress) error {
   327  	m.mu.Lock()
   328  	defer m.mu.Unlock()
   329  
   330  	args := make([]interface{}, len(addrs))
   331  	for i, addr := range addrs {
   332  		args[i] = addr
   333  	}
   334  	m.MethodCall(m, "SetProviderAddresses", args...)
   335  	if err := m.NextErr(); err != nil {
   336  		return err
   337  	}
   338  	m.providerAddresses = addrs
   339  	return nil
   340  }
   341  
   342  // AllLinkLayerDevices implements StateMachine.
   343  func (m *mockMachine) AllLinkLayerDevices() ([]networkingcommon.LinkLayerDevice, error) {
   344  	m.mu.Lock()
   345  	defer m.mu.Unlock()
   346  
   347  	m.MethodCall(m, "AllLinkLayerDevices")
   348  	if err := m.NextErr(); err != nil {
   349  		return nil, err
   350  	}
   351  	return m.linkLayerDevices, nil
   352  }
   353  
   354  // AllDeviceAddresses implements StateMachine.
   355  func (m *mockMachine) AllDeviceAddresses() ([]networkingcommon.LinkLayerAddress, error) {
   356  	m.mu.Lock()
   357  	defer m.mu.Unlock()
   358  
   359  	m.MethodCall(m, "AllDeviceAddresses")
   360  	if err := m.NextErr(); err != nil {
   361  		return nil, err
   362  	}
   363  	return m.addresses, nil
   364  }
   365  
   366  // InstanceStatus implements StateMachine.
   367  func (m *mockMachine) InstanceStatus() (status.StatusInfo, error) {
   368  	m.mu.Lock()
   369  	defer m.mu.Unlock()
   370  
   371  	m.MethodCall(m, "InstanceStatus")
   372  	if err := m.NextErr(); err != nil {
   373  		return status.StatusInfo{}, err
   374  	}
   375  	return m.instanceStatus, nil
   376  }
   377  
   378  // SetInstanceStatus implements StateMachine.
   379  func (m *mockMachine) SetInstanceStatus(instanceStatus status.StatusInfo) error {
   380  	m.mu.Lock()
   381  	defer m.mu.Unlock()
   382  
   383  	m.MethodCall(m, "SetInstanceStatus", instanceStatus)
   384  	if err := m.NextErr(); err != nil {
   385  		return err
   386  	}
   387  	m.instanceStatus = instanceStatus
   388  	return nil
   389  }
   390  
   391  // Life implements StateMachine.
   392  func (m *mockMachine) Life() state.Life {
   393  	m.mu.Lock()
   394  	defer m.mu.Unlock()
   395  
   396  	m.MethodCall(m, "Life")
   397  	m.NextErr() // consume the unused error
   398  	return m.life
   399  }
   400  
   401  // IsManual implements StateMachine.
   402  func (m *mockMachine) IsManual() (bool, error) {
   403  	m.mu.Lock()
   404  	defer m.mu.Unlock()
   405  
   406  	m.MethodCall(m, "IsManual")
   407  	return m.isManual, m.NextErr()
   408  }
   409  
   410  // Status implements StateMachine.
   411  func (m *mockMachine) Status() (status.StatusInfo, error) {
   412  	m.mu.Lock()
   413  	defer m.mu.Unlock()
   414  
   415  	m.MethodCall(m, "Status")
   416  	return m.status, m.NextErr()
   417  }
   418  
   419  // AssertAliveOp implements StateMachine.
   420  func (m *mockMachine) AssertAliveOp() txn.Op {
   421  	m.mu.Lock()
   422  	defer m.mu.Unlock()
   423  
   424  	m.MethodCall(m, "Status")
   425  	return txn.Op{C: "machine-alive"}
   426  }
   427  
   428  type mockBaseWatcher struct {
   429  	err error
   430  
   431  	closeChanges func()
   432  	done         chan struct{}
   433  }
   434  
   435  var _ state.Watcher = (*mockBaseWatcher)(nil)
   436  
   437  func NewMockBaseWatcher(err error, closeChanges func()) *mockBaseWatcher {
   438  	w := &mockBaseWatcher{
   439  		err:          err,
   440  		closeChanges: closeChanges,
   441  		done:         make(chan struct{}),
   442  	}
   443  	if err != nil {
   444  		// Don't start the loop if we should fail.
   445  		w.Stop()
   446  	}
   447  	return w
   448  }
   449  
   450  // Kill implements state.Watcher.
   451  func (m *mockBaseWatcher) Kill() {}
   452  
   453  // Stop implements state.Watcher.
   454  func (m *mockBaseWatcher) Stop() error {
   455  	select {
   456  	case <-m.done:
   457  		// already closed
   458  	default:
   459  		// Signal the loop we want to stop.
   460  		close(m.done)
   461  		// Signal the clients we've closed.
   462  		m.closeChanges()
   463  	}
   464  	return m.err
   465  }
   466  
   467  // Wait implements state.Watcher.
   468  func (m *mockBaseWatcher) Wait() error {
   469  	return m.Stop()
   470  }
   471  
   472  // Err implements state.Watcher.
   473  func (m *mockBaseWatcher) Err() error {
   474  	return m.err
   475  }
   476  
   477  type mockConfigWatcher struct {
   478  	*mockBaseWatcher
   479  
   480  	incoming chan struct{}
   481  	changes  chan struct{}
   482  }
   483  
   484  var _ state.NotifyWatcher = (*mockConfigWatcher)(nil)
   485  
   486  func NewMockConfigWatcher(err error) *mockConfigWatcher {
   487  	changes := make(chan struct{})
   488  	w := &mockConfigWatcher{
   489  		changes:         changes,
   490  		incoming:        make(chan struct{}),
   491  		mockBaseWatcher: NewMockBaseWatcher(err, func() { close(changes) }),
   492  	}
   493  	if err == nil {
   494  		go w.loop()
   495  	}
   496  	return w
   497  }
   498  
   499  func (m *mockConfigWatcher) loop() {
   500  	// Prepare initial event.
   501  	outChanges := m.changes
   502  	// Forward any incoming changes until stopped.
   503  	for {
   504  		select {
   505  		case <-m.done:
   506  			// We're about to quit.
   507  			return
   508  		case outChanges <- struct{}{}:
   509  			outChanges = nil
   510  		case <-m.incoming:
   511  			outChanges = m.changes
   512  		}
   513  	}
   514  }
   515  
   516  // Changes implements state.NotifyWatcher.
   517  func (m *mockConfigWatcher) Changes() <-chan struct{} {
   518  	return m.changes
   519  }
   520  
   521  type mockMachinesWatcher struct {
   522  	*mockBaseWatcher
   523  
   524  	initial  []string
   525  	incoming chan []string
   526  	changes  chan []string
   527  }
   528  
   529  func NewMockMachinesWatcher(initial []string, err error) *mockMachinesWatcher {
   530  	changes := make(chan []string)
   531  	w := &mockMachinesWatcher{
   532  		initial:         initial,
   533  		changes:         changes,
   534  		incoming:        make(chan []string),
   535  		mockBaseWatcher: NewMockBaseWatcher(err, func() { close(changes) }),
   536  	}
   537  	if err == nil {
   538  		go w.loop()
   539  	}
   540  	return w
   541  }
   542  
   543  func (m *mockMachinesWatcher) loop() {
   544  	// Prepare initial event.
   545  	unsent := m.initial
   546  	outChanges := m.changes
   547  	// Forward any incoming changes until stopped.
   548  	for {
   549  		select {
   550  		case <-m.done:
   551  			// We're about to quit.
   552  			return
   553  		case outChanges <- unsent:
   554  			outChanges = nil
   555  			unsent = nil
   556  		case ids := <-m.incoming:
   557  			unsent = append(unsent, ids...)
   558  			outChanges = m.changes
   559  		}
   560  	}
   561  }
   562  
   563  // Changes implements state.StringsWatcher.
   564  func (m *mockMachinesWatcher) Changes() <-chan []string {
   565  	return m.changes
   566  }
   567  
   568  var _ state.StringsWatcher = (*mockMachinesWatcher)(nil)