github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/peergrouper/mock_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package peergrouper
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"math/rand"
    10  	"path"
    11  	"reflect"
    12  	"strconv"
    13  	"sync"
    14  
    15  	"github.com/juju/errors"
    16  	"github.com/juju/replicaset"
    17  	"github.com/juju/utils/voyeur"
    18  	"gopkg.in/juju/worker.v1"
    19  	"gopkg.in/tomb.v2"
    20  
    21  	"github.com/juju/juju/controller"
    22  	"github.com/juju/juju/core/instance"
    23  	"github.com/juju/juju/core/status"
    24  	"github.com/juju/juju/environs/config"
    25  	"github.com/juju/juju/network"
    26  	"github.com/juju/juju/state"
    27  	coretesting "github.com/juju/juju/testing"
    28  )
    29  
    30  // This file holds helper functions for mocking pieces of State and replicaset
    31  // that we don't want to directly depend on in unit tests.
    32  
    33  type fakeState struct {
    34  	mu               sync.Mutex
    35  	errors           errorPatterns
    36  	machines         map[string]*fakeMachine
    37  	controllers      voyeur.Value // of *state.ControllerInfo
    38  	statuses         voyeur.Value // of statuses collection
    39  	controllerConfig voyeur.Value // of controller.Config
    40  	session          *fakeMongoSession
    41  	checkMu          sync.Mutex
    42  	check            func(st *fakeState) error
    43  }
    44  
    45  var (
    46  	_ State        = (*fakeState)(nil)
    47  	_ Machine      = (*fakeMachine)(nil)
    48  	_ MongoSession = (*fakeMongoSession)(nil)
    49  )
    50  
    51  type errorPatterns struct {
    52  	patterns []errorPattern
    53  }
    54  
    55  type errorPattern struct {
    56  	pattern string
    57  	errFunc func() error
    58  }
    59  
    60  // setErrorFor causes the given error to be returned
    61  // from any mock call that matches the given
    62  // string, which may contain wildcards as
    63  // in path.Match.
    64  //
    65  // The standard form for errors is:
    66  //    Type.Function <arg>...
    67  // See individual functions for details.
    68  func (e *errorPatterns) setErrorFor(what string, err error) {
    69  	e.setErrorFuncFor(what, func() error {
    70  		return err
    71  	})
    72  }
    73  
    74  // setErrorFuncFor causes the given function
    75  // to be invoked to return the error for the
    76  // given pattern.
    77  func (e *errorPatterns) setErrorFuncFor(what string, errFunc func() error) {
    78  	e.patterns = append(e.patterns, errorPattern{
    79  		pattern: what,
    80  		errFunc: errFunc,
    81  	})
    82  }
    83  
    84  // errorFor concatenates the call name
    85  // with all the args, space separated,
    86  // and returns any error registered with
    87  // setErrorFor that matches the resulting string.
    88  func (e *errorPatterns) errorFor(name string, args ...interface{}) error {
    89  	s := name
    90  	for _, arg := range args {
    91  		s += " " + fmt.Sprint(arg)
    92  	}
    93  	f := func() error { return nil }
    94  	for _, pattern := range e.patterns {
    95  		if ok, _ := path.Match(pattern.pattern, s); ok {
    96  			f = pattern.errFunc
    97  			break
    98  		}
    99  	}
   100  	err := f()
   101  	if err != nil {
   102  		logger.Errorf("errorFor %q -> %v", s, err)
   103  	}
   104  	return err
   105  }
   106  
   107  func (e *errorPatterns) resetErrors() {
   108  	e.patterns = e.patterns[:0]
   109  }
   110  
   111  func NewFakeState() *fakeState {
   112  	st := &fakeState{
   113  		machines: make(map[string]*fakeMachine),
   114  	}
   115  	st.session = newFakeMongoSession(st, &st.errors)
   116  	st.controllerConfig.Set(controller.Config{})
   117  	return st
   118  }
   119  
   120  func (st *fakeState) getCheck() func(st *fakeState) error {
   121  	st.checkMu.Lock()
   122  	check := st.check
   123  	st.checkMu.Unlock()
   124  	return check
   125  }
   126  
   127  func (st *fakeState) setCheck(check func(st *fakeState) error) {
   128  	st.checkMu.Lock()
   129  	st.check = check
   130  	st.checkMu.Unlock()
   131  }
   132  
   133  func (st *fakeState) checkInvariants() {
   134  	check := st.getCheck()
   135  	if check == nil {
   136  		return
   137  	}
   138  	if err := check(st); err != nil {
   139  		// Force a panic, otherwise we can deadlock
   140  		// when called from within the worker.
   141  		go panic(err)
   142  		select {}
   143  	}
   144  }
   145  
   146  // checkInvariants checks that all the expected invariants
   147  // in the state hold true. Currently we check that:
   148  // - total number of votes is odd.
   149  // - member voting status implies that machine has vote.
   150  func checkInvariants(st *fakeState) error {
   151  	st.mu.Lock()
   152  	defer st.mu.Unlock()
   153  	members := st.session.members.Get().([]replicaset.Member)
   154  	voteCount := 0
   155  	for _, m := range members {
   156  		votes := 1
   157  		if m.Votes != nil {
   158  			votes = *m.Votes
   159  		}
   160  		voteCount += votes
   161  		if id, ok := m.Tags[jujuMachineKey]; ok {
   162  			if votes > 0 {
   163  				m := st.machines[id]
   164  				if m == nil {
   165  					return fmt.Errorf("voting member with machine id %q has no associated Machine", id)
   166  				}
   167  
   168  				if !m.doc().hasVote {
   169  					return fmt.Errorf("machine %q should be marked as having the vote, but does not", id)
   170  				}
   171  			}
   172  		}
   173  	}
   174  	if voteCount%2 != 1 {
   175  		return fmt.Errorf("total vote count is not odd (got %d)", voteCount)
   176  	}
   177  	return nil
   178  }
   179  
   180  type invariantChecker interface {
   181  	checkInvariants()
   182  }
   183  
   184  // machine is similar to Machine except that
   185  // it bypasses the error mocking machinery.
   186  // It returns nil if there is no machine with the
   187  // given id.
   188  func (st *fakeState) machine(id string) *fakeMachine {
   189  	st.mu.Lock()
   190  	defer st.mu.Unlock()
   191  	return st.machines[id]
   192  }
   193  
   194  func (st *fakeState) Machine(id string) (Machine, error) {
   195  	if err := st.errors.errorFor("State.Machine", id); err != nil {
   196  		return nil, err
   197  	}
   198  	if m := st.machine(id); m != nil {
   199  		return m, nil
   200  	}
   201  	return nil, errors.NotFoundf("machine %s", id)
   202  }
   203  
   204  func (st *fakeState) addMachine(id string, wantsVote bool) *fakeMachine {
   205  	st.mu.Lock()
   206  	defer st.mu.Unlock()
   207  	logger.Infof("fakeState.addMachine %q", id)
   208  	if st.machines[id] != nil {
   209  		panic(fmt.Errorf("id %q already used", id))
   210  	}
   211  	doc := machineDoc{
   212  		id:         id,
   213  		wantsVote:  wantsVote,
   214  		statusInfo: status.StatusInfo{Status: status.Started},
   215  		life:       state.Alive,
   216  	}
   217  	m := &fakeMachine{
   218  		errors:  &st.errors,
   219  		checker: st,
   220  	}
   221  	st.machines[id] = m
   222  	m.val.Set(doc)
   223  	return m
   224  }
   225  
   226  func (st *fakeState) removeMachine(id string) {
   227  	st.mu.Lock()
   228  	defer st.mu.Unlock()
   229  	if st.machines[id] == nil {
   230  		panic(fmt.Errorf("removing non-existent machine %q", id))
   231  	}
   232  	delete(st.machines, id)
   233  }
   234  
   235  func (st *fakeState) setControllers(ids ...string) {
   236  	st.controllers.Set(&state.ControllerInfo{
   237  		MachineIds: ids,
   238  	})
   239  }
   240  
   241  func (st *fakeState) ControllerInfo() (*state.ControllerInfo, error) {
   242  	if err := st.errors.errorFor("State.ControllerInfo"); err != nil {
   243  		return nil, err
   244  	}
   245  	return deepCopy(st.controllers.Get()).(*state.ControllerInfo), nil
   246  }
   247  
   248  func (st *fakeState) WatchControllerInfo() state.NotifyWatcher {
   249  	return WatchValue(&st.controllers)
   250  }
   251  
   252  func (st *fakeState) WatchControllerStatusChanges() state.StringsWatcher {
   253  	return WatchStrings(&st.statuses)
   254  }
   255  
   256  func (st *fakeState) WatchControllerConfig() state.NotifyWatcher {
   257  	return WatchValue(&st.controllerConfig)
   258  }
   259  
   260  func (st *fakeState) ModelConfig() (*config.Config, error) {
   261  	attrs := coretesting.FakeConfig()
   262  	cfg, err := config.New(config.NoDefaults, attrs)
   263  	return cfg, err
   264  }
   265  
   266  func (st *fakeState) ControllerConfig() (controller.Config, error) {
   267  	st.mu.Lock()
   268  	defer st.mu.Unlock()
   269  
   270  	if err := st.errors.errorFor("State.ControllerConfig"); err != nil {
   271  		return nil, err
   272  	}
   273  	return deepCopy(st.controllerConfig.Get()).(controller.Config), nil
   274  }
   275  
   276  func (st *fakeState) RemoveControllerMachine(m Machine) error {
   277  	st.mu.Lock()
   278  	defer st.mu.Unlock()
   279  	controllerInfo := st.controllers.Get().(*state.ControllerInfo)
   280  	machineIds := controllerInfo.MachineIds
   281  	var newMachineIds []string
   282  	machineId := m.Id()
   283  	for _, id := range machineIds {
   284  		if id == machineId {
   285  			continue
   286  		}
   287  		newMachineIds = append(newMachineIds, id)
   288  	}
   289  	st.setControllers(newMachineIds...)
   290  	return nil
   291  }
   292  
   293  func (st *fakeState) setHASpace(spaceName string) {
   294  	st.mu.Lock()
   295  	defer st.mu.Unlock()
   296  
   297  	cfg := st.controllerConfig.Get().(controller.Config)
   298  	cfg[controller.JujuHASpace] = spaceName
   299  	st.controllerConfig.Set(cfg)
   300  }
   301  
   302  type fakeMachine struct {
   303  	mu      sync.Mutex
   304  	errors  *errorPatterns
   305  	val     voyeur.Value // of machineDoc
   306  	checker invariantChecker
   307  }
   308  
   309  type machineDoc struct {
   310  	id         string
   311  	wantsVote  bool
   312  	hasVote    bool
   313  	instanceId instance.Id
   314  	addresses  []network.Address
   315  	statusInfo status.StatusInfo
   316  	life       state.Life
   317  }
   318  
   319  func (m *fakeMachine) doc() machineDoc {
   320  	return m.val.Get().(machineDoc)
   321  }
   322  
   323  func (m *fakeMachine) Refresh() error {
   324  	m.mu.Lock()
   325  	defer m.mu.Unlock()
   326  	doc := m.doc()
   327  	if err := m.errors.errorFor("Machine.Refresh", doc.id); err != nil {
   328  		return err
   329  	}
   330  	return nil
   331  }
   332  
   333  func (m *fakeMachine) GoString() string {
   334  	m.mu.Lock()
   335  	defer m.mu.Unlock()
   336  	return fmt.Sprintf("&fakeMachine{%#v}", m.doc())
   337  }
   338  
   339  func (m *fakeMachine) Id() string {
   340  	m.mu.Lock()
   341  	defer m.mu.Unlock()
   342  	return m.doc().id
   343  }
   344  
   345  func (m *fakeMachine) Life() state.Life {
   346  	m.mu.Lock()
   347  	defer m.mu.Unlock()
   348  	return m.doc().life
   349  }
   350  
   351  func (m *fakeMachine) Watch() state.NotifyWatcher {
   352  	m.mu.Lock()
   353  	defer m.mu.Unlock()
   354  	return WatchValue(&m.val)
   355  }
   356  
   357  func (m *fakeMachine) WantsVote() bool {
   358  	m.mu.Lock()
   359  	defer m.mu.Unlock()
   360  	return m.doc().wantsVote
   361  }
   362  
   363  func (m *fakeMachine) HasVote() bool {
   364  	m.mu.Lock()
   365  	defer m.mu.Unlock()
   366  	return m.doc().hasVote
   367  }
   368  
   369  func (m *fakeMachine) Addresses() []network.Address {
   370  	m.mu.Lock()
   371  	defer m.mu.Unlock()
   372  	return m.doc().addresses
   373  }
   374  
   375  func (m *fakeMachine) Status() (status.StatusInfo, error) {
   376  	m.mu.Lock()
   377  	defer m.mu.Unlock()
   378  	return m.doc().statusInfo, nil
   379  }
   380  
   381  func (m *fakeMachine) SetStatus(sInfo status.StatusInfo) error {
   382  	m.mutate(func(doc *machineDoc) {
   383  		doc.statusInfo = sInfo
   384  	})
   385  	return nil
   386  }
   387  
   388  // mutate atomically changes the machineDoc of
   389  // the receiver by mutating it with the provided function.
   390  func (m *fakeMachine) mutate(f func(*machineDoc)) {
   391  	m.mu.Lock()
   392  	doc := m.doc()
   393  	f(&doc)
   394  	m.val.Set(doc)
   395  	m.checker.checkInvariants()
   396  	m.mu.Unlock()
   397  }
   398  
   399  func (m *fakeMachine) setAddresses(addrs ...network.Address) {
   400  	m.mutate(func(doc *machineDoc) {
   401  		doc.addresses = addrs
   402  	})
   403  }
   404  
   405  // SetHasVote implements Machine.SetHasVote.
   406  func (m *fakeMachine) SetHasVote(hasVote bool) error {
   407  	doc := m.doc()
   408  	if err := m.errors.errorFor("Machine.SetHasVote", doc.id, hasVote); err != nil {
   409  		return err
   410  	}
   411  	m.mutate(func(doc *machineDoc) {
   412  		doc.hasVote = hasVote
   413  	})
   414  	return nil
   415  }
   416  
   417  func (m *fakeMachine) setWantsVote(wantsVote bool) {
   418  	m.mutate(func(doc *machineDoc) {
   419  		doc.wantsVote = wantsVote
   420  	})
   421  }
   422  
   423  func (m *fakeMachine) advanceLifecycle(life state.Life, wantsVote bool) {
   424  	m.mutate(func(doc *machineDoc) {
   425  		doc.life = life
   426  		doc.wantsVote = wantsVote
   427  	})
   428  }
   429  
   430  type fakeMongoSession struct {
   431  	// If InstantlyReady is true, replica status of
   432  	// all members will be instantly reported as ready.
   433  	InstantlyReady bool
   434  
   435  	errors  *errorPatterns
   436  	checker invariantChecker
   437  	members voyeur.Value // of []replicaset.Member
   438  	status  voyeur.Value // of *replicaset.Status
   439  }
   440  
   441  // newFakeMongoSession returns a mock implementation of mongoSession.
   442  func newFakeMongoSession(checker invariantChecker, errors *errorPatterns) *fakeMongoSession {
   443  	s := new(fakeMongoSession)
   444  	s.checker = checker
   445  	s.errors = errors
   446  	return s
   447  }
   448  
   449  // CurrentMembers implements mongoSession.CurrentMembers.
   450  func (session *fakeMongoSession) CurrentMembers() ([]replicaset.Member, error) {
   451  	if err := session.errors.errorFor("Session.CurrentMembers"); err != nil {
   452  		return nil, err
   453  	}
   454  	return deepCopy(session.members.Get()).([]replicaset.Member), nil
   455  }
   456  
   457  // CurrentStatus implements mongoSession.CurrentStatus.
   458  func (session *fakeMongoSession) CurrentStatus() (*replicaset.Status, error) {
   459  	if err := session.errors.errorFor("Session.CurrentStatus"); err != nil {
   460  		return nil, err
   461  	}
   462  	return deepCopy(session.status.Get()).(*replicaset.Status), nil
   463  }
   464  
   465  // setStatus sets the status of the current members of the session.
   466  func (session *fakeMongoSession) setStatus(members []replicaset.MemberStatus) {
   467  	session.status.Set(deepCopy(&replicaset.Status{
   468  		Members: members,
   469  	}))
   470  }
   471  
   472  // Set implements mongoSession.Set
   473  func (session *fakeMongoSession) Set(members []replicaset.Member) error {
   474  	if err := session.errors.errorFor("Session.Set"); err != nil {
   475  		logger.Infof("NOT setting replicaset members to \n%s", prettyReplicaSetMembersSlice(members))
   476  		return err
   477  	}
   478  	logger.Infof("setting replicaset members to \n%s", prettyReplicaSetMembersSlice(members))
   479  	session.members.Set(deepCopy(members))
   480  	if session.InstantlyReady {
   481  		statuses := make([]replicaset.MemberStatus, len(members))
   482  		for i, m := range members {
   483  			statuses[i] = replicaset.MemberStatus{
   484  				Id:      m.Id,
   485  				Address: m.Address,
   486  				Healthy: true,
   487  				State:   replicaset.SecondaryState,
   488  			}
   489  			if i == 0 {
   490  				statuses[i].State = replicaset.PrimaryState
   491  			}
   492  		}
   493  		session.setStatus(statuses)
   494  	}
   495  	session.checker.checkInvariants()
   496  	return nil
   497  }
   498  
   499  func (session *fakeMongoSession) StepDownPrimary() error {
   500  	if err := session.errors.errorFor("Session.StepDownPrimary"); err != nil {
   501  		logger.Debugf("StepDownPrimary error: %v", err)
   502  		return err
   503  	}
   504  	logger.Debugf("StepDownPrimary")
   505  	status := session.status.Get().(*replicaset.Status)
   506  	members := session.members.Get().([]replicaset.Member)
   507  	membersById := make(map[int]replicaset.Member, len(members))
   508  	for _, member := range members {
   509  		membersById[member.Id] = member
   510  	}
   511  	// We use a simple algorithm, find the primary, and all the secondaries that don't have 0 priority. And then pick a
   512  	// random secondary and swap their states
   513  	primaryIndex := -1
   514  	secondaryIndexes := []int{}
   515  	var info []string
   516  	for i, statusMember := range status.Members {
   517  		if statusMember.State == replicaset.PrimaryState {
   518  			primaryIndex = i
   519  			info = append(info, fmt.Sprintf("%d: current primary", statusMember.Id))
   520  		} else if statusMember.State == replicaset.SecondaryState {
   521  			confMember := membersById[statusMember.Id]
   522  			if confMember.Priority == nil || *confMember.Priority > 0 {
   523  				secondaryIndexes = append(secondaryIndexes, i)
   524  				info = append(info, fmt.Sprintf("%d: eligible secondary", statusMember.Id))
   525  			} else {
   526  				info = append(info, fmt.Sprintf("%d: ineligible secondary", statusMember.Id))
   527  			}
   528  		}
   529  	}
   530  	if primaryIndex == -1 {
   531  		return errors.Errorf("no primary to step down, broken config?")
   532  	}
   533  	if len(secondaryIndexes) < 1 {
   534  		return errors.Errorf("no secondaries to switch to")
   535  	}
   536  	secondaryIndex := secondaryIndexes[rand.Intn(len(secondaryIndexes))]
   537  	status.Members[primaryIndex].State = replicaset.SecondaryState
   538  	status.Members[secondaryIndex].State = replicaset.PrimaryState
   539  	logger.Debugf("StepDownPrimary nominated %d to be the new primary from: %v",
   540  		status.Members[secondaryIndex].Id, info)
   541  	session.setStatus(status.Members)
   542  	return nil
   543  }
   544  
   545  func (session *fakeMongoSession) Refresh() {
   546  	// If this was a testing.Stub we would track that Refresh was called.
   547  }
   548  
   549  // prettyReplicaSetMembersSlice wraps prettyReplicaSetMembers for testing
   550  // purposes only.
   551  func prettyReplicaSetMembersSlice(members []replicaset.Member) string {
   552  	vrm := make(map[string]*replicaset.Member, len(members))
   553  	for i := range members {
   554  		m := members[i]
   555  		vrm[strconv.Itoa(m.Id)] = &m
   556  	}
   557  	return prettyReplicaSetMembers(vrm)
   558  }
   559  
   560  // deepCopy makes a deep copy of any type by marshalling
   561  // it as JSON, then unmarshalling it.
   562  func deepCopy(x interface{}) interface{} {
   563  	v := reflect.ValueOf(x)
   564  	data, err := json.Marshal(x)
   565  	if err != nil {
   566  		panic(fmt.Errorf("cannot marshal %#v: %v", x, err))
   567  	}
   568  	newv := reflect.New(v.Type())
   569  	if err := json.Unmarshal(data, newv.Interface()); err != nil {
   570  		panic(fmt.Errorf("cannot unmarshal %q into %s", data, newv.Type()))
   571  	}
   572  	// sanity check
   573  	newx := newv.Elem().Interface()
   574  	if !reflect.DeepEqual(newx, x) {
   575  		panic(fmt.Errorf("value not deep-copied correctly"))
   576  	}
   577  	return newx
   578  }
   579  
   580  type notifier struct {
   581  	tomb    tomb.Tomb
   582  	w       *voyeur.Watcher
   583  	changes chan struct{}
   584  }
   585  
   586  func (n *notifier) loop() {
   587  	for n.w.Next() {
   588  		select {
   589  		case n.changes <- struct{}{}:
   590  		case <-n.tomb.Dying():
   591  		}
   592  	}
   593  }
   594  
   595  // WatchValue returns a NotifyWatcher that triggers
   596  // when the given value changes. Its Wait and Err methods
   597  // never return a non-nil error.
   598  func WatchValue(val *voyeur.Value) state.NotifyWatcher {
   599  	n := &notifier{
   600  		w:       val.Watch(),
   601  		changes: make(chan struct{}),
   602  	}
   603  	n.tomb.Go(func() error {
   604  		n.loop()
   605  		return nil
   606  	})
   607  	return n
   608  }
   609  
   610  // Changes returns a channel that sends a value when the value changes.
   611  // The value itself can be retrieved by calling the value's Get method.
   612  func (n *notifier) Changes() <-chan struct{} {
   613  	return n.changes
   614  }
   615  
   616  // Kill stops the notifier but does not wait for it to finish.
   617  func (n *notifier) Kill() {
   618  	n.tomb.Kill(nil)
   619  	n.w.Close()
   620  }
   621  
   622  func (n *notifier) Err() error {
   623  	return n.tomb.Err()
   624  }
   625  
   626  // Wait waits for the notifier to finish. It always returns nil.
   627  func (n *notifier) Wait() error {
   628  	return n.tomb.Wait()
   629  }
   630  
   631  func (n *notifier) Stop() error {
   632  	return worker.Stop(n)
   633  }
   634  
   635  type stringsNotifier struct {
   636  	tomb    tomb.Tomb
   637  	w       *voyeur.Watcher
   638  	changes chan []string
   639  }
   640  
   641  // WatchStrings returns a StringsWatcher that triggers
   642  // when the given value changes. Its Wait and Err methods
   643  // never return a non-nil error.
   644  func WatchStrings(val *voyeur.Value) state.StringsWatcher {
   645  	n := &stringsNotifier{
   646  		w:       val.Watch(),
   647  		changes: make(chan []string),
   648  	}
   649  	n.tomb.Go(func() error {
   650  		n.loop()
   651  		return nil
   652  	})
   653  	return n
   654  }
   655  
   656  func (n *stringsNotifier) loop() {
   657  	for n.w.Next() {
   658  		select {
   659  		case n.changes <- []string{}:
   660  		case <-n.tomb.Dying():
   661  		}
   662  	}
   663  }
   664  
   665  // Changes returns a channel that sends a value when the value changes.
   666  // The value itself can be retrieved by calling the value's Get method.
   667  func (n *stringsNotifier) Changes() <-chan []string {
   668  	return n.changes
   669  }
   670  
   671  // Kill stops the notifier but does not wait for it to finish.
   672  func (n *stringsNotifier) Kill() {
   673  	n.tomb.Kill(nil)
   674  	n.w.Close()
   675  }
   676  
   677  func (n *stringsNotifier) Err() error {
   678  	return n.tomb.Err()
   679  }
   680  
   681  // Wait waits for the notifier to finish. It always returns nil.
   682  func (n *stringsNotifier) Wait() error {
   683  	return n.tomb.Wait()
   684  }
   685  
   686  func (n *stringsNotifier) Stop() error {
   687  	return worker.Stop(n)
   688  }