netbird/management/server/scheduler_test.go
Misha Bragin f984b8a091
Proactively expire peers' login per account (#698)
Goals:

Enable peer login expiration when adding new peer
Expire peer's login when the time comes
The account manager triggers peer expiration routine in future if the
following conditions are true:

peer expiration is enabled for the account
there is at least one peer that has expiration enabled and is connected
The time of the next expiration check is based on the nearest peer expiration.
Account manager finds a peer with the oldest last login (auth) timestamp and
calculates the time when it has to run the routine as a sum of the configured
peer login expiration duration and the peer's last login time.

When triggered, the expiration routine checks whether there are expired peers.
The management server closes the update channel of these peers and updates
network map of other peers to exclude expired peers so that the expired peers
are not able to connect anywhere.

The account manager can reschedule or cancel peer expiration in the following cases:

when admin changes account setting (peer expiration enable/disable)
when admin updates the expiration duration of the account
when admin updates peer expiration (enable/disable)
when peer connects (Sync)
P.S. The network map calculation was updated to exclude peers that have login expired.
2023-02-27 16:44:26 +01:00

95 lines
2.2 KiB
Go

package server
import (
"fmt"
"github.com/stretchr/testify/assert"
"math/rand"
"sync"
"testing"
"time"
)
func TestScheduler_Performance(t *testing.T) {
scheduler := NewDefaultScheduler()
n := 500
wg := &sync.WaitGroup{}
wg.Add(n)
maxMs := 500
minMs := 50
for i := 0; i < n; i++ {
millis := time.Duration(rand.Intn(maxMs-minMs)+minMs) * time.Millisecond
go scheduler.Schedule(millis, fmt.Sprintf("test-scheduler-job-%d", i), func() (nextRunIn time.Duration, reschedule bool) {
time.Sleep(millis)
wg.Done()
return 0, false
})
}
failed := waitTimeout(wg, 3*time.Second)
if failed {
t.Fatal("timed out while waiting for test to finish")
return
}
assert.Len(t, scheduler.jobs, 0)
}
func TestScheduler_Cancel(t *testing.T) {
jobID1 := "test-scheduler-job-1"
jobID2 := "test-scheduler-job-2"
scheduler := NewDefaultScheduler()
scheduler.Schedule(2*time.Second, jobID1, func() (nextRunIn time.Duration, reschedule bool) {
return 0, false
})
scheduler.Schedule(2*time.Second, jobID2, func() (nextRunIn time.Duration, reschedule bool) {
return 0, false
})
assert.Len(t, scheduler.jobs, 2)
scheduler.Cancel([]string{jobID1})
assert.Len(t, scheduler.jobs, 1)
assert.NotNil(t, scheduler.jobs[jobID2])
}
func TestScheduler_Schedule(t *testing.T) {
jobID := "test-scheduler-job-1"
scheduler := NewDefaultScheduler()
wg := &sync.WaitGroup{}
wg.Add(1)
// job without reschedule should be triggered once
job := func() (nextRunIn time.Duration, reschedule bool) {
wg.Done()
return 0, false
}
scheduler.Schedule(300*time.Millisecond, jobID, job)
failed := waitTimeout(wg, time.Second)
if failed {
t.Fatal("timed out while waiting for test to finish")
return
}
// job with reschedule should be triggered at least twice
wg = &sync.WaitGroup{}
mx := &sync.Mutex{}
scheduledTimes := 0
wg.Add(2)
job = func() (nextRunIn time.Duration, reschedule bool) {
mx.Lock()
defer mx.Unlock()
// ensure we repeat only twice
if scheduledTimes < 2 {
wg.Done()
scheduledTimes++
return 300 * time.Millisecond, true
}
return 0, false
}
scheduler.Schedule(300*time.Millisecond, jobID, job)
failed = waitTimeout(wg, time.Second)
if failed {
t.Fatal("timed out while waiting for test to finish")
return
}
scheduler.cancel(jobID)
}