github.com/sl1pm4t/consul@v1.4.5-0.20190325224627-74c31c540f9c/agent/ae/ae_test.go (about)

     1  package ae
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log"
     7  	"os"
     8  	"reflect"
     9  	"sync"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/hashicorp/consul/lib"
    14  	"github.com/stretchr/testify/assert"
    15  )
    16  
    17  func TestAE_scaleFactor(t *testing.T) {
    18  	t.Parallel()
    19  	tests := []struct {
    20  		nodes int
    21  		scale int
    22  	}{
    23  		{100, 1},
    24  		{200, 2},
    25  		{1000, 4},
    26  		{10000, 8},
    27  	}
    28  	for _, tt := range tests {
    29  		t.Run(fmt.Sprintf("%d nodes", tt.nodes), func(t *testing.T) {
    30  			if got, want := scaleFactor(tt.nodes), tt.scale; got != want {
    31  				t.Fatalf("got scale factor %d want %d", got, want)
    32  			}
    33  		})
    34  	}
    35  }
    36  
    37  func TestAE_Pause_nestedPauseResume(t *testing.T) {
    38  	t.Parallel()
    39  	l := NewStateSyncer(nil, 0, nil, nil)
    40  	if l.Paused() != false {
    41  		t.Fatal("syncer should be unPaused after init")
    42  	}
    43  	l.Pause()
    44  	if l.Paused() != true {
    45  		t.Fatal("syncer should be Paused after first call to Pause()")
    46  	}
    47  	l.Pause()
    48  	if l.Paused() != true {
    49  		t.Fatal("syncer should STILL be Paused after second call to Pause()")
    50  	}
    51  	gotR := l.Resume()
    52  	if l.Paused() != true {
    53  		t.Fatal("syncer should STILL be Paused after FIRST call to Resume()")
    54  	}
    55  	assert.False(t, gotR)
    56  	gotR = l.Resume()
    57  	if l.Paused() != false {
    58  		t.Fatal("syncer should NOT be Paused after SECOND call to Resume()")
    59  	}
    60  	assert.True(t, gotR)
    61  
    62  	defer func() {
    63  		err := recover()
    64  		if err == nil {
    65  			t.Fatal("unbalanced Resume() should panic")
    66  		}
    67  	}()
    68  	l.Resume()
    69  }
    70  
    71  func TestAE_Pause_ResumeTriggersSyncChanges(t *testing.T) {
    72  	l := NewStateSyncer(nil, 0, nil, nil)
    73  	l.Pause()
    74  	l.Resume()
    75  	select {
    76  	case <-l.SyncChanges.Notif():
    77  		// expected
    78  	case <-l.SyncFull.Notif():
    79  		t.Fatal("resume triggered SyncFull instead of SyncChanges")
    80  	default:
    81  		t.Fatal("resume did not trigger SyncFull")
    82  	}
    83  }
    84  
    85  func TestAE_staggerDependsOnClusterSize(t *testing.T) {
    86  	libRandomStagger = func(d time.Duration) time.Duration { return d }
    87  	defer func() { libRandomStagger = lib.RandomStagger }()
    88  
    89  	l := testSyncer()
    90  	if got, want := l.staggerFn(10*time.Millisecond), 10*time.Millisecond; got != want {
    91  		t.Fatalf("got %v want %v", got, want)
    92  	}
    93  	l.ClusterSize = func() int { return 256 }
    94  	if got, want := l.staggerFn(10*time.Millisecond), 20*time.Millisecond; got != want {
    95  		t.Fatalf("got %v want %v", got, want)
    96  	}
    97  }
    98  
    99  func TestAE_Run_SyncFullBeforeChanges(t *testing.T) {
   100  	shutdownCh := make(chan struct{})
   101  	state := &mock{
   102  		syncChanges: func() error {
   103  			close(shutdownCh)
   104  			return nil
   105  		},
   106  	}
   107  
   108  	// indicate that we have partial changes before starting Run
   109  	l := testSyncer()
   110  	l.State = state
   111  	l.ShutdownCh = shutdownCh
   112  	l.SyncChanges.Trigger()
   113  
   114  	var wg sync.WaitGroup
   115  	wg.Add(1)
   116  	go func() {
   117  		defer wg.Done()
   118  		l.Run()
   119  	}()
   120  	wg.Wait()
   121  
   122  	if got, want := state.seq, []string{"full", "changes"}; !reflect.DeepEqual(got, want) {
   123  		t.Fatalf("got call sequence %v want %v", got, want)
   124  	}
   125  }
   126  
   127  func TestAE_Run_Quit(t *testing.T) {
   128  	t.Run("Run panics without ClusterSize", func(t *testing.T) {
   129  		defer func() {
   130  			err := recover()
   131  			if err == nil {
   132  				t.Fatal("Run should panic")
   133  			}
   134  		}()
   135  		l := testSyncer()
   136  		l.ClusterSize = nil
   137  		l.Run()
   138  	})
   139  	t.Run("runFSM quits", func(t *testing.T) {
   140  		// start timer which explodes if runFSM does not quit
   141  		tm := time.AfterFunc(time.Second, func() { panic("timeout") })
   142  
   143  		l := testSyncer()
   144  		l.runFSM(fullSyncState, func(fsmState) fsmState { return doneState })
   145  		// should just quit
   146  		tm.Stop()
   147  	})
   148  }
   149  
   150  func TestAE_FSM(t *testing.T) {
   151  	t.Run("fullSyncState", func(t *testing.T) {
   152  		t.Run("Paused -> retryFullSyncState", func(t *testing.T) {
   153  			l := testSyncer()
   154  			l.Pause()
   155  			fs := l.nextFSMState(fullSyncState)
   156  			if got, want := fs, retryFullSyncState; got != want {
   157  				t.Fatalf("got state %v want %v", got, want)
   158  			}
   159  		})
   160  		t.Run("SyncFull() error -> retryFullSyncState", func(t *testing.T) {
   161  			l := testSyncer()
   162  			l.State = &mock{syncFull: func() error { return errors.New("boom") }}
   163  			fs := l.nextFSMState(fullSyncState)
   164  			if got, want := fs, retryFullSyncState; got != want {
   165  				t.Fatalf("got state %v want %v", got, want)
   166  			}
   167  		})
   168  		t.Run("SyncFull() OK -> partialSyncState", func(t *testing.T) {
   169  			l := testSyncer()
   170  			l.State = &mock{}
   171  			fs := l.nextFSMState(fullSyncState)
   172  			if got, want := fs, partialSyncState; got != want {
   173  				t.Fatalf("got state %v want %v", got, want)
   174  			}
   175  		})
   176  	})
   177  
   178  	t.Run("retryFullSyncState", func(t *testing.T) {
   179  		// helper for testing state transitions from retrySyncFullState
   180  		test := func(ev event, to fsmState) {
   181  			l := testSyncer()
   182  			l.retrySyncFullEvent = func() event { return ev }
   183  			fs := l.nextFSMState(retryFullSyncState)
   184  			if got, want := fs, to; got != want {
   185  				t.Fatalf("got state %v want %v", got, want)
   186  			}
   187  		}
   188  		t.Run("shutdownEvent -> doneState", func(t *testing.T) {
   189  			test(shutdownEvent, doneState)
   190  		})
   191  		t.Run("syncFullNotifEvent -> fullSyncState", func(t *testing.T) {
   192  			test(syncFullNotifEvent, fullSyncState)
   193  		})
   194  		t.Run("syncFullTimerEvent -> fullSyncState", func(t *testing.T) {
   195  			test(syncFullTimerEvent, fullSyncState)
   196  		})
   197  		t.Run("invalid event -> panic ", func(t *testing.T) {
   198  			defer func() {
   199  				err := recover()
   200  				if err == nil {
   201  					t.Fatal("invalid event should panic")
   202  				}
   203  			}()
   204  			test(event("invalid"), fsmState(""))
   205  		})
   206  	})
   207  
   208  	t.Run("partialSyncState", func(t *testing.T) {
   209  		// helper for testing state transitions from partialSyncState
   210  		test := func(ev event, to fsmState) {
   211  			l := testSyncer()
   212  			l.syncChangesEvent = func() event { return ev }
   213  			fs := l.nextFSMState(partialSyncState)
   214  			if got, want := fs, to; got != want {
   215  				t.Fatalf("got state %v want %v", got, want)
   216  			}
   217  		}
   218  		t.Run("shutdownEvent -> doneState", func(t *testing.T) {
   219  			test(shutdownEvent, doneState)
   220  		})
   221  		t.Run("syncFullNotifEvent -> fullSyncState", func(t *testing.T) {
   222  			test(syncFullNotifEvent, fullSyncState)
   223  		})
   224  		t.Run("syncFullTimerEvent -> fullSyncState", func(t *testing.T) {
   225  			test(syncFullTimerEvent, fullSyncState)
   226  		})
   227  		t.Run("syncChangesEvent+Paused -> partialSyncState", func(t *testing.T) {
   228  			l := testSyncer()
   229  			l.Pause()
   230  			l.syncChangesEvent = func() event { return syncChangesNotifEvent }
   231  			fs := l.nextFSMState(partialSyncState)
   232  			if got, want := fs, partialSyncState; got != want {
   233  				t.Fatalf("got state %v want %v", got, want)
   234  			}
   235  		})
   236  		t.Run("syncChangesEvent+SyncChanges() error -> partialSyncState", func(t *testing.T) {
   237  			l := testSyncer()
   238  			l.State = &mock{syncChanges: func() error { return errors.New("boom") }}
   239  			l.syncChangesEvent = func() event { return syncChangesNotifEvent }
   240  			fs := l.nextFSMState(partialSyncState)
   241  			if got, want := fs, partialSyncState; got != want {
   242  				t.Fatalf("got state %v want %v", got, want)
   243  			}
   244  		})
   245  		t.Run("syncChangesEvent+SyncChanges() OK -> partialSyncState", func(t *testing.T) {
   246  			l := testSyncer()
   247  			l.State = &mock{}
   248  			l.syncChangesEvent = func() event { return syncChangesNotifEvent }
   249  			fs := l.nextFSMState(partialSyncState)
   250  			if got, want := fs, partialSyncState; got != want {
   251  				t.Fatalf("got state %v want %v", got, want)
   252  			}
   253  		})
   254  		t.Run("invalid event -> panic ", func(t *testing.T) {
   255  			defer func() {
   256  				err := recover()
   257  				if err == nil {
   258  					t.Fatal("invalid event should panic")
   259  				}
   260  			}()
   261  			test(event("invalid"), fsmState(""))
   262  		})
   263  	})
   264  	t.Run("invalid state -> panic ", func(t *testing.T) {
   265  		defer func() {
   266  			err := recover()
   267  			if err == nil {
   268  				t.Fatal("invalid state should panic")
   269  			}
   270  		}()
   271  		l := testSyncer()
   272  		l.nextFSMState(fsmState("invalid"))
   273  	})
   274  }
   275  
   276  func TestAE_RetrySyncFullEvent(t *testing.T) {
   277  	t.Run("trigger shutdownEvent", func(t *testing.T) {
   278  		l := testSyncer()
   279  		l.ShutdownCh = make(chan struct{})
   280  		evch := make(chan event)
   281  		go func() { evch <- l.retrySyncFullEvent() }()
   282  		close(l.ShutdownCh)
   283  		if got, want := <-evch, shutdownEvent; got != want {
   284  			t.Fatalf("got event %q want %q", got, want)
   285  		}
   286  	})
   287  	t.Run("trigger shutdownEvent during FullNotif", func(t *testing.T) {
   288  		l := testSyncer()
   289  		l.ShutdownCh = make(chan struct{})
   290  		evch := make(chan event)
   291  		go func() { evch <- l.retrySyncFullEvent() }()
   292  		l.SyncFull.Trigger()
   293  		time.Sleep(100 * time.Millisecond)
   294  		close(l.ShutdownCh)
   295  		if got, want := <-evch, shutdownEvent; got != want {
   296  			t.Fatalf("got event %q want %q", got, want)
   297  		}
   298  	})
   299  	t.Run("trigger syncFullNotifEvent", func(t *testing.T) {
   300  		l := testSyncer()
   301  		l.serverUpInterval = 10 * time.Millisecond
   302  		evch := make(chan event)
   303  		go func() { evch <- l.retrySyncFullEvent() }()
   304  		l.SyncFull.Trigger()
   305  		if got, want := <-evch, syncFullNotifEvent; got != want {
   306  			t.Fatalf("got event %q want %q", got, want)
   307  		}
   308  	})
   309  	t.Run("trigger syncFullTimerEvent", func(t *testing.T) {
   310  		l := testSyncer()
   311  		l.retryFailInterval = 10 * time.Millisecond
   312  		evch := make(chan event)
   313  		go func() { evch <- l.retrySyncFullEvent() }()
   314  		if got, want := <-evch, syncFullTimerEvent; got != want {
   315  			t.Fatalf("got event %q want %q", got, want)
   316  		}
   317  	})
   318  }
   319  
   320  func TestAE_SyncChangesEvent(t *testing.T) {
   321  	t.Run("trigger shutdownEvent", func(t *testing.T) {
   322  		l := testSyncer()
   323  		l.ShutdownCh = make(chan struct{})
   324  		evch := make(chan event)
   325  		go func() { evch <- l.syncChangesEvent() }()
   326  		close(l.ShutdownCh)
   327  		if got, want := <-evch, shutdownEvent; got != want {
   328  			t.Fatalf("got event %q want %q", got, want)
   329  		}
   330  	})
   331  	t.Run("trigger shutdownEvent during FullNotif", func(t *testing.T) {
   332  		l := testSyncer()
   333  		l.ShutdownCh = make(chan struct{})
   334  		evch := make(chan event)
   335  		go func() { evch <- l.syncChangesEvent() }()
   336  		l.SyncFull.Trigger()
   337  		time.Sleep(100 * time.Millisecond)
   338  		close(l.ShutdownCh)
   339  		if got, want := <-evch, shutdownEvent; got != want {
   340  			t.Fatalf("got event %q want %q", got, want)
   341  		}
   342  	})
   343  	t.Run("trigger syncFullNotifEvent", func(t *testing.T) {
   344  		l := testSyncer()
   345  		l.serverUpInterval = 10 * time.Millisecond
   346  		evch := make(chan event)
   347  		go func() { evch <- l.syncChangesEvent() }()
   348  		l.SyncFull.Trigger()
   349  		if got, want := <-evch, syncFullNotifEvent; got != want {
   350  			t.Fatalf("got event %q want %q", got, want)
   351  		}
   352  	})
   353  	t.Run("trigger syncFullTimerEvent", func(t *testing.T) {
   354  		l := testSyncer()
   355  		l.Interval = 10 * time.Millisecond
   356  		evch := make(chan event)
   357  		go func() { evch <- l.syncChangesEvent() }()
   358  		if got, want := <-evch, syncFullTimerEvent; got != want {
   359  			t.Fatalf("got event %q want %q", got, want)
   360  		}
   361  	})
   362  	t.Run("trigger syncChangesNotifEvent", func(t *testing.T) {
   363  		l := testSyncer()
   364  		evch := make(chan event)
   365  		go func() { evch <- l.syncChangesEvent() }()
   366  		l.SyncChanges.Trigger()
   367  		if got, want := <-evch, syncChangesNotifEvent; got != want {
   368  			t.Fatalf("got event %q want %q", got, want)
   369  		}
   370  	})
   371  }
   372  
   373  type mock struct {
   374  	seq                   []string
   375  	syncFull, syncChanges func() error
   376  }
   377  
   378  func (m *mock) SyncFull() error {
   379  	m.seq = append(m.seq, "full")
   380  	if m.syncFull != nil {
   381  		return m.syncFull()
   382  	}
   383  	return nil
   384  }
   385  
   386  func (m *mock) SyncChanges() error {
   387  	m.seq = append(m.seq, "changes")
   388  	if m.syncChanges != nil {
   389  		return m.syncChanges()
   390  	}
   391  	return nil
   392  }
   393  
   394  func testSyncer() *StateSyncer {
   395  	logger := log.New(os.Stderr, "", 0)
   396  	l := NewStateSyncer(nil, time.Second, nil, logger)
   397  	l.stagger = func(d time.Duration) time.Duration { return d }
   398  	l.ClusterSize = func() int { return 1 }
   399  	return l
   400  }